Project import generated by Copybara.

GitOrigin-RevId: c55f2b9a3212fdb49d89ea101e924d46bfd959a2
diff --git a/.clang-format b/.clang-format
deleted file mode 100644
index 901467b..0000000
--- a/.clang-format
+++ /dev/null
@@ -1,130 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-#
-# clang-format configuration file. Intended for clang-format >= 4.
-#
-# For more information, see:
-#
-#   Documentation/process/clang-format.rst
-#   https://clang.llvm.org/docs/ClangFormat.html
-#   https://clang.llvm.org/docs/ClangFormatStyleOptions.html
-#
----
-AccessModifierOffset: -4
-AlignAfterOpenBracket: Align
-AlignConsecutiveAssignments: false
-AlignConsecutiveDeclarations: false
-#AlignEscapedNewlines: Left # Unknown to clang-format-4.0
-AlignOperands: true
-AlignTrailingComments: false
-AllowAllParametersOfDeclarationOnNextLine: false
-AllowShortBlocksOnASingleLine: false
-AllowShortCaseLabelsOnASingleLine: false
-AllowShortFunctionsOnASingleLine: None
-AllowShortIfStatementsOnASingleLine: false
-AllowShortLoopsOnASingleLine: false
-AlwaysBreakAfterDefinitionReturnType: None
-AlwaysBreakAfterReturnType: None
-AlwaysBreakBeforeMultilineStrings: false
-AlwaysBreakTemplateDeclarations: false
-BinPackArguments: true
-BinPackParameters: true
-BraceWrapping:
-  AfterClass: false
-  AfterControlStatement: false
-  AfterEnum: false
-  AfterFunction: true
-  AfterNamespace: true
-  AfterObjCDeclaration: false
-  AfterStruct: false
-  AfterUnion: false
-  #AfterExternBlock: false # Unknown to clang-format-5.0
-  BeforeCatch: false
-  BeforeElse: false
-  IndentBraces: false
-  #SplitEmptyFunction: true # Unknown to clang-format-4.0
-  #SplitEmptyRecord: true # Unknown to clang-format-4.0
-  #SplitEmptyNamespace: true # Unknown to clang-format-4.0
-BreakBeforeBinaryOperators: None
-BreakBeforeBraces: Custom
-#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0
-BreakBeforeTernaryOperators: false
-BreakConstructorInitializersBeforeComma: false
-#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0
-BreakAfterJavaFieldAnnotations: false
-BreakStringLiterals: false
-ColumnLimit: 80
-CommentPragmas: '^ IWYU pragma:'
-#CompactNamespaces: false # Unknown to clang-format-4.0
-ConstructorInitializerAllOnOneLineOrOnePerLine: false
-ConstructorInitializerIndentWidth: 8
-ContinuationIndentWidth: 8
-Cpp11BracedListStyle: false
-DerivePointerAlignment: false
-DisableFormat: false
-ExperimentalAutoDetectBinPacking: false
-#FixNamespaceComments: false # Unknown to clang-format-4.0
-
-# Taken from:
-#   git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \
-#   | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$,  - '\1'," \
-#   | sort | uniq
-ForEachMacros:
-  - 'list_for_each_entry'
-  - 'list_for_each_entry_safe'
-  - 'mnl_attr_for_each_nested'
-  - 'hlist_for_each'
-  - 'hlist_for_each_safe'
-  - 'hlist_for_each_entry'
-
-#IncludeBlocks: Preserve # Unknown to clang-format-5.0
-IncludeCategories:
-  - Regex: '.*'
-    Priority: 1
-IncludeIsMainRegex: '(Test)?$'
-IndentCaseLabels: false
-#IndentPPDirectives: None # Unknown to clang-format-5.0
-IndentWidth: 8
-IndentWrappedFunctionNames: false
-JavaScriptQuotes: Leave
-JavaScriptWrapImports: true
-KeepEmptyLinesAtTheStartOfBlocks: false
-MacroBlockBegin: ''
-MacroBlockEnd: ''
-MaxEmptyLinesToKeep: 1
-NamespaceIndentation: Inner
-#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
-ObjCBlockIndentWidth: 8
-ObjCSpaceAfterProperty: true
-ObjCSpaceBeforeProtocolList: true
-
-# Taken from git's rules
-#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0
-PenaltyBreakBeforeFirstCallParameter: 30
-PenaltyBreakComment: 10
-PenaltyBreakFirstLessLess: 0
-PenaltyBreakString: 10
-PenaltyExcessCharacter: 100
-PenaltyReturnTypeOnItsOwnLine: 60
-
-PointerAlignment: Right
-ReflowComments: false
-SortIncludes: false
-#SortUsingDeclarations: false # Unknown to clang-format-4.0
-SpaceAfterCStyleCast: false
-SpaceAfterTemplateKeyword: true
-SpaceBeforeAssignmentOperators: true
-#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0
-#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0
-SpaceBeforeParens: ControlStatements
-#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0
-SpaceInEmptyParentheses: false
-SpacesBeforeTrailingComments: 1
-SpacesInAngles: false
-SpacesInContainerLiterals: false
-SpacesInCStyleCastParentheses: false
-SpacesInParentheses: false
-SpacesInSquareBrackets: false
-Standard: Cpp03
-TabWidth: 8
-UseTab: Always
-...
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index e5234a3..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,41 +0,0 @@
-# locally generated
-Config
-static-syms.h
-config.*
-*.o
-*.a
-*.so
-*~
-\#*#
-
-# cscope
-cscope.*
-ncscope.*
-tags
-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
-
-# tests
-testsuite/results
-testsuite/iproute2/iproute2-this
-testsuite/tools/generate_nlmsg
-testsuite/tests/ip/link/dev_wo_vf_rate.nl
diff --git a/.mailmap b/.mailmap
deleted file mode 100644
index fd40c91..0000000
--- a/.mailmap
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# This list is used by git-shortlog to fix a few botched name translations
-# in the git archive, either because the author's full name was messed up
-# and/or not always written the same way, making contributions from the
-# same person appearing not to be so or badly displayed.
-#
-# Format
-#  Full name <goodaddress> <badaddress> 
-Steve Wise <larrystevenwise@gmail.com> <swise@opengridcomputing.com>
-Steve Wise <larrystevenwise@gmail.com> <swise@chelsio.com>
-
-Stephen Hemminger <stephen@networkplumber.org> <sthemmin@microsoft.com>
-Stephen Hemminger <stephen@networkplumber.org> <shemming@brocade.com>
-Stephen Hemminger <stephen@networkplumber.org> <stephen.hemminger@vyatta.com>
-Stephen Hemminger <stephen@networkplumber.org> <shemminger@vyatta.com>
-Stephen Hemminger <stephen@networkplumber.org> <shemminger>
-Stephen Hemminger <stephen@networkplumber.org> <shemminger@linux-foundation.org>
-Stephen Hemminger <stephen@networkplumber.org> <shemminger@osdl.org>
-Stephen Hemminger <stephen@networkplumber.org> <osdl.org!shemminger>
-Stephen Hemminger <stephen@networkplumber.org> <osdl.net!shemminger>
-
-David Ahern <dsahern@gmail.com> <dsa@cumulusnetworks.com>
diff --git a/MODULE_LICENSE_GPL b/MODULE_LICENSE_GPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_GPL
diff --git a/Makefile b/Makefile
index 0b79b1f..67176be 100644
--- a/Makefile
+++ b/Makefile
@@ -1,30 +1,12 @@
-# SPDX-License-Identifier: GPL-2.0
-# Top level Makefile for iproute2
-
-ifeq ("$(origin V)", "command line")
-VERBOSE = $(V)
-endif
-ifndef VERBOSE
-VERBOSE = 0
-endif
-
-ifeq ($(VERBOSE),0)
-MAKEFLAGS += --no-print-directory
-endif
-
 PREFIX?=/usr
 LIBDIR?=$(PREFIX)/lib
 SBINDIR?=/sbin
 CONFDIR?=/etc/iproute2
-NETNS_RUN_DIR?=/var/run/netns
-NETNS_ETC_DIR?=/etc/netns
 DATADIR?=$(PREFIX)/share
-HDRDIR?=$(PREFIX)/include/iproute2
 DOCDIR?=$(DATADIR)/doc/iproute2
 MANDIR?=$(DATADIR)/man
 ARPDDIR?=/var/lib/arpd
 KERNEL_INCLUDE?=/usr/include
-BASH_COMPDIR?=$(DATADIR)/bash-completion/completions
 
 # Path to db_185.h include
 DBM_INCLUDE:=$(DESTDIR)/usr/include
@@ -36,87 +18,70 @@
 DEFINES+= -DNO_SHARED_LIBS
 endif
 
-DEFINES+=-DCONFDIR=\"$(CONFDIR)\" \
-         -DNETNS_RUN_DIR=\"$(NETNS_RUN_DIR)\" \
-         -DNETNS_ETC_DIR=\"$(NETNS_ETC_DIR)\"
+DEFINES+=-DCONFDIR=\"$(CONFDIR)\"
+
+#options for decnet
+ADDLIB+=dnet_ntop.o dnet_pton.o
+
+#options for ipx
+ADDLIB+=ipx_ntop.o ipx_pton.o
 
 #options for mpls
 ADDLIB+=mpls_ntop.o mpls_pton.o
 
-CC := gcc
-HOSTCC ?= $(CC)
+CC = gcc
+HOSTCC = gcc
 DEFINES += -D_GNU_SOURCE
 # Turn on transparent support for LFS
 DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
-CCOPTS = -O2 -pipe
+CCOPTS = -O2
 WFLAGS := -Wall -Wstrict-prototypes  -Wmissing-prototypes
 WFLAGS += -Wmissing-declarations -Wold-style-definition -Wformat=2
 
-CFLAGS := $(WFLAGS) $(CCOPTS) -I../include -I../include/uapi $(DEFINES) $(CFLAGS)
+CFLAGS := $(WFLAGS) $(CCOPTS) -I../include $(DEFINES) $(CFLAGS)
 YACCFLAGS = -d -t -v
 
-SUBDIRS=lib ip tc bridge misc netem genl tipc devlink rdma man
+SUBDIRS=lib ip tc bridge misc netem genl tipc man
 
-LIBNETLINK=../lib/libutil.a ../lib/libnetlink.a
+LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a
 LDLIBS += $(LIBNETLINK)
 
-all: config.mk
+all: Config
 	@set -e; \
 	for i in $(SUBDIRS); \
-	do echo; echo $$i; $(MAKE) $(MFLAGS) -C $$i; done
+	do $(MAKE) $(MFLAGS) -C $$i; done
 
-help:
-	@echo "Make Targets:"
-	@echo " all                 - build binaries"
-	@echo " clean               - remove products of build"
-	@echo " distclean           - remove configuration and build"
-	@echo " install             - install binaries on local machine"
-	@echo " check               - run tests"
-	@echo " cscope              - build cscope database"
-	@echo " snapshot            - generate version number header"
-	@echo ""
-	@echo "Make Arguments:"
-	@echo " V=[0|1]             - set build verbosity level"
-
-config.mk:
+Config:
 	sh configure $(KERNEL_INCLUDE)
 
 install: all
 	install -m 0755 -d $(DESTDIR)$(SBINDIR)
 	install -m 0755 -d $(DESTDIR)$(CONFDIR)
 	install -m 0755 -d $(DESTDIR)$(ARPDDIR)
-	install -m 0755 -d $(DESTDIR)$(HDRDIR)
-	@for i in $(SUBDIRS);  do $(MAKE) -C $$i install; done
+	install -m 0755 -d $(DESTDIR)$(DOCDIR)/examples
+	install -m 0755 -d $(DESTDIR)$(DOCDIR)/examples/diffserv
+	install -m 0644 README.iproute2+tc $(shell find examples -maxdepth 1 -type f) \
+		$(DESTDIR)$(DOCDIR)/examples
+	install -m 0644 $(shell find examples/diffserv -maxdepth 1 -type f) \
+		$(DESTDIR)$(DOCDIR)/examples/diffserv
+	@for i in $(SUBDIRS) doc; do $(MAKE) -C $$i install; done
 	install -m 0644 $(shell find etc/iproute2 -maxdepth 1 -type f) $(DESTDIR)$(CONFDIR)
-	install -m 0755 -d $(DESTDIR)$(BASH_COMPDIR)
-	install -m 0644 bash-completion/tc $(DESTDIR)$(BASH_COMPDIR)
-	install -m 0644 include/bpf_elf.h $(DESTDIR)$(HDRDIR)
 
 snapshot:
 	echo "static const char SNAPSHOT[] = \""`date +%y%m%d`"\";" \
 		> include/SNAPSHOT.h
 
 clean:
-	@for i in $(SUBDIRS) testsuite; \
+	@for i in $(SUBDIRS) doc; \
 	do $(MAKE) $(MFLAGS) -C $$i clean; done
 
 clobber:
-	touch config.mk
+	touch Config
 	$(MAKE) $(MFLAGS) clean
-	rm -f config.mk cscope.*
+	rm -f Config cscope.*
 
 distclean: clobber
 
-check: all
-	$(MAKE) -C testsuite
-	$(MAKE) -C testsuite alltests
-	@if command -v man >/dev/null 2>&1; then \
-		echo "Checking manpages for syntax errors..."; \
-		$(MAKE) -C man check; \
-	else \
-		echo "man not installed, skipping checks for syntax errors."; \
-	fi
-
 cscope:
 	cscope -b -q -R -Iinclude -sip -slib -smisc -snetem -stc
 
diff --git a/NOTICE b/NOTICE
deleted file mode 120000
index d24842f..0000000
--- a/NOTICE
+++ /dev/null
@@ -1 +0,0 @@
-COPYING
\ No newline at end of file
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,340 @@
+		    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/OWNERS b/OWNERS
new file mode 100644
index 0000000..f002a84
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,2 @@
+ek@google.com
+lorenzo@google.com
diff --git a/README b/README
index bc82187..c7a5118 100644
--- a/README
+++ b/README
@@ -1,32 +1,28 @@
 This is a set of utilities for Linux networking.
 
 Information:
-    https://wiki.linuxfoundation.org/networking/iproute2
+    http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2
 
 Download:
     http://www.kernel.org/pub/linux/utils/net/iproute2/
 
-Stable version repository:
-    git://git.kernel.org/pub/scm/network/iproute2/iproute2.git
-
-Development repository:
-    git://git.kernel.org/pub/scm/network/iproute2/iproute2-next.git
+Repository:
+    git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git
 
 How to compile this.
 --------------------
 1. libdbm
 
-arpd needs to have the berkeleydb development libraries. For Debian
-users this is the package with a name like libdbX.X-dev.
+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 Berkeley
 database routines.  Often this is in the db-devel package.
 
 2. make
 
-The makefile will automatically build a config.mk file which
-contains definitions of libraries that may or may not be available
-on the system such as: ATM, ELF, MNL, and SELINUX.
+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
    look at start of Makefile and set correct values for
diff --git a/README.decnet b/README.decnet
new file mode 100644
index 0000000..4300f90
--- /dev/null
+++ b/README.decnet
@@ -0,0 +1,33 @@
+
+Here are a few quick points about DECnet support...
+
+ o iproute2 is the tool of choice for configuring the DECnet support for
+   Linux. For many features, it is the only tool which can be used to
+   configure them.
+
+ o No name resolution is available as yet, all addresses must be
+   entered numerically.
+
+ o Remember to set the hardware address of the interface using: 
+
+   ip link set ethX address xx:xx:xx:xx:xx:xx
+      (where xx:xx:xx:xx:xx:xx is the MAC address for your DECnet node
+       address)
+
+   if your Ethernet card won't listen to more than one unicast
+   mac address at once. If the Linux DECnet stack doesn't talk to
+   any other DECnet nodes, then check this with tcpdump and if its
+   a problem, change the mac address (but do this _before_ starting
+   any other network protocol on the interface)
+
+ o Whilst you can use ip addr add to add more than one DECnet address to an
+   interface, don't expect addresses which are not the same as the
+   kernels node address to work properly with 2.4 kernels. This should
+   be fine with 2.6 kernels as the routing code has been extensively
+   modified and improved.
+
+ o The DECnet support is currently self contained. It does not depend on
+   the libdnet library.
+
+Steve Whitehouse <steve@chygwyn.com>
+
diff --git a/README.devel b/README.devel
index 60c9546..9b7d11e 100644
--- a/README.devel
+++ b/README.devel
@@ -4,15 +4,12 @@
 Please submit both to the Linux networking mailing list
    <netdev@vger.kernel.org>
 
-The current source for the stable version is in the git repository:
-    git://git.kernel.org/pub/scm/network/iproute2/iproute2.git
+The current source is in the git repository:
+    git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git
 
-The development git repository is available at the following address:
-    git://git.kernel.org/pub/scm/network/iproute2/iproute2-next.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.
 
-The stable repository contains the source corresponding to the
-current code in the Linux networking tree (net), which in turn is
-aligned on the mainline Linux kernel (ie follows Linus).
-The iproute2-next repository tracks the code intended for the next
-release; it corresponds with networking development tree (net-next)
-in the kernel.
diff --git a/README.distribution b/README.distribution
new file mode 100644
index 0000000..4e2e8ea
--- /dev/null
+++ b/README.distribution
@@ -0,0 +1,95 @@
+I. About the distribution tables
+
+The table used for "synthesizing" the distribution is essentially a scaled,
+translated, inverse to the cumulative distribution function.
+
+Here's how to think about it: Let F() be the cumulative distribution
+function for a probability distribution X.  We'll assume we've scaled
+things so that X has mean 0 and standard deviation 1, though that's not
+so important here.  Then:
+
+	F(x) = P(X <= x) = \int_{-inf}^x f
+
+where f is the probability density function.
+
+F is monotonically increasing, so has an inverse function G, with range
+0 to 1.  Here, G(t) = the x such that P(X <= x) = t.  (In general, G may
+have singularities if X has point masses, i.e., points x such that
+P(X = x) > 0.)
+
+Now we create a tabular representation of G as follows:  Choose some table
+size N, and for the ith entry, put in G(i/N).  Let's call this table T.
+
+The claim now is, I can create a (discrete) random variable Y whose
+distribution has the same approximate "shape" as X, simply by letting
+Y = T(U), where U is a discrete uniform random variable with range 1 to N.
+To see this, it's enough to show that Y's cumulative distribution function,
+(let's call it H), is a discrete approximation to F.  But
+
+	H(x) = P(Y <= x)
+	     = (# of entries in T <= x) / N   -- as Y chosen uniformly from T
+	     = i/N, where i is the largest integer such that G(i/N) <= x
+	     = i/N, where i is the largest integer such that i/N <= F(x)
+	     		-- since G and F are inverse functions (and F is
+	     		   increasing)
+	     = floor(N*F(x))/N
+
+as desired.
+
+II. How to create distribution tables (in theory)
+
+How can we create this table in practice? In some cases, F may have a
+simple expression which allows evaluating its inverse directly.  The
+Pareto distribution is one example of this.  In other cases, and
+especially for matching an experimentally observed distribution, it's
+easiest simply to create a table for F and "invert" it.  Here, we give
+a concrete example, namely how the new "experimental" distribution was
+created.
+
+1. Collect enough data points to characterize the distribution.  Here, I
+collected 25,000 "ping" roundtrip times to a "distant" point (time.nist.gov).
+That's far more data than is really necessary, but it was fairly painless to
+collect it, so...
+
+2. Normalize the data so that it has mean 0 and standard deviation 1.
+
+3. Determine the cumulative distribution.  The code I wrote creates a table
+covering the range -10 to +10, with granularity .00005.  Obviously, this
+is absurdly over-precise, but since it's a one-time only computation, I
+figured it hardly mattered.
+
+4. Invert the table: for each table entry F(x) = y, make the y*TABLESIZE
+(here, 4096) entry be x*TABLEFACTOR (here, 8192).  This creates a table
+for the ("normalized") inverse of size TABLESIZE, covering its domain 0
+to 1 with granularity 1/TABLESIZE.  Note that even with the granularity
+used in creating the table for F, it's possible not all the entries in
+the table for G will be filled in.  So, make a pass through the
+inverse's table, filling in any missing entries by linear interpolation.
+
+III. How to create distribution tables (in practice)
+
+If you want to do all this yourself, I've provided several tools to help:
+
+1. maketable does the steps 2-4 above, and then generates the appropriate
+header file.  So if you have your own time distribution, you can generate
+the header simply by:
+
+	maketable < time.values > header.h
+
+2. As explained in the other README file, the somewhat sleazy way I have
+of generating correlated values needs correction.  You can generate your
+own correction tables by compiling makesigtable and makemutable with
+your header file.  Check the Makefile to see how this is done.
+
+3. Warning: maketable, makesigtable and especially makemutable do
+enormous amounts of floating point arithmetic.  Don't try running
+these on an old 486.  (NIST Net itself will run fine on such a
+system, since in operation, it just needs to do a few simple integral
+calculations.  But getting there takes some work.)
+
+4. The tables produced are all normalized for mean 0 and standard
+deviation 1.  How do you know what values to use for real?  Here, I've
+provided a simple "stats" utility.  Give it a series of floating point
+values, and it will return their mean (mu), standard deviation (sigma),
+and correlation coefficient (rho).  You can then plug these values
+directly into NIST Net.
diff --git a/README.iproute2+tc b/README.iproute2+tc
new file mode 100644
index 0000000..2a5638d
--- /dev/null
+++ b/README.iproute2+tc
@@ -0,0 +1,123 @@
+iproute2+tc*
+
+It's the first release of Linux traffic control engine.
+
+
+NOTES.
+* csz scheduler is inoperational at the moment, and probably
+  never will be repaired but replaced with h-pfq scheduler.
+* To use "fw" classifier you will need ipfwchains patch.
+* No manual available. Ask me, if you have problems (only try to guess
+  answer yourself at first 8)).
+
+
+Micro-manual how to start it the first time
+-------------------------------------------
+
+A. Attach CBQ to eth1:
+
+tc qdisc add dev eth1 root handle 1: cbq bandwidth 10Mbit allot 1514 cell 8 \
+avpkt 1000 mpu 64
+
+B. Add root class:
+
+tc class add dev eth1 parent 1:0 classid 1:1 cbq bandwidth 10Mbit rate 10Mbit \
+allot 1514 cell 8 weight 1Mbit prio 8 maxburst 20 avpkt 1000
+
+C. Add default interactive class:
+
+tc class add dev eth1 parent 1:1 classid 1:2 cbq bandwidth 10Mbit rate 1Mbit \
+allot 1514 cell 8 weight 100Kbit prio 3 maxburst 20 avpkt 1000 split 1:0 \
+defmap c0
+
+D. Add default class:
+
+tc class add dev eth1 parent 1:1 classid 1:3 cbq bandwidth 10Mbit rate 8Mbit \
+allot 1514 cell 8 weight 800Kbit prio 7 maxburst 20 avpkt 1000 split 1:0 \
+defmap 3f
+
+etc. etc. etc. Well, it is enough to start 8) The rest can be guessed 8)
+Look also at more elaborated example, ready to start rsvpd,
+in rsvp/cbqinit.eth1.
+
+
+Terminology and advices about setting CBQ parameters may be found in Sally Floyd
+papers. 
+
+
+Pairs X:Y are class handles, X:0 are qdisc handles.
+weight should be proportional to rate for leaf classes
+(I choosed it ten times less, but it is not necessary)
+
+defmap is bitmap of logical priorities served by this class.
+
+E. Another qdiscs are simpler. F.e. let's join TBF on class 1:2
+
+tc qdisc add dev eth1 parent 1:2 tbf rate 64Kbit buffer 5Kb/8 limit 10Kb
+
+F. Look at all that we created:
+
+tc qdisc ls dev eth1
+tc class ls dev eth1
+
+G. Install "route" classifier on root of cbq and map destination from realm
+1 to class 1:2
+
+tc filter add dev eth1 parent 1:0 protocol ip prio 100 route to 1 classid 1:2
+
+H. Assign routes to 10.11.12.0/24 to realm 1
+
+ip route add 10.11.12.0/24 dev eth1 via whatever realm 1
+
+etc. The same thing can be made with rules.
+I still did not test ipchains, but they should work too.
+
+
+Setup and code example of BPF classifier and action can be found under
+examples/bpf/, which should explain everything for getting started.
+
+
+Setup of rsvp and u32 classifiers is more hairy.
+If you read RSVP specs, you will understand how rsvp classifier
+works easily. What's about u32... That's example:
+
+
+#! /bin/sh
+
+TC=/home/root/tc
+
+# Setup classifier root on eth1 root (it is cbq)
+$TC filter add dev eth1 parent 1:0 prio 5 protocol ip u32
+
+# Create hash table of 256 slots with ID 1:
+$TC filter add dev eth1 parent 1:0 prio 5 handle 1: u32 divisor 256
+
+# Add to 6th slot of hash table rule to select tcp/telnet to 193.233.7.75
+# direct it to class 1:4 and prescribe to fall to best effort,
+# if traffic violate TBF (32kbit,5K)
+$TC filter add dev eth1 parent 1:0 prio 5 u32 ht 1:6: \
+	match ip dst 193.233.7.75 \
+	match tcp dst 0x17 0xffff \
+	flowid 1:4 \
+	police rate 32kbit buffer 5kb/8 mpu 64 mtu 1514 index 1
+
+# Add to 1th slot of hash table rule to select icmp to 193.233.7.75
+# direct it to class 1:4 and prescribe to fall to best effort,
+# if traffic violate TBF (10kbit,5K)
+$TC filter add dev eth1 parent 1:0 prio 5 u32 ht 1:: \
+	sample ip protocol 1 0xff \
+	match ip dst 193.233.7.75 \
+	flowid 1:4 \
+	police rate 10kbit buffer 5kb/8 mpu 64 mtu 1514 index 2
+
+# Lookup hash table, if it is not fragmented frame
+# Use protocol as hash key
+$TC filter add dev eth1 parent 1:0 prio 5 handle ::1 u32 ht 800:: \
+	match ip nofrag \
+	offset mask 0x0F00 shift 6 \
+	hashkey mask 0x00ff0000 at 8 \
+	link 1:
+
+
+Alexey Kuznetsov
+kuznet@ms2.inr.ac.ru
diff --git a/README.lnstat b/README.lnstat
new file mode 100644
index 0000000..057925f
--- /dev/null
+++ b/README.lnstat
@@ -0,0 +1,81 @@
+lnstat - linux networking statistics
+(C) 2004 Harald Welte <laforge@gnumonks.org
+======================================================================
+
+This tool is a generalized and more feature-complete replacement for the old
+'rtstat' program.
+
+In addition to routing cache statistics, it supports any kind of statistics
+the linux kernel exports via a file in /proc/net/stat.  In a stock 2.6.9
+kernel, this is 
+	per-protocol neighbour cache statistics 
+		(ipv4, ipv6, atm, decnet)
+	routing cache statistics
+		(ipv4)
+	connection tracking statistics
+		(ipv4)
+
+Please note that lnstat will adopt to any additional statistics that might be
+added to the kernel at some later point
+
+I personally always like examples more than any reference documentation, so I
+list the following examples.  If somebody wants to do a manpage, feel free
+to send me a patch :)
+
+EXAMPLES:
+
+In order to get a list of supported statistics files, you can run
+
+	lnstat -d
+
+It will display something like
+ 
+/proc/net/stat/arp_cache:
+         1: entries
+         2: allocs
+         3: destroys
+[...]
+/proc/net/stat/rt_cache:
+         1: entries
+         2: in_hit
+         3: in_slow_tot
+
+You can now select the files/keys you are interested by something like
+
+	lnstat -k arp_cache:entries,rt_cache:in_hit,arp_cache:destroys
+
+arp_cach|rt_cache|arp_cach|
+ entries|  in_hit|destroys|
+       6|       6|       0|
+       6|       0|       0|
+       6|       2|       0|
+
+
+You can specify the interval (e.g. 10 seconds) by:
+	
+	lnstat -i 10
+
+You can specify to only use one particular statistics file:
+
+	lnstat -f ip_conntrack
+
+You can specify individual field widths 
+
+	lnstat -k arp_cache:entries,rt_cache:entries -w 20,8
+
+You can specify not to print a header at all
+	
+	lnstat -s 0
+
+You can specify to print a header only at start of the program
+
+	lnstat -s 1
+
+You can specify to print a header at start and every 20 lines:
+
+	lnstat -s 20
+
+You can specify the number of samples you want to take (e.g. 5):
+	
+	lnstat -c 5
+
diff --git a/README.version b/README.version
new file mode 100644
index 0000000..082fb9f
--- /dev/null
+++ b/README.version
@@ -0,0 +1,3 @@
+URL: https://www.kernel.org/pub/linux/utils/net/iproute2/iproute2-3.4.0.tar.gz
+Version: 3.4.0
+BugComponent: 119449
diff --git a/bash-completion/tc b/bash-completion/tc
deleted file mode 100644
index 007e1c2..0000000
--- a/bash-completion/tc
+++ /dev/null
@@ -1,800 +0,0 @@
-# tc(8) completion                                         -*- shell-script -*-
-# Copyright 2016 6WIND S.A.
-# Copyright 2016 Quentin Monnet <quentin.monnet@6wind.com>
-
-QDISC_KIND=' choke codel bfifo pfifo pfifo_head_drop fq fq_codel gred hhf \
-            mqprio multiq netem pfifo_fast pie red rr sfb sfq tbf atm cbq drr \
-            dsmark hfsc htb prio qfq '
-FILTER_KIND=' basic bpf cgroup flow flower fw route rsvp tcindex u32 matchall '
-ACTION_KIND=' gact mirred bpf sample '
-
-# Takes a list of words in argument; each one of them is added to COMPREPLY if
-# it is not already present on the command line. Returns no value.
-_tc_once_attr()
-{
-    local w subcword found
-    for w in $*; do
-        found=0
-        for (( subcword=3; subcword < ${#words[@]}-1; subcword++ )); do
-            if [[ $w == ${words[subcword]} ]]; then
-                found=1
-                break
-            fi
-        done
-        [[ $found -eq 0 ]] && \
-            COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) )
-    done
-}
-
-# Takes a list of words in argument; each one of them is added to COMPREPLY if
-# it is not already present on the command line from the provided index. Returns
-# no value.
-_tc_once_attr_from()
-{
-    local w subcword found from=$1
-    shift
-    for w in $*; do
-        found=0
-        for (( subcword=$from; subcword < ${#words[@]}-1; subcword++ )); do
-            if [[ $w == ${words[subcword]} ]]; then
-                found=1
-                break
-            fi
-        done
-        [[ $found -eq 0 ]] && \
-            COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) )
-    done
-}
-
-# Takes a list of words in argument; adds them all to COMPREPLY if none of them
-# is already present on the command line. Returns no value.
-_tc_one_of_list()
-{
-    local w subcword
-    for w in $*; do
-        for (( subcword=3; subcword < ${#words[@]}-1; subcword++ )); do
-            [[ $w == ${words[subcword]} ]] && return 1
-        done
-    done
-    COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )
-}
-
-# Takes a list of words in argument; adds them all to COMPREPLY if none of them
-# is already present on the command line from the provided index. Returns no
-# value.
-_tc_one_of_list_from()
-{
-    local w subcword from=$1
-    shift
-    for w in $*; do
-        for (( subcword=$from; subcword < ${#words[@]}-1; subcword++ )); do
-            [[ $w == ${words[subcword]} ]] && return 1
-        done
-    done
-    COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )
-}
-
-# Returns "$cur ${cur}arg1 ${cur}arg2 ..."
-_tc_expand_units()
-{
-    [[ $cur =~ ^[0-9]+ ]] || return 1
-    local value=${cur%%[^0-9]*}
-    [[ $cur == $value ]] && echo $cur
-    echo ${@/#/$value}
-}
-
-# Complete based on given word, usually $prev (or possibly the word before),
-# for when an argument or an option name has but a few possible arguments (so
-# tc does not take particular commands into account here).
-# Returns 0 is completion should stop after running this function, 1 otherwise.
-_tc_direct_complete()
-{
-    case $1 in
-        # Command options
-        dev)
-            _available_interfaces
-            return 0
-            ;;
-        classid)
-            return 0
-            ;;
-        estimator)
-            local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' )
-            COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
-            return 0
-            ;;
-        handle)
-            return 0
-            ;;
-        parent|flowid)
-            local i iface ids cmd
-            for (( i=3; i < ${#words[@]}-2; i++ )); do
-                [[ ${words[i]} == dev ]] && iface=${words[i+1]}
-                break
-            done
-            for cmd in qdisc class; do
-                if [[ -n $iface ]]; then
-                    ids+=$( tc $cmd show dev $iface 2>/dev/null | \
-                        cut -d\  -f 3 )" "
-                else
-                    ids+=$( tc $cmd show 2>/dev/null | cut -d\  -f 3 )
-                fi
-            done
-            [[ $ids != " " ]] && \
-                COMPREPLY+=( $( compgen -W "$ids" -- "$cur" ) )
-            return 0
-            ;;
-        protocol) # list comes from lib/ll_proto.c
-            COMPREPLY+=( $( compgen -W ' 802.1Q 802.1ad 802_2 802_3 LLDP aarp \
-                all aoe arp atalk atmfate atmmpoa ax25 bpq can control cust \
-                ddcmp dec diag dna_dl dna_rc dna_rt econet ieeepup ieeepupat \
-                ip ipv4 ipv6 ipx irda lat localtalk loop mobitex ppp_disc \
-                ppp_mp ppp_ses ppptalk pup pupat rarp sca snap tipc tr_802_2 \
-                wan_ppp x25' -- "$cur" ) )
-            return 0
-            ;;
-        prio)
-            return 0
-            ;;
-        stab)
-            COMPREPLY+=( $( compgen -W 'mtu tsize mpu overhead
-                linklayer' -- "$cur" ) )
-            ;;
-
-        # Qdiscs and classes options
-        alpha|bands|beta|buckets|corrupt|debug|decrement|default|\
-        default_index|depth|direct_qlen|divisor|duplicate|ewma|flow_limit|\
-        flows|hh_limit|increment|indices|linklayer|non_hh_weight|num_tc|\
-        penalty_burst|penalty_rate|prio|priomap|probability|queues|r2q|\
-        reorder|vq|vqs)
-            return 0
-            ;;
-        setup)
-            COMPREPLY+=( $( compgen -W 'vqs' -- "$cur" ) )
-            return 0
-            ;;
-        hw)
-            COMPREPLY+=( $( compgen -W '1 0' -- "$cur" ) )
-            return 0
-            ;;
-        distribution)
-            COMPREPLY+=( $( compgen -W 'uniform normal pareto
-                paretonormal' -- "$cur" ) )
-            return 0
-            ;;
-        loss)
-            COMPREPLY+=( $( compgen -W 'random state gmodel' -- "$cur" ) )
-            return 0
-            ;;
-
-        # Qdiscs and classes options options
-        gap|gmodel|state)
-            return 0
-            ;;
-
-        # Filters options
-        map)
-            COMPREPLY+=( $( compgen -W 'key' -- "$cur" ) )
-            return 0
-            ;;
-        hash)
-            COMPREPLY+=( $( compgen -W 'keys' -- "$cur" ) )
-            return 0
-            ;;
-        indev)
-            _available_interfaces
-            return 0
-            ;;
-        eth_type)
-            COMPREPLY+=( $( compgen -W 'ipv4 ipv6' -- "$cur" ) )
-            return 0
-            ;;
-        ip_proto)
-            COMPREPLY+=( $( compgen -W 'tcp udp' -- "$cur" ) )
-            return 0
-            ;;
-
-        # Filters options options
-        key|keys)
-            [[ ${words[@]} =~ graft ]] && return 1
-            COMPREPLY+=( $( compgen -W 'src dst proto proto-src proto-dst iif \
-                priority mark nfct nfct-src nfct-dst nfct-proto-src \
-                nfct-proto-dst rt-classid sk-uid sk-gid vlan-tag rxhash' -- \
-                "$cur" ) )
-            return 0
-            ;;
-
-        # BPF options - used for filters, actions, and exec
-        export|bytecode|bytecode-file|object-file)
-            _filedir
-            return 0
-            ;;
-        object-pinned|graft) # Pinned object is probably under /sys/fs/bpf/
-            [[ -n "$cur" ]] && _filedir && return 0
-            COMPREPLY=( $( compgen -G "/sys/fs/bpf/*" -- "$cur" ) ) || _filedir
-            compopt -o nospace
-            return 0
-            ;;
-        section)
-            if (type objdump > /dev/null 2>&1) ; then
-                local fword objfile section_list
-                for (( fword=3; fword < ${#words[@]}-3; fword++ )); do
-                    if [[ ${words[fword]} == object-file ]]; then
-                        objfile=${words[fword+1]}
-                        break
-                    fi
-                done
-                section_list=$( objdump -h $objfile 2>/dev/null | \
-                    sed -n 's/^ *[0-9]\+ \([^ ]*\) *.*/\1/p' )
-                COMPREPLY+=( $( compgen -W "$section_list" -- "$cur" ) )
-            fi
-            return 0
-            ;;
-        import|run)
-            _filedir
-            return 0
-            ;;
-        type)
-            COMPREPLY+=( $( compgen -W 'cls act' -- "$cur" ) )
-            return 0
-            ;;
-
-        # Actions options
-        random)
-            _tc_one_of_list 'netrand determ'
-            return 0
-            ;;
-
-        # Units for option arguments
-        bandwidth|maxrate|peakrate|rate)
-            local list=$( _tc_expand_units 'bit' \
-                'kbit' 'kibit' 'kbps' 'kibps' \
-                'mbit' 'mibit' 'mbps' 'mibps' \
-                'gbit' 'gibit' 'gbps' 'gibps' \
-                'tbit' 'tibit' 'tbps' 'tibps' )
-            COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
-            ;;
-        admit_bytes|avpkt|burst|cell|initial_quantum|limit|max|min|mtu|mpu|\
-        overhead|quantum|redflowlist)
-            local list=$( _tc_expand_units \
-                'b' 'kbit' 'k' 'mbit' 'm' 'gbit' 'g' )
-            COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
-            ;;
-        db|delay|evict_timeout|interval|latency|perturb|rehash|reset_timeout|\
-        target|tupdate)
-            local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' )
-            COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
-            ;;
-    esac
-    return 1
-}
-
-# Complete with options names for qdiscs. Each qdisc has its own set of options
-# and it seems we cannot really parse it from anywhere, so we add it manually
-# in this function.
-# Returns 0 is completion should stop after running this function, 1 otherwise.
-_tc_qdisc_options()
-{
-    case $1 in
-        choke)
-            _tc_once_attr 'limit bandwidth ecn min max burst'
-            return 0
-            ;;
-        codel)
-            _tc_once_attr 'limit target interval'
-            _tc_one_of_list 'ecn noecn'
-            return 0
-            ;;
-        bfifo|pfifo|pfifo_head_drop)
-            _tc_once_attr 'limit'
-            return 0
-            ;;
-        fq)
-            _tc_once_attr 'limit flow_limit quantum initial_quantum maxrate \
-                buckets'
-            _tc_one_of_list 'pacing nopacing'
-            return 0
-            ;;
-        fq_codel)
-            _tc_once_attr 'limit flows target interval quantum'
-            _tc_one_of_list 'ecn noecn'
-            return 0
-            ;;
-        gred)
-            _tc_once_attr 'setup vqs default grio vq prio limit min max avpkt \
-                burst probability bandwidth ecn harddrop'
-            return 0
-            ;;
-        hhf)
-            _tc_once_attr 'limit quantum hh_limit reset_timeout admit_bytes \
-                evict_timeout non_hh_weight'
-            return 0
-            ;;
-        mqprio)
-            _tc_once_attr 'num_tc map queues hw'
-            return 0
-            ;;
-        netem)
-            _tc_once_attr 'delay distribution corrupt duplicate loss ecn \
-                reorder rate'
-            return 0
-            ;;
-        pie)
-            _tc_once_attr 'limit target tupdate alpha beta'
-            _tc_one_of_list 'bytemode nobytemode'
-            _tc_one_of_list 'ecn noecn'
-            return 0
-            ;;
-        red)
-            _tc_once_attr 'limit min max avpkt burst adaptive probability \
-                bandwidth ecn harddrop'
-            return 0
-            ;;
-        rr|prio)
-            _tc_once_attr 'bands priomap multiqueue'
-            return 0
-            ;;
-        sfb)
-            _tc_once_attr 'rehash db limit max target increment decrement \
-                penalty_rate penalty_burst'
-            return 0
-            ;;
-        sfq)
-            _tc_once_attr 'limit perturb quantum divisor flows depth headdrop \
-                redflowlimit min max avpkt burst probability ecn harddrop'
-            return 0
-            ;;
-        tbf)
-            _tc_once_attr 'limit burst rate mtu peakrate latency overhead \
-                linklayer'
-            return 0
-            ;;
-        cbq)
-            _tc_once_attr 'bandwidth avpkt mpu cell ewma'
-            return 0
-            ;;
-        dsmark)
-            _tc_once_attr 'indices default_index set_tc_index'
-            return 0
-            ;;
-        hfsc)
-            _tc_once_attr 'default'
-            return 0
-            ;;
-        htb)
-            _tc_once_attr 'default r2q direct_qlen debug'
-            return 0
-            ;;
-        multiq|pfifo_fast|atm|drr|qfq)
-            return 0
-            ;;
-    esac
-    return 1
-}
-
-# Complete with options names for BPF filters or actions.
-# Returns 0 is completion should stop after running this function, 1 otherwise.
-_tc_bpf_options()
-{
-    [[ ${words[${#words[@]}-3]} == object-file ]] && \
-        _tc_once_attr 'section export'
-    [[ ${words[${#words[@]}-5]} == object-file ]] && \
-        [[ ${words[${#words[@]}-3]} =~ (section|export) ]] && \
-        _tc_once_attr 'section export'
-    _tc_one_of_list 'bytecode bytecode-file object-file object-pinned'
-    _tc_once_attr 'verbose index direct-action action classid'
-    return 0
-}
-
-# Complete with options names for filter actions.
-# This function is recursive, thus allowing multiple actions statement to be
-# parsed.
-# Returns 0 is completion should stop after running this function, 1 otherwise.
-_tc_filter_action_options()
-{
-    for ((acwd=$1; acwd < ${#words[@]}-1; acwd++));
-    do
-        if [[ action == ${words[acwd]} ]]; then
-            _tc_filter_action_options $((acwd+1)) && return 0
-        fi
-    done
-
-    local action acwd
-    for ((acwd=$1; acwd < ${#words[@]}-1; acwd++)); do
-        if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then
-            _tc_one_of_list_from $acwd action
-            _tc_action_options $acwd && return 0
-        fi
-    done
-    _tc_one_of_list_from $acwd $ACTION_KIND
-    return 0
-}
-
-# Complete with options names for filters.
-# Returns 0 is completion should stop after running this function, 1 otherwise.
-_tc_filter_options()
-{
-
-    for ((acwd=$1; acwd < ${#words[@]}-1; acwd++));
-    do
-        if [[ action == ${words[acwd]} ]]; then
-            _tc_filter_action_options $((acwd+1)) && return 0
-        fi
-    done
-
-    filter=${words[$1]}
-    case $filter in
-        basic)
-            _tc_once_attr 'match action classid'
-            return 0
-            ;;
-        bpf)
-            _tc_bpf_options
-            return 0
-            ;;
-        cgroup)
-            _tc_once_attr 'match action'
-            return 0
-            ;;
-        flow)
-            local i
-            for (( i=5; i < ${#words[@]}-1; i++ )); do
-                if [[ ${words[i]} =~ ^keys?$ ]]; then
-                    _tc_direct_complete 'key'
-                    COMPREPLY+=( $( compgen -W 'or and xor rshift addend' -- \
-                        "$cur" ) )
-                    break
-                fi
-            done
-            _tc_once_attr 'map hash divisor baseclass match action'
-            return 0
-            ;;
-        matchall)
-            _tc_once_attr 'action classid skip_sw skip_hw'
-            return 0
-            ;;
-        flower)
-            _tc_once_attr 'action classid indev dst_mac src_mac eth_type \
-                ip_proto dst_ip src_ip dst_port src_port'
-            return 0
-            ;;
-        fw)
-            _tc_once_attr 'action classid'
-            return 0
-            ;;
-        route)
-            _tc_one_of_list 'from fromif'
-            _tc_once_attr 'to classid action'
-            return 0
-            ;;
-        rsvp)
-            _tc_once_attr 'ipproto session sender classid action tunnelid \
-                tunnel flowlabel spi/ah spi/esp u8 u16 u32'
-            [[ ${words[${#words[@]}-3]} == tunnel ]] && \
-                    COMPREPLY+=( $( compgen -W 'skip' -- "$cur" ) )
-            [[ ${words[${#words[@]}-3]} =~ u(8|16|32) ]] && \
-                    COMPREPLY+=( $( compgen -W 'mask' -- "$cur" ) )
-            [[ ${words[${#words[@]}-3]} == mask ]] && \
-                    COMPREPLY+=( $( compgen -W 'at' -- "$cur" ) )
-            return 0
-            ;;
-        tcindex)
-            _tc_once_attr 'hash mask shift classid action'
-            _tc_one_of_list 'pass_on fall_through'
-            return 0
-            ;;
-        u32)
-            _tc_once_attr 'match link classid action offset ht hashkey sample'
-            COMPREPLY+=( $( compgen -W 'ip ip6 udp tcp icmp u8 u16 u32 mark \
-                divisor' -- "$cur" ) )
-            return 0
-            ;;
-    esac
-    return 1
-}
-
-# Complete with options names for actions.
-# Returns 0 is completion should stop after running this function, 1 otherwise.
-_tc_action_options()
-{
-    local from=$1
-    local action=${words[from]}
-    case $action in
-        bpf)
-            _tc_bpf_options
-            return 0
-            ;;
-        mirred)
-            _tc_one_of_list_from $from 'ingress egress'
-            _tc_one_of_list_from $from 'mirror redirect'
-            _tc_once_attr_from $from 'index dev'
-            return 0
-            ;;
-        sample)
-            _tc_once_attr_from $from 'rate'
-            _tc_once_attr_from $from 'trunc'
-            _tc_once_attr_from $from 'group'
-            return 0
-            ;;
-        gact)
-            _tc_one_of_list_from $from 'reclassify drop continue pass'
-            _tc_once_attr_from $from 'random'
-            return 0
-            ;;
-    esac
-    return 1
-}
-
-# Complete with options names for exec.
-# Returns 0 is completion should stop after running this function, 1 otherwise.
-_tc_exec_options()
-{
-    case $1 in
-        import)
-            [[ ${words[${#words[@]}-3]} == import ]] && \
-                _tc_once_attr 'run'
-            return 0
-            ;;
-        graft)
-            COMPREPLY+=( $( compgen -W 'key type' -- "$cur" ) )
-            [[ ${words[${#words[@]}-3]} == object-file ]] && \
-                _tc_once_attr 'type'
-            _tc_bpf_options
-            return 0
-            ;;
-    esac
-    return 1
-}
-
-# Main completion function
-# Logic is as follows:
-#   1. Check if previous word is a global option; if so, propose arguments.
-#   2. Check if current word is a global option; if so, propose completion.
-#   3. Check for the presence of a main command (qdisc|class|filter|...). If
-#      there is one, first call _tc_direct_complete to see if previous word is
-#      waiting for a particular completion. If so, propose completion and exit.
-#   4. Extract main command and -- if available -- its subcommand
-#      (add|delete|show|...).
-#   5. Propose completion based on main and sub- command in use. Additional
-#      functions may be called for qdiscs, classes or filter options.
-_tc()
-{
-    local cur prev words cword
-    _init_completion || return
-
-    case $prev in
-        -V|-Version)
-            return 0
-            ;;
-        -b|-batch|-cf|-conf)
-            _filedir
-            return 0
-            ;;
-        -force)
-            COMPREPLY=( $( compgen -W '-batch' -- "$cur" ) )
-            return 0
-            ;;
-        -nm|name)
-            [[ -r /etc/iproute2/tc_cls ]] || \
-                COMPREPLY=( $( compgen -W '-conf' -- "$cur" ) )
-            return 0
-            ;;
-        -n|-net|-netns)
-            local nslist=$( ip netns list 2>/dev/null )
-            COMPREPLY+=( $( compgen -W "$nslist" -- "$cur" ) )
-            return 0
-            ;;
-        -tshort)
-            _tc_once_attr '-statistics'
-            COMPREPLY+=( $( compgen -W 'monitor' -- "$cur" ) )
-            return 0
-            ;;
-        -timestamp)
-            _tc_once_attr '-statistics -tshort'
-            COMPREPLY+=( $( compgen -W 'monitor' -- "$cur" ) )
-            return 0
-            ;;
-    esac
-
-    # Search for main commands
-    local subcword cmd subcmd
-    for (( subcword=1; subcword < ${#words[@]}-1; subcword++ )); do
-        [[ ${words[subcword]} == -b?(atch) ]] && return 0
-        [[ -n $cmd ]] && subcmd=${words[subcword]} && break
-        [[ ${words[subcword]} != -* && \
-            ${words[subcword-1]} != -@(n?(et?(ns))|c?(on)f) ]] && \
-            cmd=${words[subcword]}
-    done
-
-    if [[ -z $cmd ]]; then
-        case $cur in
-            -*)
-                local c='-Version -statistics -details -raw -pretty \
-                    -iec -graphe -batch -name -netns -timestamp'
-                [[ $cword -eq 1 ]] && c+=' -force'
-                COMPREPLY=( $( compgen -W "$c" -- "$cur" ) )
-                return 0
-                ;;
-            *)
-                COMPREPLY=( $( compgen -W "help $( tc help 2>&1 | \
-                    command sed \
-                    -e '/OBJECT := /!d' \
-                    -e 's/.*{//' \
-                    -e 's/}.*//' \
-                    -e \ 's/|//g' )" -- "$cur" ) )
-                return 0
-                ;;
-        esac
-    fi
-
-    [[ $subcmd == help ]] && return 0
-
-    # For this set of commands we may create COMPREPLY just by analysing the
-    # previous word, if it expects for a specific list of options or values.
-    if [[ $cmd =~ (qdisc|class|filter|action|exec) ]]; then
-        _tc_direct_complete $prev && return 0
-        if [[ ${words[${#words[@]}-3]} == estimator ]]; then
-            local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' )
-            COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) ) && return 0
-        fi
-    fi
-
-    # Completion depends on main command and subcommand in use.
-    case $cmd in
-        qdisc)
-            case $subcmd in
-                add|change|replace|link|del|delete)
-                    if [[ $(($cword-$subcword)) -eq 1 ]]; then
-                        COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
-                        return 0
-                    fi
-                    local qdisc qdwd
-                    for ((qdwd=$subcword; qdwd < ${#words[@]}-1; qdwd++)); do
-                        if [[ $QDISC_KIND =~ ' '${words[qdwd]}' ' ]]; then
-                            qdisc=${words[qdwd]}
-                            _tc_qdisc_options $qdisc && return 0
-                        fi
-                    done
-                    _tc_one_of_list $QDISC_KIND
-                    _tc_one_of_list 'root ingress parent clsact'
-                    _tc_once_attr 'handle estimator stab'
-                    ;;
-                show)
-                    _tc_once_attr 'dev'
-                    _tc_one_of_list 'ingress clsact'
-                    _tc_once_attr '-statistics -details -raw -pretty -iec \
-                        -graph -name'
-                    ;;
-                help)
-                    return 0
-                    ;;
-                *)
-                    [[ $cword -eq $subcword ]] && \
-                        COMPREPLY=( $( compgen -W 'help add delete change \
-                            replace link show' -- "$cur" ) )
-                    ;;
-            esac
-            ;;
-
-        class)
-            case $subcmd in
-                add|change|replace|del|delete)
-                    if [[ $(($cword-$subcword)) -eq 1 ]]; then
-                        COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
-                        return 0
-                    fi
-                    local qdisc qdwd
-                    for ((qdwd=$subcword; qdwd < ${#words[@]}-1; qdwd++)); do
-                        if [[ $QDISC_KIND =~ ' '${words[qdwd]}' ' ]]; then
-                            qdisc=${words[qdwd]}
-                            _tc_qdisc_options $qdisc && return 0
-                        fi
-                    done
-                    _tc_one_of_list $QDISC_KIND
-                    _tc_one_of_list 'root parent'
-                    _tc_once_attr 'classid'
-                    ;;
-                show)
-                    _tc_once_attr 'dev'
-                    _tc_one_of_list 'root parent'
-                    _tc_once_attr '-statistics -details -raw -pretty -iec \
-                        -graph -name'
-                    ;;
-                help)
-                    return 0
-                    ;;
-                *)
-                    [[ $cword -eq $subcword ]] && \
-                        COMPREPLY=( $( compgen -W 'help add delete change \
-                            replace show' -- "$cur" ) )
-                    ;;
-            esac
-            ;;
-
-        filter)
-            case $subcmd in
-                add|change|replace|del|delete)
-                    if [[ $(($cword-$subcword)) -eq 1 ]]; then
-                        COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
-                        return 0
-                    fi
-                    local filter fltwd
-                    for ((fltwd=$subcword; fltwd < ${#words[@]}-1; fltwd++));
-                    do
-                        if [[ $FILTER_KIND =~ ' '${words[fltwd]}' ' ]]; then
-                            _tc_filter_options $fltwd && return 0
-                        fi
-                    done
-                    _tc_one_of_list $FILTER_KIND
-                    _tc_one_of_list 'root ingress egress parent'
-                    _tc_once_attr 'handle estimator pref protocol'
-                    ;;
-                show)
-                    _tc_once_attr 'dev'
-                    _tc_one_of_list 'root ingress egress parent'
-                    _tc_once_attr '-statistics -details -raw -pretty -iec \
-                        -graph -name'
-                    ;;
-                help)
-                    return 0
-                    ;;
-                *)
-                    [[ $cword -eq $subcword ]] && \
-                        COMPREPLY=( $( compgen -W 'help add delete change \
-                            replace show' -- "$cur" ) )
-                    ;;
-            esac
-            ;;
-
-        action)
-            case $subcmd in
-                add|change|replace)
-                    local action acwd
-                    for ((acwd=$subcword; acwd < ${#words[@]}-1; acwd++)); do
-                        if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then
-                            _tc_action_options $acwd && return 0
-                        fi
-                    done
-                    _tc_one_of_list $ACTION_KIND
-                    ;;
-                get|del|delete)
-                    _tc_once_attr 'index'
-                    ;;
-                lst|list|flush|show)
-                    _tc_one_of_list $ACTION_KIND
-                    ;;
-                *)
-                    [[ $cword -eq $subcword ]] && \
-                        COMPREPLY=( $( compgen -W 'help add delete change \
-                            replace show list flush action' -- "$cur" ) )
-                    ;;
-            esac
-            ;;
-
-        monitor)
-            COMPREPLY=( $( compgen -W 'help' -- "$cur" ) )
-            ;;
-
-        exec)
-            case $subcmd in
-                bpf)
-                    local excmd exwd EXEC_KIND=' import debug graft '
-                    for ((exwd=$subcword; exwd < ${#words[@]}-1; exwd++)); do
-                        if [[ $EXEC_KIND =~ ' '${words[exwd]}' ' ]]; then
-                            excmd=${words[exwd]}
-                            _tc_exec_options $excmd && return 0
-                        fi
-                    done
-                    _tc_one_of_list $EXEC_KIND
-                    ;;
-                *)
-                    [[ $cword -eq $subcword ]] && \
-                        COMPREPLY=( $( compgen -W 'bpf' -- "$cur" ) )
-                    ;;
-            esac
-            ;;
-    esac
-} &&
-complete -F _tc tc
-
-# ex: ts=4 sw=4 et filetype=sh
diff --git a/bridge/Android.mk b/bridge/Android.mk
deleted file mode 100644
index cf0487f..0000000
--- a/bridge/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := bridge
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := bridge.c \
-		   fdb.c \
-		   monitor.c \
-		   link.c \
-		   mdb.c \
-		   vlan.c
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include \
-		    $(LOCAL_PATH)/../include/uapi
-LOCAL_STATIC_LIBRARIES := libnetlink \
-			  libiprouteutil
-LOCAL_CFLAGS := \
-    -O2 -W -Wall \
-    -Wno-implicit-function-declaration \
-    -Wno-missing-field-initializers \
-    -Wno-pointer-arith \
-    -Wno-sign-compare \
-    -Wno-unused-parameter \
-    -Werror \
-    -D_GNU_SOURCE -DHAVE_SETNS
-include $(BUILD_EXECUTABLE)
diff --git a/bridge/Makefile b/bridge/Makefile
index c6b7d08..9800753 100644
--- a/bridge/Makefile
+++ b/bridge/Makefile
@@ -1,15 +1,18 @@
-# SPDX-License-Identifier: GPL-2.0
 BROBJ = bridge.o fdb.o monitor.o link.o mdb.o vlan.o
 
-include ../config.mk
+include ../Config
+
+ifeq ($(IP_CONFIG_SETNS),y)
+	CFLAGS += -DHAVE_SETNS
+endif
 
 all: bridge
 
-bridge: $(BROBJ) $(LIBNETLINK)
-	$(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
+bridge: $(BROBJ) $(LIBNETLINK) 
 
 install: all
 	install -m 0755 bridge $(DESTDIR)$(SBINDIR)
 
 clean:
 	rm -f $(BROBJ) bridge
+
diff --git a/bridge/br_common.h b/bridge/br_common.h
index b5798da..169a162 100644
--- a/bridge/br_common.h
+++ b/bridge/br_common.h
@@ -1,26 +1,20 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+extern int print_linkinfo(const struct sockaddr_nl *who,
+			  struct nlmsghdr *n,
+			  void *arg);
+extern int print_fdb(const struct sockaddr_nl *who,
+		     struct nlmsghdr *n, void *arg);
+extern int print_mdb(const struct sockaddr_nl *who,
+		     struct nlmsghdr *n, void *arg);
 
-#define MDB_RTA(r) \
-		((struct rtattr *)(((char *)(r)) + RTA_ALIGN(sizeof(struct br_mdb_entry))))
-
-#define MDB_RTR_RTA(r) \
-		((struct rtattr *)(((char *)(r)) + RTA_ALIGN(sizeof(__u32))))
-
-void print_vlan_info(struct rtattr *tb, int ifindex);
-int print_linkinfo(struct nlmsghdr *n, void *arg);
-int print_mdb_mon(struct nlmsghdr *n, void *arg);
-int print_fdb(struct nlmsghdr *n, void *arg);
-
-int do_fdb(int argc, char **argv);
-int do_mdb(int argc, char **argv);
-int do_monitor(int argc, char **argv);
-int do_vlan(int argc, char **argv);
-int do_link(int argc, char **argv);
+extern int do_fdb(int argc, char **argv);
+extern int do_mdb(int argc, char **argv);
+extern int do_monitor(int argc, char **argv);
+extern int do_vlan(int argc, char **argv);
+extern int do_link(int argc, char **argv);
 
 extern int preferred_family;
 extern int show_stats;
 extern int show_details;
 extern int timestamp;
 extern int compress_vlans;
-extern int json;
 extern struct rtnl_handle rth;
diff --git a/bridge/bridge.c b/bridge/bridge.c
index a50d9d5..72f153f 100644
--- a/bridge/bridge.c
+++ b/bridge/bridge.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Get/set/delete bridge with netlink
  *
@@ -16,19 +15,18 @@
 #include "utils.h"
 #include "br_common.h"
 #include "namespace.h"
-#include "color.h"
 
 struct rtnl_handle rth = { .fd = -1 };
 int preferred_family = AF_UNSPEC;
+int resolve_hosts;
 int oneline;
 int show_stats;
 int show_details;
-static int color;
 int compress_vlans;
-int json;
 int timestamp;
-static const char *batch_file;
+char *batch_file;
 int force;
+const char *_SL_;
 
 static void usage(void) __attribute__((noreturn));
 
@@ -40,7 +38,7 @@
 "where	OBJECT := { link | fdb | mdb | vlan | monitor }\n"
 "	OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] |\n"
 "		     -o[neline] | -t[imestamp] | -n[etns] name |\n"
-"		     -c[ompressvlans] -color -p[retty] -j[son] }\n");
+"		     -c[ompressvlans] }\n");
 	exit(-1);
 }
 
@@ -97,8 +95,6 @@
 		return EXIT_FAILURE;
 	}
 
-	rtnl_set_strict_dump(&rth);
-
 	cmdlineno = 0;
 	while (getcmdline(&line, &len, stdin) != -1) {
 		char *largv[100];
@@ -175,13 +171,8 @@
 				exit(-1);
 		} else if (matches(opt, "-compressvlans") == 0) {
 			++compress_vlans;
-		} else if (matches_color(opt, &color)) {
 		} else if (matches(opt, "-force") == 0) {
 			++force;
-		} else if (matches(opt, "-json") == 0) {
-			++json;
-		} else if (matches(opt, "-pretty") == 0) {
-			++pretty;
 		} else if (matches(opt, "-batch") == 0) {
 			argc--;
 			argv++;
@@ -199,16 +190,12 @@
 
 	_SL_ = oneline ? "\\" : "\n";
 
-	check_enable_color(color, json);
-
 	if (batch_file)
 		return batch(batch_file);
 
 	if (rtnl_open(&rth, 0) < 0)
 		exit(1);
 
-	rtnl_set_strict_dump(&rth);
-
 	if (argc > 1)
 		return do_cmd(argv[1], argc-1, argv+1);
 
diff --git a/bridge/fdb.c b/bridge/fdb.c
index 941ce2d..4d10925 100644
--- a/bridge/fdb.c
+++ b/bridge/fdb.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Get/set/delete fdb table with netlink
  *
@@ -22,29 +21,25 @@
 #include <linux/neighbour.h>
 #include <string.h>
 #include <limits.h>
-#include <stdbool.h>
 
-#include "json_print.h"
 #include "libnetlink.h"
 #include "br_common.h"
 #include "rt_names.h"
 #include "utils.h"
 
-static unsigned int filter_index, filter_vlan, filter_state, filter_master;
+static unsigned int filter_index;
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: bridge fdb { add | append | del | replace } ADDR dev DEV\n"
-		"              [ self ] [ master ] [ use ] [ router ] [ extern_learn ]\n"
-		"              [ sticky ] [ local | static | dynamic ] [ dst IPADDR ]\n"
-		"              [ vlan VID ] [ port PORT] [ vni VNI ] [ via DEV ]\n"
-		"              [ src_vni VNI ]\n"
-		"       bridge fdb [ show [ br BRDEV ] [ brport DEV ] [ vlan VID ] [ state STATE ] ]\n");
+	fprintf(stderr, "Usage: bridge fdb { add | append | del | replace } ADDR dev DEV\n"
+			"              [ self ] [ master ] [ use ] [ router ]\n"
+			"              [ local | temp ] [ dst IPADDR ] [ vlan VID ]\n"
+		        "              [ port PORT] [ vni VNI ] [ via DEV ]\n");
+	fprintf(stderr, "       bridge fdb [ show [ br BRDEV ] [ brport DEV ] ]\n");
 	exit(-1);
 }
 
-static const char *state_n2a(unsigned int s)
+static const char *state_n2a(unsigned s)
 {
 	static char buf[32];
 
@@ -64,80 +59,17 @@
 	return buf;
 }
 
-static int state_a2n(unsigned int *s, const char *arg)
-{
-	if (matches(arg, "permanent") == 0)
-		*s = NUD_PERMANENT;
-	else if (matches(arg, "static") == 0 || matches(arg, "temp") == 0)
-		*s = NUD_NOARP;
-	else if (matches(arg, "stale") == 0)
-		*s = NUD_STALE;
-	else if (matches(arg, "reachable") == 0 || matches(arg, "dynamic") == 0)
-		*s = NUD_REACHABLE;
-	else if (strcmp(arg, "all") == 0)
-		*s = ~0;
-	else if (get_unsigned(s, arg, 0))
-		return -1;
-
-	return 0;
-}
-
-static void fdb_print_flags(FILE *fp, unsigned int flags)
-{
-	open_json_array(PRINT_JSON,
-			is_json_context() ?  "flags" : "");
-
-	if (flags & NTF_SELF)
-		print_string(PRINT_ANY, NULL, "%s ", "self");
-
-	if (flags & NTF_ROUTER)
-		print_string(PRINT_ANY, NULL, "%s ", "router");
-
-	if (flags & NTF_EXT_LEARNED)
-		print_string(PRINT_ANY, NULL, "%s ", "extern_learn");
-
-	if (flags & NTF_OFFLOADED)
-		print_string(PRINT_ANY, NULL, "%s ", "offload");
-
-	if (flags & NTF_MASTER)
-		print_string(PRINT_ANY, NULL, "%s ", "master");
-
-	if (flags & NTF_STICKY)
-		print_string(PRINT_ANY, NULL, "%s ", "sticky");
-
-	close_json_array(PRINT_JSON, NULL);
-}
-
-static void fdb_print_stats(FILE *fp, const struct nda_cacheinfo *ci)
-{
-	static int hz;
-
-	if (!hz)
-		hz = get_user_hz();
-
-	if (is_json_context()) {
-		print_uint(PRINT_JSON, "used", NULL,
-				 ci->ndm_used / hz);
-		print_uint(PRINT_JSON, "updated", NULL,
-				ci->ndm_updated / hz);
-	} else {
-		fprintf(fp, "used %d/%d ", ci->ndm_used / hz,
-					ci->ndm_updated / hz);
-
-	}
-}
-
-int print_fdb(struct nlmsghdr *n, void *arg)
+int print_fdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
 	FILE *fp = arg;
 	struct ndmsg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
-	struct rtattr *tb[NDA_MAX+1];
-	__u16 vid = 0;
+	struct rtattr * tb[NDA_MAX+1];
 
 	if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) {
 		fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n",
 			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+
 		return 0;
 	}
 
@@ -153,153 +85,104 @@
 	if (filter_index && filter_index != r->ndm_ifindex)
 		return 0;
 
-	if (filter_state && !(r->ndm_state & filter_state))
-		return 0;
-
 	parse_rtattr(tb, NDA_MAX, NDA_RTA(r),
 		     n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
 
-	if (tb[NDA_VLAN])
-		vid = rta_getattr_u16(tb[NDA_VLAN]);
-
-	if (filter_vlan && filter_vlan != vid)
-		return 0;
-
-	open_json_object(NULL);
 	if (n->nlmsg_type == RTM_DELNEIGH)
-		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
+		fprintf(fp, "Deleted ");
 
 	if (tb[NDA_LLADDR]) {
-		const char *lladdr;
 		SPRINT_BUF(b1);
-
-		lladdr = ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
-				     RTA_PAYLOAD(tb[NDA_LLADDR]),
-				     ll_index_to_type(r->ndm_ifindex),
-				     b1, sizeof(b1));
-
-		print_color_string(PRINT_ANY, COLOR_MAC,
-				   "mac", "%s ", lladdr);
+		fprintf(fp, "%s ",
+			ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
+				    RTA_PAYLOAD(tb[NDA_LLADDR]),
+				    ll_index_to_type(r->ndm_ifindex),
+				    b1, sizeof(b1)));
 	}
 
 	if (!filter_index && r->ndm_ifindex)
-		print_color_string(PRINT_ANY, COLOR_IFNAME,
-				   "ifname", "dev %s ",
-				   ll_index_to_name(r->ndm_ifindex));
+		fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex));
 
 	if (tb[NDA_DST]) {
+		SPRINT_BUF(abuf);
 		int family = AF_INET;
-		const char *dst;
 
 		if (RTA_PAYLOAD(tb[NDA_DST]) == sizeof(struct in6_addr))
 			family = AF_INET6;
 
-		dst = format_host(family,
-				  RTA_PAYLOAD(tb[NDA_DST]),
-				  RTA_DATA(tb[NDA_DST]));
-
-		print_color_string(PRINT_ANY,
-				   ifa_family_color(family),
-				    "dst", "dst %s ", dst);
+		fprintf(fp, "dst %s ",
+			format_host(family,
+				    RTA_PAYLOAD(tb[NDA_DST]),
+				    RTA_DATA(tb[NDA_DST]),
+				    abuf, sizeof(abuf)));
 	}
 
-	if (vid)
-		print_uint(PRINT_ANY,
-				 "vlan", "vlan %hu ", vid);
+	if (tb[NDA_VLAN]) {
+		__u16 vid = rta_getattr_u16(tb[NDA_VLAN]);
+		fprintf(fp, "vlan %hu ", vid);
+	}
 
 	if (tb[NDA_PORT])
-		print_uint(PRINT_ANY,
-				 "port", "port %u ",
-				 rta_getattr_be16(tb[NDA_PORT]));
-
+		fprintf(fp, "port %d ", ntohs(rta_getattr_u16(tb[NDA_PORT])));
 	if (tb[NDA_VNI])
-		print_uint(PRINT_ANY,
-				 "vni", "vni %u ",
-				 rta_getattr_u32(tb[NDA_VNI]));
-
-	if (tb[NDA_SRC_VNI])
-		print_uint(PRINT_ANY,
-				 "src_vni", "src_vni %u ",
-				rta_getattr_u32(tb[NDA_SRC_VNI]));
-
+		fprintf(fp, "vni %d ", rta_getattr_u32(tb[NDA_VNI]));
 	if (tb[NDA_IFINDEX]) {
 		unsigned int ifindex = rta_getattr_u32(tb[NDA_IFINDEX]);
 
-		if (tb[NDA_LINK_NETNSID])
-			print_uint(PRINT_ANY,
-					 "viaIfIndex", "via ifindex %u ",
-					 ifindex);
-		else
-			print_string(PRINT_ANY,
-					   "viaIf", "via %s ",
-					   ll_index_to_name(ifindex));
-	}
+		if (ifindex) {
+			char ifname[IF_NAMESIZE];
 
+			if (!tb[NDA_LINK_NETNSID] &&
+			    if_indextoname(ifindex, ifname))
+				fprintf(fp, "via %s ", ifname);
+			else
+				fprintf(fp, "via ifindex %u ", ifindex);
+		}
+	}
 	if (tb[NDA_LINK_NETNSID])
-		print_uint(PRINT_ANY,
-				 "linkNetNsId", "link-netnsid %d ",
-				 rta_getattr_u32(tb[NDA_LINK_NETNSID]));
+		fprintf(fp, "link-netnsid %d ",
+			rta_getattr_u32(tb[NDA_LINK_NETNSID]));
 
-	if (show_stats && tb[NDA_CACHEINFO])
-		fdb_print_stats(fp, RTA_DATA(tb[NDA_CACHEINFO]));
+	if (show_stats && tb[NDA_CACHEINFO]) {
+		struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
+		int hz = get_user_hz();
 
-	fdb_print_flags(fp, r->ndm_flags);
-
-
+		fprintf(fp, "used %d/%d ", ci->ndm_used/hz,
+		       ci->ndm_updated/hz);
+	}
+	if (r->ndm_flags & NTF_SELF)
+		fprintf(fp, "self ");
 	if (tb[NDA_MASTER])
-		print_string(PRINT_ANY, "master", "master %s ",
-			     ll_index_to_name(rta_getattr_u32(tb[NDA_MASTER])));
+		fprintf(fp, "master %s ",
+			ll_index_to_name(rta_getattr_u32(tb[NDA_MASTER])));
+	else if (r->ndm_flags & NTF_MASTER)
+		fprintf(fp, "master ");
+	if (r->ndm_flags & NTF_ROUTER)
+		fprintf(fp, "router ");
+	if (r->ndm_flags & NTF_EXT_LEARNED)
+		fprintf(fp, "offload ");
 
-	print_string(PRINT_ANY, "state", "%s\n",
-			   state_n2a(r->ndm_state));
-	close_json_object();
+	fprintf(fp, "%s\n", state_n2a(r->ndm_state));
 	fflush(fp);
-	return 0;
-}
-
-static int fdb_linkdump_filter(struct nlmsghdr *nlh, int reqlen)
-{
-	int err;
-
-	if (filter_index) {
-		struct ifinfomsg *ifm = NLMSG_DATA(nlh);
-
-		ifm->ifi_index = filter_index;
-	}
-
-	if (filter_master) {
-		err = addattr32(nlh, reqlen, IFLA_MASTER, filter_master);
-		if (err)
-			return err;
-	}
-
-	return 0;
-}
-
-static int fdb_dump_filter(struct nlmsghdr *nlh, int reqlen)
-{
-	int err;
-
-	if (filter_index) {
-		struct ndmsg *ndm = NLMSG_DATA(nlh);
-
-		ndm->ndm_ifindex = filter_index;
-	}
-
-	if (filter_master) {
-		err = addattr32(nlh, reqlen, NDA_MASTER, filter_master);
-		if (err)
-			return err;
-	}
 
 	return 0;
 }
 
 static int fdb_show(int argc, char **argv)
 {
+	struct {
+		struct nlmsghdr 	n;
+		struct ifinfomsg	ifm;
+		char   			buf[256];
+	} req;
+
 	char *filter_dev = NULL;
 	char *br = NULL;
-	int rc;
+	int msg_size = sizeof(struct ifinfomsg);
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.ifm.ifi_family = PF_BRIDGE;
 
 	while (argc > 0) {
 		if ((strcmp(*argv, "brport") == 0) || strcmp(*argv, "dev") == 0) {
@@ -308,18 +191,6 @@
 		} else if (strcmp(*argv, "br") == 0) {
 			NEXT_ARG();
 			br = *argv;
-		} else if (strcmp(*argv, "vlan") == 0) {
-			NEXT_ARG();
-			if (filter_vlan)
-				duparg("vlan", *argv);
-			filter_vlan = atoi(*argv);
-		} else if (strcmp(*argv, "state") == 0) {
-			unsigned int state;
-
-			NEXT_ARG();
-			if (state_a2n(&state, *argv))
-				invarg("invalid state", *argv);
-			filter_state |= state;
 		} else {
 			if (matches(*argv, "help") == 0)
 				usage();
@@ -329,37 +200,34 @@
 
 	if (br) {
 		int br_ifindex = ll_name_to_index(br);
-
 		if (br_ifindex == 0) {
 			fprintf(stderr, "Cannot find bridge device \"%s\"\n", br);
 			return -1;
 		}
-		filter_master = br_ifindex;
+		addattr32(&req.n, sizeof(req), IFLA_MASTER, br_ifindex);
+		msg_size += RTA_LENGTH(4);
 	}
 
 	/*we'll keep around filter_dev for older kernels */
 	if (filter_dev) {
-		filter_index = ll_name_to_index(filter_dev);
-		if (!filter_index)
-			return nodev(filter_dev);
+		filter_index = if_nametoindex(filter_dev);
+		if (filter_index == 0) {
+			fprintf(stderr, "Cannot find device \"%s\"\n",
+				filter_dev);
+			return -1;
+		}
+		req.ifm.ifi_index = filter_index;
 	}
 
-	if (rth.flags & RTNL_HANDLE_F_STRICT_CHK)
-		rc = rtnl_neighdump_req(&rth, PF_BRIDGE, fdb_dump_filter);
-	else
-		rc = rtnl_fdb_linkdump_req_filter_fn(&rth, fdb_linkdump_filter);
-	if (rc < 0) {
+	if (rtnl_dump_request(&rth, RTM_GETNEIGH, &req.ifm, msg_size) < 0) {
 		perror("Cannot send dump request");
 		exit(1);
 	}
 
-	new_json_obj(json);
 	if (rtnl_dump_filter(&rth, print_fdb, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
-	delete_json_obj();
-	fflush(stdout);
 
 	return 0;
 }
@@ -367,16 +235,10 @@
 static int fdb_modify(int cmd, int flags, int argc, char **argv)
 {
 	struct {
-		struct nlmsghdr	n;
-		struct ndmsg		ndm;
-		char			buf[256];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST | flags,
-		.n.nlmsg_type = cmd,
-		.ndm.ndm_family = PF_BRIDGE,
-		.ndm.ndm_state = NUD_NOARP,
-	};
+		struct nlmsghdr 	n;
+		struct ndmsg 		ndm;
+		char   			buf[256];
+	} req;
 	char *addr = NULL;
 	char *d = NULL;
 	char abuf[ETH_ALEN];
@@ -384,11 +246,18 @@
 	inet_prefix dst;
 	unsigned long port = 0;
 	unsigned long vni = ~0;
-	unsigned long src_vni = ~0;
 	unsigned int via = 0;
 	char *endptr;
 	short vid = -1;
 
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+	req.n.nlmsg_type = cmd;
+	req.ndm.ndm_family = PF_BRIDGE;
+	req.ndm.ndm_state = NUD_NOARP;
+
 	while (argc > 0) {
 		if (strcmp(*argv, "dev") == 0) {
 			NEXT_ARG();
@@ -418,32 +287,22 @@
 			if ((endptr && *endptr) ||
 			    (vni >> 24) || vni == ULONG_MAX)
 				invarg("invalid VNI\n", *argv);
-		} else if (strcmp(*argv, "src_vni") == 0) {
-			NEXT_ARG();
-			src_vni = strtoul(*argv, &endptr, 0);
-			if ((endptr && *endptr) ||
-			    (src_vni >> 24) || src_vni == ULONG_MAX)
-				invarg("invalid src VNI\n", *argv);
 		} else if (strcmp(*argv, "via") == 0) {
 			NEXT_ARG();
-			via = ll_name_to_index(*argv);
-			if (!via)
-				exit(nodev(*argv));
+			via = if_nametoindex(*argv);
+			if (via == 0)
+				invarg("invalid device\n", *argv);
 		} else if (strcmp(*argv, "self") == 0) {
 			req.ndm.ndm_flags |= NTF_SELF;
 		} else if (matches(*argv, "master") == 0) {
 			req.ndm.ndm_flags |= NTF_MASTER;
 		} else if (matches(*argv, "router") == 0) {
 			req.ndm.ndm_flags |= NTF_ROUTER;
-		} else if (matches(*argv, "local") == 0 ||
+		} else if (matches(*argv, "local") == 0||
 			   matches(*argv, "permanent") == 0) {
 			req.ndm.ndm_state |= NUD_PERMANENT;
-		} else if (matches(*argv, "temp") == 0 ||
-			   matches(*argv, "static") == 0) {
+		} else if (matches(*argv, "temp") == 0) {
 			req.ndm.ndm_state |= NUD_REACHABLE;
-		} else if (matches(*argv, "dynamic") == 0) {
-			req.ndm.ndm_state |= NUD_REACHABLE;
-			req.ndm.ndm_state &= ~NUD_NOARP;
 		} else if (matches(*argv, "vlan") == 0) {
 			if (vid >= 0)
 				duparg2("vlan", *argv);
@@ -451,14 +310,10 @@
 			vid = atoi(*argv);
 		} else if (matches(*argv, "use") == 0) {
 			req.ndm.ndm_flags |= NTF_USE;
-		} else if (matches(*argv, "extern_learn") == 0) {
-			req.ndm.ndm_flags |= NTF_EXT_LEARNED;
-		} else if (matches(*argv, "sticky") == 0) {
-			req.ndm.ndm_flags |= NTF_STICKY;
 		} else {
-			if (strcmp(*argv, "to") == 0)
+			if (strcmp(*argv, "to") == 0) {
 				NEXT_ARG();
-
+			}
 			if (matches(*argv, "help") == 0)
 				usage();
 			if (addr)
@@ -503,16 +358,16 @@
 	}
 	if (vni != ~0)
 		addattr32(&req.n, sizeof(req), NDA_VNI, vni);
-	if (src_vni != ~0)
-		addattr32(&req.n, sizeof(req), NDA_SRC_VNI, src_vni);
 	if (via)
 		addattr32(&req.n, sizeof(req), NDA_IFINDEX, via);
 
 	req.ndm.ndm_ifindex = ll_name_to_index(d);
-	if (!req.ndm.ndm_ifindex)
-		return nodev(d);
+	if (req.ndm.ndm_ifindex == 0) {
+		fprintf(stderr, "Cannot find device \"%s\"\n", d);
+		return -1;
+	}
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		return -1;
 
 	return 0;
diff --git a/bridge/link.c b/bridge/link.c
index 074edf0..a9b1262 100644
--- a/bridge/link.c
+++ b/bridge/link.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -12,7 +11,6 @@
 #include <string.h>
 #include <stdbool.h>
 
-#include "json_print.h"
 #include "libnetlink.h"
 #include "utils.h"
 #include "br_common.h"
@@ -27,21 +25,17 @@
 	[BR_STATE_BLOCKING] = "blocking",
 };
 
-static const char *hw_mode[] = {
-	"VEB", "VEPA"
-};
+extern char *if_indextoname (unsigned int __ifindex, char *__ifname);
 
-static void print_link_flags(FILE *fp, unsigned int flags, unsigned int mdown)
+static void print_link_flags(FILE *fp, unsigned flags)
 {
-	open_json_array(PRINT_ANY, is_json_context() ? "flags" : "<");
+	fprintf(fp, "<");
 	if (flags & IFF_UP && !(flags & IFF_RUNNING))
-		print_string(PRINT_ANY, NULL,
-			     flags ? "%s," : "%s", "NO-CARRIER");
+		fprintf(fp, "NO-CARRIER%s", flags ? "," : "");
 	flags &= ~IFF_RUNNING;
-
-#define _PF(f) if (flags&IFF_##f) {					\
-		flags &= ~IFF_##f ;					\
-		print_string(PRINT_ANY, NULL, flags ? "%s," : "%s", #f); }
+#define _PF(f) if (flags&IFF_##f) { \
+                  flags &= ~IFF_##f ; \
+                  fprintf(fp, #f "%s", flags ? "," : ""); }
 	_PF(LOOPBACK);
 	_PF(BROADCAST);
 	_PF(POINTOPOINT);
@@ -61,152 +55,61 @@
 	_PF(DORMANT);
 	_PF(ECHO);
 #undef _PF
-	if (flags)
-		print_hex(PRINT_ANY, NULL, "%x", flags);
-	if (mdown)
-		print_string(PRINT_ANY, NULL, ",%s", "M-DOWN");
-	close_json_array(PRINT_ANY, "> ");
+        if (flags)
+		fprintf(fp, "%x", flags);
+	fprintf(fp, "> ");
 }
 
-static void print_portstate(__u8 state)
+static const char *oper_states[] = {
+	"UNKNOWN", "NOTPRESENT", "DOWN", "LOWERLAYERDOWN",
+	"TESTING", "DORMANT",	 "UP"
+};
+
+static const char *hw_mode[] = {"VEB", "VEPA"};
+
+static void print_operstate(FILE *f, __u8 state)
+{
+	if (state >= sizeof(oper_states)/sizeof(oper_states[0]))
+		fprintf(f, "state %#x ", state);
+	else
+		fprintf(f, "state %s ", oper_states[state]);
+}
+
+static void print_portstate(FILE *f, __u8 state)
 {
 	if (state <= BR_STATE_BLOCKING)
-		print_string(PRINT_ANY, "state",
-			     "state %s ", port_states[state]);
+		fprintf(f, "state %s ", port_states[state]);
 	else
-		print_uint(PRINT_ANY, "state",
-			     "state (%d) ", state);
+		fprintf(f, "state (%d) ", state);
 }
 
-static void print_onoff(FILE *fp, const char *flag, __u8 val)
+static void print_onoff(FILE *f, char *flag, __u8 val)
 {
-	if (is_json_context())
-		print_bool(PRINT_JSON, flag, NULL, val);
+	fprintf(f, "%s %s ", flag, val ? "on" : "off");
+}
+
+static void print_hwmode(FILE *f, __u16 mode)
+{
+	if (mode >= sizeof(hw_mode)/sizeof(hw_mode[0]))
+		fprintf(f, "hwmode %#hx ", mode);
 	else
-		fprintf(fp, "%s %s ", flag, val ? "on" : "off");
+		fprintf(f, "hwmode %s ", hw_mode[mode]);
 }
 
-static void print_hwmode(__u16 mode)
-{
-	if (mode >= ARRAY_SIZE(hw_mode))
-		print_0xhex(PRINT_ANY, "hwmode",
-			    "hwmode %#llx ", mode);
-	else
-		print_string(PRINT_ANY, "hwmode",
-			     "hwmode %s ", hw_mode[mode]);
-}
-
-static void print_protinfo(FILE *fp, struct rtattr *attr)
-{
-	if (attr->rta_type & NLA_F_NESTED) {
-		struct rtattr *prtb[IFLA_BRPORT_MAX + 1];
-
-		parse_rtattr_nested(prtb, IFLA_BRPORT_MAX, attr);
-
-		if (prtb[IFLA_BRPORT_STATE])
-			print_portstate(rta_getattr_u8(prtb[IFLA_BRPORT_STATE]));
-
-		if (prtb[IFLA_BRPORT_PRIORITY])
-			print_uint(PRINT_ANY, "priority",
-				   "priority %u ",
-				   rta_getattr_u16(prtb[IFLA_BRPORT_PRIORITY]));
-
-		if (prtb[IFLA_BRPORT_COST])
-			print_uint(PRINT_ANY, "cost",
-				   "cost %u ",
-				   rta_getattr_u32(prtb[IFLA_BRPORT_COST]));
-
-		if (!show_details)
-			return;
-
-		if (!is_json_context())
-			fprintf(fp, "%s    ", _SL_);
-
-		if (prtb[IFLA_BRPORT_MODE])
-			print_onoff(fp, "hairpin",
-				    rta_getattr_u8(prtb[IFLA_BRPORT_MODE]));
-		if (prtb[IFLA_BRPORT_GUARD])
-			print_onoff(fp, "guard",
-				    rta_getattr_u8(prtb[IFLA_BRPORT_GUARD]));
-		if (prtb[IFLA_BRPORT_PROTECT])
-			print_onoff(fp, "root_block",
-				    rta_getattr_u8(prtb[IFLA_BRPORT_PROTECT]));
-		if (prtb[IFLA_BRPORT_FAST_LEAVE])
-			print_onoff(fp, "fastleave",
-				    rta_getattr_u8(prtb[IFLA_BRPORT_FAST_LEAVE]));
-		if (prtb[IFLA_BRPORT_LEARNING])
-			print_onoff(fp, "learning",
-				    rta_getattr_u8(prtb[IFLA_BRPORT_LEARNING]));
-		if (prtb[IFLA_BRPORT_LEARNING_SYNC])
-			print_onoff(fp, "learning_sync",
-				    rta_getattr_u8(prtb[IFLA_BRPORT_LEARNING_SYNC]));
-		if (prtb[IFLA_BRPORT_UNICAST_FLOOD])
-			print_onoff(fp, "flood",
-				    rta_getattr_u8(prtb[IFLA_BRPORT_UNICAST_FLOOD]));
-		if (prtb[IFLA_BRPORT_MCAST_FLOOD])
-			print_onoff(fp, "mcast_flood",
-				    rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_FLOOD]));
-		if (prtb[IFLA_BRPORT_MCAST_TO_UCAST])
-			print_onoff(fp, "mcast_to_unicast",
-				    rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_TO_UCAST]));
-		if (prtb[IFLA_BRPORT_NEIGH_SUPPRESS])
-			print_onoff(fp, "neigh_suppress",
-				    rta_getattr_u8(prtb[IFLA_BRPORT_NEIGH_SUPPRESS]));
-		if (prtb[IFLA_BRPORT_VLAN_TUNNEL])
-			print_onoff(fp, "vlan_tunnel",
-				    rta_getattr_u8(prtb[IFLA_BRPORT_VLAN_TUNNEL]));
-
-		if (prtb[IFLA_BRPORT_BACKUP_PORT]) {
-			int ifidx;
-
-			ifidx = rta_getattr_u32(prtb[IFLA_BRPORT_BACKUP_PORT]);
-			print_string(PRINT_ANY,
-				     "backup_port", "backup_port %s ",
-				     ll_index_to_name(ifidx));
-		}
-
-		if (prtb[IFLA_BRPORT_ISOLATED])
-			print_onoff(fp, "isolated",
-				    rta_getattr_u8(prtb[IFLA_BRPORT_ISOLATED]));
-	} else
-		print_portstate(rta_getattr_u8(attr));
-}
-
-
-/*
- * This is reported by HW devices that have some bridging
- * capabilities.
- */
-static void print_af_spec(struct rtattr *attr, int ifindex)
-{
-	struct rtattr *aftb[IFLA_BRIDGE_MAX+1];
-
-	parse_rtattr_nested(aftb, IFLA_BRIDGE_MAX, attr);
-
-	if (aftb[IFLA_BRIDGE_MODE])
-		print_hwmode(rta_getattr_u16(aftb[IFLA_BRIDGE_MODE]));
-
-	if (!show_details)
-		return;
-
-	if (aftb[IFLA_BRIDGE_VLAN_INFO])
-		print_vlan_info(aftb[IFLA_BRIDGE_VLAN_INFO], ifindex);
-}
-
-int print_linkinfo(struct nlmsghdr *n, void *arg)
+int print_linkinfo(const struct sockaddr_nl *who,
+		   struct nlmsghdr *n, void *arg)
 {
 	FILE *fp = arg;
-	struct ifinfomsg *ifi = NLMSG_DATA(n);
-	struct rtattr *tb[IFLA_MAX+1];
-	unsigned int m_flag = 0;
 	int len = n->nlmsg_len;
-	const char *name;
+	struct ifinfomsg *ifi = NLMSG_DATA(n);
+	struct rtattr * tb[IFLA_MAX+1];
+	char b1[IFNAMSIZ];
 
 	len -= NLMSG_LENGTH(sizeof(*ifi));
 	if (len < 0) {
 		fprintf(stderr, "Message too short!\n");
 		return -1;
-	}
+        }
 
 	if (!(ifi->ifi_family == AF_BRIDGE || ifi->ifi_family == AF_UNSPEC))
 		return 0;
@@ -216,62 +119,116 @@
 
 	parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), len, NLA_F_NESTED);
 
-	name = get_ifname_rta(ifi->ifi_index, tb[IFLA_IFNAME]);
-	if (!name)
+	if (tb[IFLA_IFNAME] == NULL) {
+		fprintf(stderr, "BUG: nil ifname\n");
 		return -1;
-
-	open_json_object(NULL);
-	if (n->nlmsg_type == RTM_DELLINK)
-		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
-
-	print_int(PRINT_ANY, "ifindex", "%d: ", ifi->ifi_index);
-	m_flag = print_name_and_link("%s: ", name, tb);
-	print_link_flags(fp, ifi->ifi_flags, m_flag);
-
-	if (tb[IFLA_MTU])
-		print_int(PRINT_ANY,
-			  "mtu", "mtu %u ",
-			  rta_getattr_u32(tb[IFLA_MTU]));
-
-	if (tb[IFLA_MASTER]) {
-		int master = rta_getattr_u32(tb[IFLA_MASTER]);
-
-		print_string(PRINT_ANY, "master", "master %s ",
-			     ll_index_to_name(master));
 	}
 
-	if (tb[IFLA_PROTINFO])
-		print_protinfo(fp, tb[IFLA_PROTINFO]);
+	if (n->nlmsg_type == RTM_DELLINK)
+		fprintf(fp, "Deleted ");
 
-	if (tb[IFLA_AF_SPEC])
-		print_af_spec(tb[IFLA_AF_SPEC], ifi->ifi_index);
+	fprintf(fp, "%d: %s ", ifi->ifi_index,
+		tb[IFLA_IFNAME] ? rta_getattr_str(tb[IFLA_IFNAME]) : "<nil>");
 
-	print_string(PRINT_FP, NULL, "%s", "\n");
-	close_json_object();
+	if (tb[IFLA_OPERSTATE])
+		print_operstate(fp, rta_getattr_u8(tb[IFLA_OPERSTATE]));
+
+	if (tb[IFLA_LINK]) {
+		SPRINT_BUF(b1);
+		int iflink = rta_getattr_u32(tb[IFLA_LINK]);
+		if (iflink == 0)
+			fprintf(fp, "@NONE: ");
+		else
+			fprintf(fp, "@%s: ",
+				if_indextoname(iflink, b1));
+	} else
+		fprintf(fp, ": ");
+
+	print_link_flags(fp, ifi->ifi_flags);
+
+	if (tb[IFLA_MTU])
+		fprintf(fp, "mtu %u ", rta_getattr_u32(tb[IFLA_MTU]));
+
+	if (tb[IFLA_MASTER])
+		fprintf(fp, "master %s ",
+			if_indextoname(rta_getattr_u32(tb[IFLA_MASTER]), b1));
+
+	if (tb[IFLA_PROTINFO]) {
+		if (tb[IFLA_PROTINFO]->rta_type & NLA_F_NESTED) {
+			struct rtattr *prtb[IFLA_BRPORT_MAX+1];
+
+			parse_rtattr_nested(prtb, IFLA_BRPORT_MAX,
+					    tb[IFLA_PROTINFO]);
+
+			if (prtb[IFLA_BRPORT_STATE])
+				print_portstate(fp,
+						rta_getattr_u8(prtb[IFLA_BRPORT_STATE]));
+			if (prtb[IFLA_BRPORT_PRIORITY])
+				fprintf(fp, "priority %hu ",
+					rta_getattr_u16(prtb[IFLA_BRPORT_PRIORITY]));
+			if (prtb[IFLA_BRPORT_COST])
+				fprintf(fp, "cost %u ",
+					rta_getattr_u32(prtb[IFLA_BRPORT_COST]));
+
+			if (show_details) {
+				fprintf(fp, "%s    ", _SL_);
+
+				if (prtb[IFLA_BRPORT_MODE])
+					print_onoff(fp, "hairpin",
+						    rta_getattr_u8(prtb[IFLA_BRPORT_MODE]));
+				if (prtb[IFLA_BRPORT_GUARD])
+					print_onoff(fp, "guard",
+						    rta_getattr_u8(prtb[IFLA_BRPORT_GUARD]));
+				if (prtb[IFLA_BRPORT_PROTECT])
+					print_onoff(fp, "root_block",
+						    rta_getattr_u8(prtb[IFLA_BRPORT_PROTECT]));
+				if (prtb[IFLA_BRPORT_FAST_LEAVE])
+					print_onoff(fp, "fastleave",
+						    rta_getattr_u8(prtb[IFLA_BRPORT_FAST_LEAVE]));
+				if (prtb[IFLA_BRPORT_LEARNING])
+					print_onoff(fp, "learning",
+						    rta_getattr_u8(prtb[IFLA_BRPORT_LEARNING]));
+				if (prtb[IFLA_BRPORT_LEARNING_SYNC])
+					print_onoff(fp, "learning_sync",
+						    rta_getattr_u8(prtb[IFLA_BRPORT_LEARNING_SYNC]));
+				if (prtb[IFLA_BRPORT_UNICAST_FLOOD])
+					print_onoff(fp, "flood",
+						    rta_getattr_u8(prtb[IFLA_BRPORT_UNICAST_FLOOD]));
+			}
+		} else
+			print_portstate(fp, rta_getattr_u8(tb[IFLA_PROTINFO]));
+	}
+
+	if (tb[IFLA_AF_SPEC]) {
+		/* This is reported by HW devices that have some bridging
+		 * capabilities.
+		 */
+		struct rtattr *aftb[IFLA_BRIDGE_MAX+1];
+
+		parse_rtattr_nested(aftb, IFLA_BRIDGE_MAX, tb[IFLA_AF_SPEC]);
+
+		if (aftb[IFLA_BRIDGE_MODE])
+			print_hwmode(fp, rta_getattr_u16(aftb[IFLA_BRIDGE_MODE]));
+	}
+
+	fprintf(fp, "\n");
 	fflush(fp);
 	return 0;
 }
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: bridge link set dev DEV [ cost COST ] [ priority PRIO ] [ state STATE ]\n"
-		"                               [ guard {on | off} ]\n"
-		"                               [ hairpin {on | off} ]\n"
-		"                               [ fastleave {on | off} ]\n"
-		"                               [ root_block {on | off} ]\n"
-		"                               [ learning {on | off} ]\n"
-		"                               [ learning_sync {on | off} ]\n"
-		"                               [ flood {on | off} ]\n"
-		"                               [ mcast_flood {on | off} ]\n"
-		"                               [ mcast_to_unicast {on | off} ]\n"
-		"                               [ neigh_suppress {on | off} ]\n"
-		"                               [ vlan_tunnel {on | off} ]\n"
-		"                               [ isolated {on | off} ]\n"
-		"                               [ hwmode {vepa | veb} ]\n"
-		"                               [ backup_port DEVICE ] [ nobackup_port ]\n"
-		"                               [ self ] [ master ]\n"
-		"       bridge link show [dev DEV]\n");
+	fprintf(stderr, "Usage: bridge link set dev DEV [ cost COST ] [ priority PRIO ] [ state STATE ]\n");
+	fprintf(stderr, "                               [ guard {on | off} ]\n");
+	fprintf(stderr, "                               [ hairpin {on | off} ] \n");
+	fprintf(stderr, "                               [ fastleave {on | off} ]\n");
+	fprintf(stderr,	"                               [ root_block {on | off} ]\n");
+	fprintf(stderr,	"                               [ learning {on | off} ]\n");
+	fprintf(stderr,	"                               [ learning_sync {on | off} ]\n");
+	fprintf(stderr,	"                               [ flood {on | off} ]\n");
+	fprintf(stderr, "                               [ hwmode {vepa | veb} ]\n");
+	fprintf(stderr, "                               [ self ] [ master ]\n");
+	fprintf(stderr, "       bridge link show [dev DEV]\n");
 	exit(-1);
 }
 
@@ -297,22 +254,11 @@
 		struct nlmsghdr  n;
 		struct ifinfomsg ifm;
 		char             buf[512];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_SETLINK,
-		.ifm.ifi_family = PF_BRIDGE,
-	};
+	} req;
 	char *d = NULL;
-	int backup_port_idx = -1;
-	__s8 neigh_suppress = -1;
 	__s8 learning = -1;
 	__s8 learning_sync = -1;
 	__s8 flood = -1;
-	__s8 vlan_tunnel = -1;
-	__s8 mcast_flood = -1;
-	__s8 mcast_to_unicast = -1;
-	__s8 isolated = -1;
 	__s8 hairpin = -1;
 	__s8 bpdu_guard = -1;
 	__s8 fast_leave = -1;
@@ -324,6 +270,13 @@
 	__u16 flags = 0;
 	struct rtattr *nest;
 
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = RTM_SETLINK;
+	req.ifm.ifi_family = PF_BRIDGE;
+
 	while (argc > 0) {
 		if (strcmp(*argv, "dev") == 0) {
 			NEXT_ARG();
@@ -334,7 +287,7 @@
 				return -1;
 		} else if (strcmp(*argv, "hairpin") == 0) {
 			NEXT_ARG();
-			if (!on_off("hairpin", &hairpin, *argv))
+			if (!on_off("hairping", &hairpin, *argv))
 				return -1;
 		} else if (strcmp(*argv, "fastleave") == 0) {
 			NEXT_ARG();
@@ -356,14 +309,6 @@
 			NEXT_ARG();
 			if (!on_off("flood", &flood, *argv))
 				return -1;
-		} else if (strcmp(*argv, "mcast_flood") == 0) {
-			NEXT_ARG();
-			if (!on_off("mcast_flood", &mcast_flood, *argv))
-				return -1;
-		} else if (strcmp(*argv, "mcast_to_unicast") == 0) {
-			NEXT_ARG();
-			if (!on_off("mcast_to_unicast", &mcast_to_unicast, *argv))
-				return -1;
 		} else if (strcmp(*argv, "cost") == 0) {
 			NEXT_ARG();
 			cost = atoi(*argv);
@@ -373,8 +318,7 @@
 		} else if (strcmp(*argv, "state") == 0) {
 			NEXT_ARG();
 			char *endptr;
-			size_t nstates = ARRAY_SIZE(port_states);
-
+			size_t nstates = sizeof(port_states) / sizeof(*port_states);
 			state = strtol(*argv, &endptr, 10);
 			if (!(**argv != '\0' && *endptr == '\0')) {
 				for (state = 0; state < nstates; state++)
@@ -395,37 +339,14 @@
 				mode = BRIDGE_MODE_VEB;
 			else {
 				fprintf(stderr,
-					"Mode argument must be \"vepa\" or \"veb\".\n");
+					"Mode argument must be \"vepa\" or "
+					"\"veb\".\n");
 				return -1;
 			}
 		} else if (strcmp(*argv, "self") == 0) {
 			flags |= BRIDGE_FLAGS_SELF;
 		} else if (strcmp(*argv, "master") == 0) {
 			flags |= BRIDGE_FLAGS_MASTER;
-		} else if (strcmp(*argv, "neigh_suppress") == 0) {
-			NEXT_ARG();
-			if (!on_off("neigh_suppress", &neigh_suppress,
-				    *argv))
-				return -1;
-		} else if (strcmp(*argv, "vlan_tunnel") == 0) {
-			NEXT_ARG();
-			if (!on_off("vlan_tunnel", &vlan_tunnel,
-				    *argv))
-				return -1;
-		} else if (strcmp(*argv, "isolated") == 0) {
-			NEXT_ARG();
-			if (!on_off("isolated", &isolated, *argv))
-				return -1;
-		} else if (strcmp(*argv, "backup_port") == 0) {
-			NEXT_ARG();
-			backup_port_idx = ll_name_to_index(*argv);
-			if (!backup_port_idx) {
-				fprintf(stderr, "Error: device %s does not exist\n",
-					*argv);
-				return -1;
-			}
-		} else if (strcmp(*argv, "nobackup_port") == 0) {
-			backup_port_idx = 0;
 		} else {
 			usage();
 		}
@@ -460,12 +381,6 @@
 		addattr8(&req.n, sizeof(req), IFLA_BRPORT_PROTECT, root_block);
 	if (flood >= 0)
 		addattr8(&req.n, sizeof(req), IFLA_BRPORT_UNICAST_FLOOD, flood);
-	if (mcast_flood >= 0)
-		addattr8(&req.n, sizeof(req), IFLA_BRPORT_MCAST_FLOOD,
-			 mcast_flood);
-	if (mcast_to_unicast >= 0)
-		addattr8(&req.n, sizeof(req), IFLA_BRPORT_MCAST_TO_UCAST,
-			 mcast_to_unicast);
 	if (learning >= 0)
 		addattr8(&req.n, sizeof(req), IFLA_BRPORT_LEARNING, learning);
 	if (learning_sync >= 0)
@@ -481,19 +396,6 @@
 	if (state >= 0)
 		addattr8(&req.n, sizeof(req), IFLA_BRPORT_STATE, state);
 
-	if (neigh_suppress != -1)
-		addattr8(&req.n, sizeof(req), IFLA_BRPORT_NEIGH_SUPPRESS,
-			 neigh_suppress);
-	if (vlan_tunnel != -1)
-		addattr8(&req.n, sizeof(req), IFLA_BRPORT_VLAN_TUNNEL,
-			 vlan_tunnel);
-	if (isolated != -1)
-		addattr8(&req.n, sizeof(req), IFLA_BRPORT_ISOLATED, isolated);
-
-	if (backup_port_idx != -1)
-		addattr32(&req.n, sizeof(req), IFLA_BRPORT_BACKUP_PORT,
-			  backup_port_idx);
-
 	addattr_nest_end(&req.n, nest);
 
 	/* IFLA_AF_SPEC nested attribute. Contains IFLA_BRIDGE_FLAGS that
@@ -513,7 +415,7 @@
 		addattr_nest_end(&req.n, nest);
 	}
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		return -1;
 
 	return 0;
@@ -534,34 +436,22 @@
 	}
 
 	if (filter_dev) {
-		filter_index = ll_name_to_index(filter_dev);
-		if (!filter_index)
-			return nodev(filter_dev);
-	}
-
-	if (show_details) {
-		if (rtnl_linkdump_req_filter(&rth, PF_BRIDGE,
-					     (compress_vlans ?
-					      RTEXT_FILTER_BRVLAN_COMPRESSED :
-					      RTEXT_FILTER_BRVLAN)) < 0) {
-			perror("Cannon send dump request");
-			exit(1);
-		}
-	} else {
-		if (rtnl_linkdump_req(&rth, PF_BRIDGE) < 0) {
-			perror("Cannon send dump request");
-			exit(1);
+		if ((filter_index = ll_name_to_index(filter_dev)) == 0) {
+			fprintf(stderr, "Cannot find device \"%s\"\n",
+				filter_dev);
+			return -1;
 		}
 	}
 
-	new_json_obj(json);
+	if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETLINK) < 0) {
+		perror("Cannon send dump request");
+		exit(1);
+	}
+
 	if (rtnl_dump_filter(&rth, print_linkinfo, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
-
-	delete_json_obj();
-	fflush(stdout);
 	return 0;
 }
 
diff --git a/bridge/mdb.c b/bridge/mdb.c
index 928ae56..24c4903 100644
--- a/bridge/mdb.c
+++ b/bridge/mdb.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Get mdb table with netlink
  */
@@ -19,216 +18,80 @@
 #include "br_common.h"
 #include "rt_names.h"
 #include "utils.h"
-#include "json_print.h"
 
 #ifndef MDBA_RTA
 #define MDBA_RTA(r) \
-	((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg))))
+	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg))))
 #endif
 
-static unsigned int filter_index, filter_vlan;
+static unsigned int filter_index;
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: bridge mdb { add | del } dev DEV port PORT grp GROUP [permanent | temp] [vid VID]\n"
-		"       bridge mdb {show} [ dev DEV ] [ vid VID ]\n");
+	fprintf(stderr, "Usage: bridge mdb { add | del } dev DEV port PORT grp GROUP [permanent | temp] [vid VID]\n");
+	fprintf(stderr, "       bridge mdb {show} [ dev DEV ]\n");
 	exit(-1);
 }
 
-static bool is_temp_mcast_rtr(__u8 type)
+static void br_print_router_ports(FILE *f, struct rtattr *attr)
 {
-	return type == MDB_RTR_TYPE_TEMP_QUERY || type == MDB_RTR_TYPE_TEMP;
-}
-
-static const char *format_timer(__u32 ticks)
-{
-	struct timeval tv;
-	static char tbuf[32];
-
-	__jiffies_to_tv(&tv, ticks);
-	snprintf(tbuf, sizeof(tbuf), "%4lu.%.2lu",
-		 (unsigned long)tv.tv_sec,
-		 (unsigned long)tv.tv_usec / 10000);
-
-	return tbuf;
-}
-
-static void __print_router_port_stats(FILE *f, struct rtattr *pattr)
-{
-	struct rtattr *tb[MDBA_ROUTER_PATTR_MAX + 1];
-
-	parse_rtattr(tb, MDBA_ROUTER_PATTR_MAX, MDB_RTR_RTA(RTA_DATA(pattr)),
-		     RTA_PAYLOAD(pattr) - RTA_ALIGN(sizeof(uint32_t)));
-
-	if (tb[MDBA_ROUTER_PATTR_TIMER]) {
-		__u32 timer = rta_getattr_u32(tb[MDBA_ROUTER_PATTR_TIMER]);
-
-		print_string(PRINT_ANY, "timer", " %s",
-			     format_timer(timer));
-	}
-
-	if (tb[MDBA_ROUTER_PATTR_TYPE]) {
-		__u8 type = rta_getattr_u8(tb[MDBA_ROUTER_PATTR_TYPE]);
-
-		print_string(PRINT_ANY, "type", " %s",
-			     is_temp_mcast_rtr(type) ? "temp" : "permanent");
-	}
-}
-
-static void br_print_router_ports(FILE *f, struct rtattr *attr,
-				  const char *brifname)
-{
-	int rem = RTA_PAYLOAD(attr);
-	struct rtattr *i;
-
-	if (is_json_context())
-		open_json_array(PRINT_JSON, brifname);
-	else if (!show_stats)
-		fprintf(f, "router ports on %s: ", brifname);
-
-	for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
-		uint32_t *port_ifindex = RTA_DATA(i);
-		const char *port_ifname = ll_index_to_name(*port_ifindex);
-
-		if (is_json_context()) {
-			open_json_object(NULL);
-			print_string(PRINT_JSON, "port", NULL, port_ifname);
-
-			if (show_stats)
-				__print_router_port_stats(f, i);
-			close_json_object();
-		} else if (show_stats) {
-			fprintf(f, "router ports on %s: %s",
-				brifname, port_ifname);
-
-			__print_router_port_stats(f, i);
-			fprintf(f, "\n");
-		} else {
-			fprintf(f, "%s ", port_ifname);
-		}
-	}
-
-	if (!show_stats)
-		print_nl();
-
-	close_json_array(PRINT_JSON, NULL);
-}
-
-static void print_mdb_entry(FILE *f, int ifindex, const struct br_mdb_entry *e,
-			    struct nlmsghdr *n, struct rtattr **tb)
-{
-	SPRINT_BUF(abuf);
-	const char *dev;
-	const void *src;
-	int af;
-
-	if (filter_vlan && e->vid != filter_vlan)
-		return;
-
-	af = e->addr.proto == htons(ETH_P_IP) ? AF_INET : AF_INET6;
-	src = af == AF_INET ? (const void *)&e->addr.u.ip4 :
-			      (const void *)&e->addr.u.ip6;
-	dev = ll_index_to_name(ifindex);
-
-	open_json_object(NULL);
-
-	print_int(PRINT_JSON, "index", NULL, ifindex);
-	print_color_string(PRINT_ANY, COLOR_IFNAME, "dev", "dev %s", dev);
-	print_string(PRINT_ANY, "port", " port %s",
-		     ll_index_to_name(e->ifindex));
-
-	print_color_string(PRINT_ANY, ifa_family_color(af),
-			    "grp", " grp %s",
-			    inet_ntop(af, src, abuf, sizeof(abuf)));
-
-	print_string(PRINT_ANY, "state", " %s",
-			   (e->state & MDB_PERMANENT) ? "permanent" : "temp");
-
-	open_json_array(PRINT_JSON, "flags");
-	if (e->flags & MDB_FLAGS_OFFLOAD)
-		print_string(PRINT_ANY, NULL, " %s", "offload");
-	close_json_array(PRINT_JSON, NULL);
-
-	if (e->vid)
-		print_uint(PRINT_ANY, "vid", " vid %u", e->vid);
-
-	if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) {
-		__u32 timer = rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER]);
-
-		print_string(PRINT_ANY, "timer", " %s",
-			     format_timer(timer));
-	}
-
-	print_nl();
-	close_json_object();
-}
-
-static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr,
-			       struct nlmsghdr *n)
-{
-	struct rtattr *etb[MDBA_MDB_EATTR_MAX + 1];
-	struct br_mdb_entry *e;
+	uint32_t *port_ifindex;
 	struct rtattr *i;
 	int rem;
 
 	rem = RTA_PAYLOAD(attr);
 	for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
-		e = RTA_DATA(i);
-		parse_rtattr(etb, MDBA_MDB_EATTR_MAX, MDB_RTA(RTA_DATA(i)),
-			     RTA_PAYLOAD(i) - RTA_ALIGN(sizeof(*e)));
-		print_mdb_entry(f, ifindex, e, n, etb);
+		port_ifindex = RTA_DATA(i);
+		fprintf(f, "%s ", ll_index_to_name(*port_ifindex));
 	}
+
+	fprintf(f, "\n");
 }
 
-static void print_mdb_entries(FILE *fp, struct nlmsghdr *n,
-			      int ifindex,  struct rtattr *mdb)
+static void print_mdb_entry(FILE *f, int ifindex, struct br_mdb_entry *e,
+			    struct nlmsghdr *n)
 {
-	int rem = RTA_PAYLOAD(mdb);
+	SPRINT_BUF(abuf);
+	const void *src;
+	int af;
+
+	af = e->addr.proto == htons(ETH_P_IP) ? AF_INET : AF_INET6;
+	src = af == AF_INET ? (const void *)&e->addr.u.ip4 :
+			      (const void *)&e->addr.u.ip6;
+	if (n->nlmsg_type == RTM_DELMDB)
+		fprintf(f, "Deleted ");
+	fprintf(f, "dev %s port %s grp %s %s", ll_index_to_name(ifindex),
+		ll_index_to_name(e->ifindex),
+		inet_ntop(af, src, abuf, sizeof(abuf)),
+		(e->state & MDB_PERMANENT) ? "permanent" : "temp");
+	if (e->vid)
+		fprintf(f, " vid %hu", e->vid);
+	fprintf(f, "\n");
+}
+
+static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr,
+			       struct nlmsghdr *n)
+{
 	struct rtattr *i;
+	int rem;
+	struct br_mdb_entry *e;
 
-	for (i = RTA_DATA(mdb); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
-		br_print_mdb_entry(fp, ifindex, i, n);
-}
-
-static void print_router_entries(FILE *fp, struct nlmsghdr *n,
-				 int ifindex, struct rtattr *router)
-{
-	const char *brifname = ll_index_to_name(ifindex);
-
-	if (n->nlmsg_type == RTM_GETMDB) {
-		if (show_details)
-			br_print_router_ports(fp, router, brifname);
-	} else {
-		struct rtattr *i = RTA_DATA(router);
-		uint32_t *port_ifindex = RTA_DATA(i);
-		const char *port_name = ll_index_to_name(*port_ifindex);
-
-		if (is_json_context()) {
-			open_json_array(PRINT_JSON, brifname);
-			open_json_object(NULL);
-
-			print_string(PRINT_JSON, "port", NULL,
-				     port_name);
-			close_json_object();
-			close_json_array(PRINT_JSON, NULL);
-		} else {
-			fprintf(fp, "router port dev %s master %s\n",
-				port_name, brifname);
-		}
+	rem = RTA_PAYLOAD(attr);
+	for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+		e = RTA_DATA(i);
+		print_mdb_entry(f, ifindex, e, n);
 	}
 }
 
-static int __parse_mdb_nlmsg(struct nlmsghdr *n, struct rtattr **tb)
+int print_mdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
+	FILE *fp = arg;
 	struct br_port_msg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
+	struct rtattr *tb[MDBA_MAX+1], *i;
 
-	if (n->nlmsg_type != RTM_GETMDB &&
-	    n->nlmsg_type != RTM_NEWMDB &&
-	    n->nlmsg_type != RTM_DELMDB) {
-		fprintf(stderr,
-			"Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n",
+	if (n->nlmsg_type != RTM_GETMDB && n->nlmsg_type != RTM_NEWMDB && n->nlmsg_type != RTM_DELMDB) {
+		fprintf(stderr, "Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n",
 			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
 
 		return 0;
@@ -245,62 +108,34 @@
 
 	parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
 
-	return 1;
-}
+	if (tb[MDBA_MDB]) {
+		int rem = RTA_PAYLOAD(tb[MDBA_MDB]);
 
-static int print_mdbs(struct nlmsghdr *n, void *arg)
-{
-	struct br_port_msg *r = NLMSG_DATA(n);
-	struct rtattr *tb[MDBA_MAX+1];
-	FILE *fp = arg;
-	int ret;
+		for (i = RTA_DATA(tb[MDBA_MDB]); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
+			br_print_mdb_entry(fp, r->ifindex, i, n);
+	}
 
-	ret = __parse_mdb_nlmsg(n, tb);
-	if (ret != 1)
-		return ret;
+	if (tb[MDBA_ROUTER]) {
+		if (n->nlmsg_type == RTM_GETMDB) {
+			if (show_details) {
+				fprintf(fp, "router ports on %s: ",
+					ll_index_to_name(r->ifindex));
+				br_print_router_ports(fp, tb[MDBA_ROUTER]);
+			}
+		} else {
+			uint32_t *port_ifindex;
 
-	if (tb[MDBA_MDB])
-		print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
+			i = RTA_DATA(tb[MDBA_ROUTER]);
+			port_ifindex = RTA_DATA(i);
+			if (n->nlmsg_type == RTM_DELMDB)
+				fprintf(fp, "Deleted ");
+			fprintf(fp, "router port dev %s master %s\n",
+				ll_index_to_name(*port_ifindex),
+				ll_index_to_name(r->ifindex));
+		}
+	}
 
-	return 0;
-}
-
-static int print_rtrs(struct nlmsghdr *n, void *arg)
-{
-	struct br_port_msg *r = NLMSG_DATA(n);
-	struct rtattr *tb[MDBA_MAX+1];
-	FILE *fp = arg;
-	int ret;
-
-	ret = __parse_mdb_nlmsg(n, tb);
-	if (ret != 1)
-		return ret;
-
-	if (tb[MDBA_ROUTER])
-		print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
-
-	return 0;
-}
-
-int print_mdb_mon(struct nlmsghdr *n, void *arg)
-{
-	struct br_port_msg *r = NLMSG_DATA(n);
-	struct rtattr *tb[MDBA_MAX+1];
-	FILE *fp = arg;
-	int ret;
-
-	ret = __parse_mdb_nlmsg(n, tb);
-	if (ret != 1)
-		return ret;
-
-	if (n->nlmsg_type == RTM_DELMDB)
-		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
-
-	if (tb[MDBA_MDB])
-		print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
-
-	if (tb[MDBA_ROUTER])
-		print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
+	fflush(fp);
 
 	return 0;
 }
@@ -315,53 +150,28 @@
 			if (filter_dev)
 				duparg("dev", *argv);
 			filter_dev = *argv;
-		} else if (strcmp(*argv, "vid") == 0) {
-			NEXT_ARG();
-			if (filter_vlan)
-				duparg("vid", *argv);
-			filter_vlan = atoi(*argv);
 		}
 		argc--; argv++;
 	}
 
 	if (filter_dev) {
-		filter_index = ll_name_to_index(filter_dev);
-		if (!filter_index)
-			return nodev(filter_dev);
+		filter_index = if_nametoindex(filter_dev);
+		if (filter_index == 0) {
+			fprintf(stderr, "Cannot find device \"%s\"\n",
+				filter_dev);
+			return -1;
+		}
 	}
 
-	new_json_obj(json);
-	open_json_object(NULL);
-
-	/* get mdb entries */
-	if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
+	if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETMDB) < 0) {
 		perror("Cannot send dump request");
 		return -1;
 	}
 
-	open_json_array(PRINT_JSON, "mdb");
-	if (rtnl_dump_filter(&rth, print_mdbs, stdout) < 0) {
+	if (rtnl_dump_filter(&rth, print_mdb, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		return -1;
 	}
-	close_json_array(PRINT_JSON, NULL);
-
-	/* get router ports */
-	if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
-		perror("Cannot send dump request");
-		return -1;
-	}
-
-	open_json_object("router");
-	if (rtnl_dump_filter(&rth, print_rtrs, stdout) < 0) {
-		fprintf(stderr, "Dump terminated\n");
-		return -1;
-	}
-	close_json_object();
-
-	close_json_object();
-	delete_json_obj();
-	fflush(stdout);
 
 	return 0;
 }
@@ -369,19 +179,22 @@
 static int mdb_modify(int cmd, int flags, int argc, char **argv)
 {
 	struct {
-		struct nlmsghdr	n;
+		struct nlmsghdr 	n;
 		struct br_port_msg	bpm;
-		char			buf[1024];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
-		.n.nlmsg_flags = NLM_F_REQUEST | flags,
-		.n.nlmsg_type = cmd,
-		.bpm.family = PF_BRIDGE,
-	};
-	struct br_mdb_entry entry = {};
+		char   			buf[1024];
+	} req;
+	struct br_mdb_entry entry;
 	char *d = NULL, *p = NULL, *grp = NULL;
 	short vid = 0;
 
+	memset(&req, 0, sizeof(req));
+	memset(&entry, 0, sizeof(entry));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg));
+	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+	req.n.nlmsg_type = cmd;
+	req.bpm.family = PF_BRIDGE;
+
 	while (argc > 0) {
 		if (strcmp(*argv, "dev") == 0) {
 			NEXT_ARG();
@@ -413,12 +226,16 @@
 	}
 
 	req.bpm.ifindex = ll_name_to_index(d);
-	if (!req.bpm.ifindex)
-		return nodev(d);
+	if (req.bpm.ifindex == 0) {
+		fprintf(stderr, "Cannot find device \"%s\"\n", d);
+		return -1;
+	}
 
 	entry.ifindex = ll_name_to_index(p);
-	if (!entry.ifindex)
-		return nodev(p);
+	if (entry.ifindex == 0) {
+		fprintf(stderr, "Cannot find device \"%s\"\n", p);
+		return -1;
+	}
 
 	if (!inet_pton(AF_INET, grp, &entry.addr.u.ip4)) {
 		if (!inet_pton(AF_INET6, grp, &entry.addr.u.ip6)) {
@@ -432,7 +249,7 @@
 	entry.vid = vid;
 	addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		return -1;
 
 	return 0;
diff --git a/bridge/monitor.c b/bridge/monitor.c
index 08439a6..d8341ec 100644
--- a/bridge/monitor.c
+++ b/bridge/monitor.c
@@ -27,7 +27,7 @@
 
 
 static void usage(void) __attribute__((noreturn));
-static int prefix_banner;
+int prefix_banner;
 
 static void usage(void)
 {
@@ -35,7 +35,8 @@
 	exit(-1);
 }
 
-static int accept_msg(struct rtnl_ctrl_data *ctrl,
+static int accept_msg(const struct sockaddr_nl *who,
+		      struct rtnl_ctrl_data *ctrl,
 		      struct nlmsghdr *n, void *arg)
 {
 	FILE *fp = arg;
@@ -49,19 +50,19 @@
 		if (prefix_banner)
 			fprintf(fp, "[LINK]");
 
-		return print_linkinfo(n, arg);
+		return print_linkinfo(who, n, arg);
 
 	case RTM_NEWNEIGH:
 	case RTM_DELNEIGH:
 		if (prefix_banner)
 			fprintf(fp, "[NEIGH]");
-		return print_fdb(n, arg);
+		return print_fdb(who, n, arg);
 
 	case RTM_NEWMDB:
 	case RTM_DELMDB:
 		if (prefix_banner)
 			fprintf(fp, "[MDB]");
-		return print_mdb_mon(n, arg);
+		return print_mdb(who, n, arg);
 
 	case NLMSG_TSTAMP:
 		print_nlmsg_timestamp(fp, n);
@@ -75,10 +76,10 @@
 int do_monitor(int argc, char **argv)
 {
 	char *file = NULL;
-	unsigned int groups = ~RTMGRP_TC;
-	int llink = 0;
-	int lneigh = 0;
-	int lmdb = 0;
+	unsigned groups = ~RTMGRP_TC;
+	int llink=0;
+	int lneigh=0;
+	int lmdb=0;
 
 	rtnl_close(&rth);
 
@@ -87,7 +88,7 @@
 			NEXT_ARG();
 			file = *argv;
 		} else if (matches(*argv, "link") == 0) {
-			llink = 1;
+			llink=1;
 			groups = 0;
 		} else if (matches(*argv, "fdb") == 0) {
 			lneigh = 1;
@@ -97,7 +98,7 @@
 			groups = 0;
 		} else if (strcmp(*argv, "all") == 0) {
 			groups = ~RTMGRP_TC;
-			prefix_banner = 1;
+			prefix_banner=1;
 		} else if (matches(*argv, "help") == 0) {
 			usage();
 		} else {
@@ -121,7 +122,6 @@
 	if (file) {
 		FILE *fp;
 		int err;
-
 		fp = fopen(file, "r");
 		if (fp == NULL) {
 			perror("Cannot fopen");
@@ -141,3 +141,4 @@
 
 	return 0;
 }
+
diff --git a/bridge/vlan.c b/bridge/vlan.c
index 6d33b0a..ac2f523 100644
--- a/bridge/vlan.c
+++ b/bridge/vlan.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -10,139 +9,41 @@
 #include <linux/if_ether.h>
 #include <string.h>
 
-#include "json_print.h"
 #include "libnetlink.h"
 #include "br_common.h"
 #include "utils.h"
 
-static unsigned int filter_index, filter_vlan;
-static int show_vlan_tunnel_info = 0;
+static unsigned int filter_index;
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ tunnel_info id TUNNEL_ID ]\n"
-		"                                                     [ pvid ] [ untagged ]\n"
-		"                                                     [ self ] [ master ]\n"
-		"       bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n"
-		"       bridge vlan { tunnelshow } [ dev DEV ] [ vid VLAN_ID ]\n");
+	fprintf(stderr, "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ pvid] [ untagged ]\n");
+	fprintf(stderr, "                                                     [ self ] [ master ]\n");
+	fprintf(stderr, "       bridge vlan { show } [ dev DEV ]\n");
 	exit(-1);
 }
 
-static int parse_tunnel_info(int *argcp, char ***argvp, __u32 *tun_id_start,
-			     __u32 *tun_id_end)
-{
-	char **argv = *argvp;
-	int argc = *argcp;
-	char *t;
-
-	NEXT_ARG();
-	if (!matches(*argv, "id")) {
-		NEXT_ARG();
-		t = strchr(*argv, '-');
-		if (t) {
-			*t = '\0';
-			if (get_u32(tun_id_start, *argv, 0) ||
-				    *tun_id_start >= 1u << 24)
-				invarg("invalid tun id", *argv);
-			if (get_u32(tun_id_end, t + 1, 0) ||
-				    *tun_id_end >= 1u << 24)
-				invarg("invalid tun id", *argv);
-
-		} else {
-			if (get_u32(tun_id_start, *argv, 0) ||
-				    *tun_id_start >= 1u << 24)
-				invarg("invalid tun id", *argv);
-		}
-	} else {
-		invarg("tunnel id expected", *argv);
-	}
-
-	*argcp = argc;
-	*argvp = argv;
-
-	return 0;
-}
-
-static int add_tunnel_info(struct nlmsghdr *n, int reqsize,
-			   __u16 vid, __u32 tun_id, __u16 flags)
-{
-	struct rtattr *tinfo;
-
-	tinfo = addattr_nest(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_INFO);
-	addattr32(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_ID, tun_id);
-	addattr32(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_VID, vid);
-	addattr32(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, flags);
-
-	addattr_nest_end(n, tinfo);
-
-	return 0;
-}
-
-static int add_tunnel_info_range(struct nlmsghdr *n, int reqsize,
-				 __u16 vid_start, int16_t vid_end,
-				 __u32 tun_id_start, __u32 tun_id_end)
-{
-	if (vid_end != -1 && (vid_end - vid_start) > 0) {
-		add_tunnel_info(n, reqsize, vid_start, tun_id_start,
-				BRIDGE_VLAN_INFO_RANGE_BEGIN);
-
-		add_tunnel_info(n, reqsize, vid_end, tun_id_end,
-				BRIDGE_VLAN_INFO_RANGE_END);
-	} else {
-		add_tunnel_info(n, reqsize, vid_start, tun_id_start, 0);
-	}
-
-	return 0;
-}
-
-static int add_vlan_info_range(struct nlmsghdr *n, int reqsize, __u16 vid_start,
-			       int16_t vid_end, __u16 flags)
-{
-	struct bridge_vlan_info vinfo = {};
-
-	vinfo.flags = flags;
-	vinfo.vid = vid_start;
-	if (vid_end != -1) {
-		/* send vlan range start */
-		addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo,
-			  sizeof(vinfo));
-		vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
-
-		/* Now send the vlan range end */
-		vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
-		vinfo.vid = vid_end;
-		addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo,
-			  sizeof(vinfo));
-	} else {
-		addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo,
-			  sizeof(vinfo));
-	}
-
-	return 0;
-}
-
 static int vlan_modify(int cmd, int argc, char **argv)
 {
 	struct {
-		struct nlmsghdr	n;
-		struct ifinfomsg	ifm;
-		char			buf[1024];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = cmd,
-		.ifm.ifi_family = PF_BRIDGE,
-	};
+		struct nlmsghdr 	n;
+		struct ifinfomsg 	ifm;
+		char   			buf[1024];
+	} req;
 	char *d = NULL;
 	short vid = -1;
 	short vid_end = -1;
 	struct rtattr *afspec;
-	struct bridge_vlan_info vinfo = {};
-	bool tunnel_info_set = false;
+	struct bridge_vlan_info vinfo;
 	unsigned short flags = 0;
-	__u32 tun_id_start = 0;
-	__u32 tun_id_end = 0;
+
+	memset(&vinfo, 0, sizeof(vinfo));
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = cmd;
+	req.ifm.ifi_family = PF_BRIDGE;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "dev") == 0) {
@@ -150,7 +51,6 @@
 			d = *argv;
 		} else if (strcmp(*argv, "vid") == 0) {
 			char *p;
-
 			NEXT_ARG();
 			p = strchr(*argv, '-');
 			if (p) {
@@ -170,15 +70,10 @@
 			vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
 		} else if (strcmp(*argv, "untagged") == 0) {
 			vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
-		} else if (strcmp(*argv, "tunnel_info") == 0) {
-				if (parse_tunnel_info(&argc, &argv,
-						      &tun_id_start,
-						      &tun_id_end))
-					return -1;
-				tunnel_info_set = true;
 		} else {
-			if (matches(*argv, "help") == 0)
+			if (matches(*argv, "help") == 0) {
 				NEXT_ARG();
+			}
 		}
 		argc--; argv++;
 	}
@@ -217,142 +112,39 @@
 	if (flags)
 		addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags);
 
-	if (tunnel_info_set)
-		add_tunnel_info_range(&req.n, sizeof(req), vid, vid_end,
-				      tun_id_start, tun_id_end);
-	else
-		add_vlan_info_range(&req.n, sizeof(req), vid, vid_end,
-				    vinfo.flags);
+	vinfo.vid = vid;
+	if (vid_end != -1) {
+		/* send vlan range start */
+		addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
+			  sizeof(vinfo));
+		vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
+
+		/* Now send the vlan range end */
+		vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
+		vinfo.vid = vid_end;
+		addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
+			  sizeof(vinfo));
+	} else {
+		addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
+			  sizeof(vinfo));
+	}
 
 	addattr_nest_end(&req.n, afspec);
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		return -1;
 
 	return 0;
 }
 
-/* In order to use this function for both filtering and non-filtering cases
- * we need to make it a tristate:
- * return -1 - if filtering we've gone over so don't continue
- * return  0 - skip entry and continue (applies to range start or to entries
- *             which are less than filter_vlan)
- * return  1 - print the entry and continue
- */
-static int filter_vlan_check(__u16 vid, __u16 flags)
+static int print_vlan(const struct sockaddr_nl *who,
+		      struct nlmsghdr *n,
+		      void *arg)
 {
-	/* if we're filtering we should stop on the first greater entry */
-	if (filter_vlan && vid > filter_vlan &&
-	    !(flags & BRIDGE_VLAN_INFO_RANGE_END))
-		return -1;
-	if ((flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) ||
-	    vid < filter_vlan)
-		return 0;
-
-	return 1;
-}
-
-static void open_vlan_port(int ifi_index, const char *fmt)
-{
-	open_json_object(NULL);
-	print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", fmt,
-			   ll_index_to_name(ifi_index));
-	open_json_array(PRINT_JSON, "vlans");
-}
-
-static void close_vlan_port(void)
-{
-	close_json_array(PRINT_JSON, NULL);
-	close_json_object();
-}
-
-static void print_range(const char *name, __u16 start, __u16 id)
-{
-	char end[64];
-
-	snprintf(end, sizeof(end), "%sEnd", name);
-
-	print_hu(PRINT_ANY, name, "\t %hu", start);
-	if (start != id)
-		print_hu(PRINT_ANY, end, "-%hu", id);
-
-}
-
-static void print_vlan_tunnel_info(FILE *fp, struct rtattr *tb, int ifindex)
-{
-	struct rtattr *i, *list = tb;
-	int rem = RTA_PAYLOAD(list);
-	__u16 last_vid_start = 0;
-	__u32 last_tunid_start = 0;
-
-	if (!filter_vlan)
-		open_vlan_port(ifindex, "%s");
-
-	open_json_array(PRINT_JSON, "tunnel");
-	for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
-		struct rtattr *ttb[IFLA_BRIDGE_VLAN_TUNNEL_MAX+1];
-		__u32 tunnel_id = 0;
-		__u16 tunnel_vid = 0;
-		__u16 tunnel_flags = 0;
-		int vcheck_ret;
-
-		if (i->rta_type != IFLA_BRIDGE_VLAN_TUNNEL_INFO)
-			continue;
-
-		parse_rtattr(ttb, IFLA_BRIDGE_VLAN_TUNNEL_MAX,
-			     RTA_DATA(i), RTA_PAYLOAD(i));
-
-		if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_VID])
-			tunnel_vid =
-				rta_getattr_u32(ttb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
-		else
-			continue;
-
-		if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_ID])
-			tunnel_id =
-				rta_getattr_u32(ttb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
-
-		if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
-			tunnel_flags =
-				rta_getattr_u32(ttb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
-
-		if (!(tunnel_flags & BRIDGE_VLAN_INFO_RANGE_END)) {
-			last_vid_start = tunnel_vid;
-			last_tunid_start = tunnel_id;
-		}
-
-		vcheck_ret = filter_vlan_check(tunnel_vid, tunnel_flags);
-		if (vcheck_ret == -1)
-			break;
-		else if (vcheck_ret == 0)
-			continue;
-
-		if (tunnel_flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
-			continue;
-
-		if (filter_vlan)
-			open_vlan_port(ifindex, "%s");
-
-		open_json_object(NULL);
-		print_range("vlan", last_vid_start, tunnel_vid);
-		print_range("tunid", last_tunid_start, tunnel_id);
-		close_json_object();
-
-		print_string(PRINT_FP, NULL, "%s", _SL_);
-		if (filter_vlan)
-			close_vlan_port();
-	}
-
-	if (!filter_vlan)
-		close_vlan_port();
-}
-
-static int print_vlan_tunnel(struct nlmsghdr *n, void *arg)
-{
-	struct ifinfomsg *ifm = NLMSG_DATA(n);
-	struct rtattr *tb[IFLA_MAX+1];
-	int len = n->nlmsg_len;
 	FILE *fp = arg;
+	struct ifinfomsg *ifm = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr * tb[IFLA_MAX+1];
 
 	if (n->nlmsg_type != RTM_NEWLINK) {
 		fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n",
@@ -376,169 +168,34 @@
 
 	/* if AF_SPEC isn't there, vlan table is not preset for this port */
 	if (!tb[IFLA_AF_SPEC]) {
-		if (!filter_vlan && !is_json_context()) {
-			color_fprintf(fp, COLOR_IFNAME, "%s",
-				      ll_index_to_name(ifm->ifi_index));
-			fprintf(fp, "\tNone\n");
+		fprintf(fp, "%s\tNone\n", ll_index_to_name(ifm->ifi_index));
+		return 0;
+	} else {
+		struct rtattr *i, *list = tb[IFLA_AF_SPEC];
+		int rem = RTA_PAYLOAD(list);
+
+		fprintf(fp, "%s", ll_index_to_name(ifm->ifi_index));
+		for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+			struct bridge_vlan_info *vinfo;
+
+			if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
+				continue;
+
+			vinfo = RTA_DATA(i);
+			if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END)
+				fprintf(fp, "-%hu", vinfo->vid);
+			else
+				fprintf(fp, "\t %hu", vinfo->vid);
+			if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
+				continue;
+			if (vinfo->flags & BRIDGE_VLAN_INFO_PVID)
+				fprintf(fp, " PVID");
+			if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED)
+				fprintf(fp, " Egress Untagged");
+			fprintf(fp, "\n");
 		}
-		return 0;
 	}
-
-	print_vlan_tunnel_info(fp, tb[IFLA_AF_SPEC], ifm->ifi_index);
-
-	fflush(fp);
-	return 0;
-}
-
-static int print_vlan(struct nlmsghdr *n, void *arg)
-{
-	FILE *fp = arg;
-	struct ifinfomsg *ifm = NLMSG_DATA(n);
-	int len = n->nlmsg_len;
-	struct rtattr *tb[IFLA_MAX+1];
-
-	if (n->nlmsg_type != RTM_NEWLINK) {
-		fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n",
-			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
-		return 0;
-	}
-
-	len -= NLMSG_LENGTH(sizeof(*ifm));
-	if (len < 0) {
-		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
-		return -1;
-	}
-
-	if (ifm->ifi_family != AF_BRIDGE)
-		return 0;
-
-	if (filter_index && filter_index != ifm->ifi_index)
-		return 0;
-
-	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len);
-
-	/* if AF_SPEC isn't there, vlan table is not preset for this port */
-	if (!tb[IFLA_AF_SPEC]) {
-		if (!filter_vlan && !is_json_context()) {
-			color_fprintf(fp, COLOR_IFNAME, "%s",
-				      ll_index_to_name(ifm->ifi_index));
-			fprintf(fp, "\tNone\n");
-		}
-		return 0;
-	}
-
-	print_vlan_info(tb[IFLA_AF_SPEC], ifm->ifi_index);
-	print_string(PRINT_FP, NULL, "%s", _SL_);
-
-	fflush(fp);
-	return 0;
-}
-
-static void print_vlan_flags(__u16 flags)
-{
-	if (flags == 0)
-		return;
-
-	open_json_array(PRINT_JSON, "flags");
-	if (flags & BRIDGE_VLAN_INFO_PVID)
-		print_string(PRINT_ANY, NULL, " %s", "PVID");
-
-	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
-		print_string(PRINT_ANY, NULL, " %s", "Egress Untagged");
-	close_json_array(PRINT_JSON, NULL);
-}
-
-static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats)
-{
-	open_json_object(NULL);
-	print_hu(PRINT_ANY, "vid", " %hu", vstats->vid);
-
-	print_vlan_flags(vstats->flags);
-
-	print_lluint(PRINT_ANY, "rx_bytes",
-		     "\n                   RX: %llu bytes",
-		     vstats->rx_bytes);
-	print_lluint(PRINT_ANY, "rx_packets", " %llu packets\n",
-		vstats->rx_packets);
-	print_lluint(PRINT_ANY, "tx_bytes",
-		     "                   TX: %llu bytes",
-		     vstats->tx_bytes);
-	print_lluint(PRINT_ANY, "tx_packets", " %llu packets\n",
-		vstats->tx_packets);
-	close_json_object();
-}
-
-static void print_vlan_stats_attr(struct rtattr *attr, int ifindex)
-{
-	struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1];
-	struct rtattr *i, *list;
-	bool found_vlan = false;
-	int rem;
-
-	parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr),
-		     RTA_PAYLOAD(attr));
-	if (!brtb[LINK_XSTATS_TYPE_BRIDGE])
-		return;
-
-	list = brtb[LINK_XSTATS_TYPE_BRIDGE];
-	rem = RTA_PAYLOAD(list);
-
-	for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
-		const struct bridge_vlan_xstats *vstats = RTA_DATA(i);
-
-		if (i->rta_type != BRIDGE_XSTATS_VLAN)
-			continue;
-
-		if (filter_vlan && filter_vlan != vstats->vid)
-			continue;
-
-		/* skip pure port entries, they'll be dumped via the slave stats call */
-		if ((vstats->flags & BRIDGE_VLAN_INFO_MASTER) &&
-		    !(vstats->flags & BRIDGE_VLAN_INFO_BRENTRY))
-			continue;
-
-		/* found vlan stats, first time print the interface name */
-		if (!found_vlan) {
-			open_vlan_port(ifindex, "%-16s");
-			found_vlan = true;
-		} else {
-			print_string(PRINT_FP, NULL, "%-16s", "");
-		}
-		print_one_vlan_stats(vstats);
-	}
-
-	/* vlan_port is opened only if there are any vlan stats */
-	if (found_vlan)
-		close_vlan_port();
-}
-
-static int print_vlan_stats(struct nlmsghdr *n, void *arg)
-{
-	struct if_stats_msg *ifsm = NLMSG_DATA(n);
-	struct rtattr *tb[IFLA_STATS_MAX+1];
-	int len = n->nlmsg_len;
-	FILE *fp = arg;
-
-	len -= NLMSG_LENGTH(sizeof(*ifsm));
-	if (len < 0) {
-		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
-		return -1;
-	}
-
-	if (filter_index && filter_index != ifsm->ifindex)
-		return 0;
-
-	parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
-
-	/* We have to check if any of the two attrs are usable */
-	if (tb[IFLA_STATS_LINK_XSTATS])
-		print_vlan_stats_attr(tb[IFLA_STATS_LINK_XSTATS],
-				      ifsm->ifindex);
-
-	if (tb[IFLA_STATS_LINK_XSTATS_SLAVE])
-		print_vlan_stats_attr(tb[IFLA_STATS_LINK_XSTATS_SLAVE],
-				      ifsm->ifindex);
-
+	fprintf(fp, "\n");
 	fflush(fp);
 	return 0;
 }
@@ -546,7 +203,6 @@
 static int vlan_show(int argc, char **argv)
 {
 	char *filter_dev = NULL;
-	int ret = 0;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "dev") == 0) {
@@ -554,116 +210,35 @@
 			if (filter_dev)
 				duparg("dev", *argv);
 			filter_dev = *argv;
-		} else if (strcmp(*argv, "vid") == 0) {
-			NEXT_ARG();
-			if (filter_vlan)
-				duparg("vid", *argv);
-			filter_vlan = atoi(*argv);
 		}
 		argc--; argv++;
 	}
 
 	if (filter_dev) {
-		filter_index = ll_name_to_index(filter_dev);
-		if (!filter_index)
-			return nodev(filter_dev);
-	}
-
-	new_json_obj(json);
-
-	if (!show_stats) {
-		if (rtnl_linkdump_req_filter(&rth, PF_BRIDGE,
-					     (compress_vlans ?
-					      RTEXT_FILTER_BRVLAN_COMPRESSED :
-					      RTEXT_FILTER_BRVLAN)) < 0) {
-			perror("Cannont send dump request");
-			exit(1);
-		}
-
-		if (!is_json_context()) {
-			if (show_vlan_tunnel_info)
-				printf("port\tvlan ids\ttunnel id\n");
-			else
-				printf("port\tvlan ids\n");
-		}
-
-		if (show_vlan_tunnel_info)
-			ret = rtnl_dump_filter(&rth, print_vlan_tunnel,
-					       stdout);
-		else
-			ret = rtnl_dump_filter(&rth, print_vlan, stdout);
-		if (ret < 0) {
-			fprintf(stderr, "Dump ternminated\n");
-			exit(1);
-		}
-	} else {
-		__u32 filt_mask;
-
-		filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS);
-		if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
-			perror("Cannont send dump request");
-			exit(1);
-		}
-
-		if (!is_json_context())
-			printf("%-16s vlan id\n", "port");
-
-		if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
-			fprintf(stderr, "Dump terminated\n");
-			exit(1);
-		}
-
-		filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE);
-		if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
-			perror("Cannont send slave dump request");
-			exit(1);
-		}
-
-		if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
-			fprintf(stderr, "Dump terminated\n");
-			exit(1);
+		if ((filter_index = if_nametoindex(filter_dev)) == 0) {
+			fprintf(stderr, "Cannot find device \"%s\"\n",
+			       filter_dev);
+			return -1;
 		}
 	}
 
-	delete_json_obj();
-	fflush(stdout);
+	if (rtnl_wilddump_req_filter(&rth, PF_BRIDGE, RTM_GETLINK,
+				    (compress_vlans ?
+				    RTEXT_FILTER_BRVLAN_COMPRESSED :
+				    RTEXT_FILTER_BRVLAN)) < 0) {
+		perror("Cannont send dump request");
+		exit(1);
+	}
+
+	printf("port\tvlan ids\n");
+	if (rtnl_dump_filter(&rth, print_vlan, stdout) < 0) {
+		fprintf(stderr, "Dump ternminated\n");
+		exit(1);
+	}
+
 	return 0;
 }
 
-void print_vlan_info(struct rtattr *tb, int ifindex)
-{
-	struct rtattr *i, *list = tb;
-	int rem = RTA_PAYLOAD(list);
-	__u16 last_vid_start = 0;
-
-	open_vlan_port(ifindex, "%s");
-
-	for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
-		struct bridge_vlan_info *vinfo;
-		int vcheck_ret;
-
-		if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
-			continue;
-
-		vinfo = RTA_DATA(i);
-
-		if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
-			last_vid_start = vinfo->vid;
-		vcheck_ret = filter_vlan_check(vinfo->vid, vinfo->flags);
-		if (vcheck_ret == -1)
-			break;
-		else if (vcheck_ret == 0)
-			continue;
-
-		open_json_object(NULL);
-		print_range("vlan", last_vid_start, vinfo->vid);
-
-		print_vlan_flags(vinfo->flags);
-		close_json_object();
-		print_string(PRINT_FP, NULL, "%s", _SL_);
-	}
-	close_vlan_port();
-}
 
 int do_vlan(int argc, char **argv)
 {
@@ -678,16 +253,11 @@
 		    matches(*argv, "lst") == 0 ||
 		    matches(*argv, "list") == 0)
 			return vlan_show(argc-1, argv+1);
-		if (matches(*argv, "tunnelshow") == 0) {
-			show_vlan_tunnel_info = 1;
-			return vlan_show(argc-1, argv+1);
-		}
 		if (matches(*argv, "help") == 0)
 			usage();
-	} else {
+	} else
 		return vlan_show(0, NULL);
-	}
 
-	fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vlan help\".\n", *argv);
+	fprintf(stderr, "Command \"%s\" is unknown, try \"bridge fdb help\".\n", *argv);
 	exit(-1);
 }
diff --git a/configure b/configure
index 45fcffb..d2540b0 100755
--- a/configure
+++ b/configure
@@ -1,24 +1,38 @@
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0
+#! /bin/bash
 # This is not an autoconf generated configure
 #
 INCLUDE=${1:-"$PWD/include"}
 
-# Output file which is input to Makefile
-CONFIG=config.mk
-
 # Make a temp directory in build tree.
 TMPDIR=$(mktemp -d config.XXXXXX)
 trap 'status=$?; rm -rf $TMPDIR; exit $status' EXIT HUP INT QUIT TERM
 
+check_prog()
+{
+    echo -n "$2"
+    command -v $1 >/dev/null 2>&1 && (echo "$3:=y" >> Config; echo "yes") || (echo "no"; return 1)
+}
+
+check_docs()
+{
+    if check_prog latex " latex: " HAVE_LATEX; then
+        check_prog pdflatex " pdflatex: " HAVE_PDFLATEX || echo " WARNING: no PDF docs can be built from LaTeX files"
+        check_prog sgml2latex " sgml2latex: " HAVE_SGML2LATEX || echo " WARNING: no LaTeX files can be build from SGML files"
+    else
+        echo " WARNING: no docs can be built from LaTeX files"
+    fi
+
+    check_prog sgml2html " sgml2html: " HAVE_SGML2HTML || echo " WARNING: no HTML docs can be built from SGML"
+}
+
 check_toolchain()
 {
     : ${PKG_CONFIG:=pkg-config}
     : ${AR=ar}
     : ${CC=gcc}
-    echo "PKG_CONFIG:=${PKG_CONFIG}" >>$CONFIG
-    echo "AR:=${AR}" >>$CONFIG
-    echo "CC:=${CC}" >>$CONFIG
+    echo "PKG_CONFIG:=${PKG_CONFIG}" >>Config
+    echo "AR:=${AR}" >>Config
+    echo "CC:=${CC}" >>Config
 }
 
 check_atm()
@@ -32,8 +46,10 @@
 }
 EOF
 
-    if $CC -I$INCLUDE -o $TMPDIR/atmtest $TMPDIR/atmtest.c -latm >/dev/null 2>&1; then
-	echo "TC_CONFIG_ATM:=y" >>$CONFIG
+    $CC -I$INCLUDE -o $TMPDIR/atmtest $TMPDIR/atmtest.c -latm >/dev/null 2>&1
+    if [ $? -eq 0 ]
+    then
+	echo "TC_CONFIG_ATM:=y" >>Config
 	echo yes
     else
 	echo no
@@ -41,13 +57,6 @@
     rm -f $TMPDIR/atmtest.c $TMPDIR/atmtest
 }
 
-check_xtables()
-{
-	if ! ${PKG_CONFIG} xtables --exists; then
-		echo "TC_CONFIG_NO_XT:=y" >>$CONFIG
-	fi
-}
-
 check_xt()
 {
     #check if we have xtables from iptables >= 1.4.5.
@@ -71,8 +80,9 @@
 EOF
 
     if $CC -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
+	$(${PKG_CONFIG} xtables --cflags --libs) -ldl >/dev/null 2>&1
+    then
+	echo "TC_CONFIG_XT:=y" >>Config
 	echo "using xtables"
     fi
     rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest
@@ -80,10 +90,13 @@
 
 check_xt_old()
 {
-    # bail if previous XT checks has already succeeded.
-    grep -q TC_CONFIG_XT $CONFIG && return
+    # bail if previous XT checks has already succeded.
+    if grep -q TC_CONFIG_XT Config
+    then
+	return
+    fi
 
-    #check if we don't need our internal header ..
+    #check if we dont need our internal header ..
     cat >$TMPDIR/ipttest.c <<EOF
 #include <xtables.h>
 char *lib_dir;
@@ -105,8 +118,10 @@
 
 EOF
 
-    if $CC -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL -ldl >/dev/null 2>&1; then
-	echo "TC_CONFIG_XT_OLD:=y" >>$CONFIG
+    $CC -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 $TMPDIR/ipttest.c $TMPDIR/ipttest
@@ -114,8 +129,11 @@
 
 check_xt_old_internal_h()
 {
-    # bail if previous XT checks has already succeeded.
-    grep -q TC_CONFIG_XT $CONFIG && return
+    # bail if previous XT checks has already succeded.
+    if grep -q TC_CONFIG_XT Config
+    then
+	return
+    fi
 
     #check if we need our own internal.h
     cat >$TMPDIR/ipttest.c <<EOF
@@ -139,16 +157,20 @@
 }
 
 EOF
-	if $CC -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL -ldl >/dev/null 2>&1; then
+	$CC -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
+	    echo "TC_CONFIG_XT_OLD_H:=y" >>Config
 	fi
 	rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest
 }
 
 check_ipt()
 {
-	if ! grep TC_CONFIG_XT $CONFIG > /dev/null; then
+	if ! grep TC_CONFIG_XT Config > /dev/null
+	then
 		echo "using iptables"
 	fi
 }
@@ -158,16 +180,16 @@
 	IPT_LIB_DIR=$(${PKG_CONFIG} --variable=xtlibdir xtables)
 	if [ -n "$IPT_LIB_DIR" ]; then
 		echo $IPT_LIB_DIR
-		echo "IPT_LIB_DIR:=$IPT_LIB_DIR" >> $CONFIG
+		echo "IPT_LIB_DIR:=$IPT_LIB_DIR" >> Config
 		return
 	fi
 
-	for dir in /lib /usr/lib /usr/local/lib; do
-		for file in "xtables" "iptables"; do
-			file="$dir/$file/lib*t_*so"
+	for dir in /lib /usr/lib /usr/local/lib
+	do
+		for file in $dir/{xtables,iptables}/lib*t_*so ; do
 			if [ -f $file ]; then
 				echo ${file%/*}
-				echo "IPT_LIB_DIR:=${file%/*}" >> $CONFIG
+				echo "IPT_LIB_DIR:=${file%/*}" >> Config
 				return
 			fi
 		done
@@ -185,10 +207,11 @@
 	return 0;
 }
 EOF
-    if $CC -I$INCLUDE -o $TMPDIR/setnstest $TMPDIR/setnstest.c >/dev/null 2>&1; then
-	echo "IP_CONFIG_SETNS:=y" >>$CONFIG
+    $CC -I$INCLUDE -o $TMPDIR/setnstest $TMPDIR/setnstest.c >/dev/null 2>&1
+    if [ $? -eq 0 ]
+    then
+	echo "IP_CONFIG_SETNS:=y" >>Config
 	echo "yes"
-	echo "CFLAGS += -DHAVE_SETNS" >>$CONFIG
     else
 	echo "no"
     fi
@@ -216,8 +239,9 @@
 #endif
 EOF
 
-    if $CC -I$INCLUDE -o $TMPDIR/ipsettest $TMPDIR/ipsettest.c >/dev/null 2>&1; then
-	echo "TC_CONFIG_IPSET:=y" >>$CONFIG
+    if $CC -I$INCLUDE -o $TMPDIR/ipsettest $TMPDIR/ipsettest.c >/dev/null 2>&1
+    then
+	echo "TC_CONFIG_IPSET:=y" >>Config
 	echo "yes"
     else
 	echo "no"
@@ -227,26 +251,34 @@
 
 check_elf()
 {
-    if ${PKG_CONFIG} libelf --exists; then
-	echo "HAVE_ELF:=y" >>$CONFIG
-	echo "yes"
+    cat >$TMPDIR/elftest.c <<EOF
+#include <libelf.h>
+#include <gelf.h>
+int main(void)
+{
+	Elf_Scn *scn;
+	GElf_Shdr shdr;
+	return elf_version(EV_CURRENT);
+}
+EOF
 
-	echo 'CFLAGS += -DHAVE_ELF' `${PKG_CONFIG} libelf --cflags` >> $CONFIG
-	echo 'LDLIBS += ' `${PKG_CONFIG} libelf --libs` >>$CONFIG
+    if $CC -I$INCLUDE -o $TMPDIR/elftest $TMPDIR/elftest.c -lelf >/dev/null 2>&1
+    then
+	echo "TC_CONFIG_ELF:=y" >>Config
+	echo "yes"
     else
 	echo "no"
     fi
+    rm -f $TMPDIR/elftest.c $TMPDIR/elftest
 }
 
 check_selinux()
 # SELinux is a compile time option in the ss utility
 {
-	if ${PKG_CONFIG} libselinux --exists; then
-		echo "HAVE_SELINUX:=y" >>$CONFIG
+	if ${PKG_CONFIG} libselinux --exists
+	then
+		echo "HAVE_SELINUX:=y" >>Config
 		echo "yes"
-
-		echo 'LDLIBS +=' `${PKG_CONFIG} --libs libselinux` >>$CONFIG
-		echo 'CFLAGS += -DHAVE_SELINUX' `${PKG_CONFIG} --cflags libselinux` >>$CONFIG
 	else
 		echo "no"
 	fi
@@ -254,12 +286,10 @@
 
 check_mnl()
 {
-	if ${PKG_CONFIG} libmnl --exists; then
-		echo "HAVE_MNL:=y" >>$CONFIG
+	if ${PKG_CONFIG} libmnl --exists
+	then
+		echo "HAVE_MNL:=y" >>Config
 		echo "yes"
-
-		echo 'CFLAGS += -DHAVE_LIBMNL' `${PKG_CONFIG} libmnl --cflags` >>$CONFIG
-		echo 'LDLIBS +=' `${PKG_CONFIG} libmnl --libs` >> $CONFIG
 	else
 		echo "no"
 	fi
@@ -276,8 +306,10 @@
 	return 0;
 }
 EOF
-    if $CC -I$INCLUDE -o $TMPDIR/dbtest $TMPDIR/dbtest.c -ldb >/dev/null 2>&1; then
-	echo "HAVE_BERKELEY_DB:=y" >>$CONFIG
+    $CC -I$INCLUDE -o $TMPDIR/dbtest $TMPDIR/dbtest.c -ldb >/dev/null 2>&1
+    if [ $? -eq 0 ]
+    then
+	echo "HAVE_BERKELEY_DB:=y" >>Config
 	echo "yes"
     else
 	echo "no"
@@ -285,73 +317,7 @@
     rm -f $TMPDIR/dbtest.c $TMPDIR/dbtest
 }
 
-check_strlcpy()
-{
-    cat >$TMPDIR/strtest.c <<EOF
-#include <string.h>
-int main(int argc, char **argv) {
-	char dst[10];
-	strlcpy(dst, "test", sizeof(dst));
-	return 0;
-}
-EOF
-    if $CC -I$INCLUDE -o $TMPDIR/strtest $TMPDIR/strtest.c >/dev/null 2>&1; then
-	echo "no"
-    else
-	if ${PKG_CONFIG} libbsd --exists; then
-		echo 'CFLAGS += -DHAVE_LIBBSD' `${PKG_CONFIG} libbsd --cflags` >>$CONFIG
-		echo 'LDLIBS +=' `${PKG_CONFIG} libbsd --libs` >> $CONFIG
-		echo "no"
-	else
-		echo 'CFLAGS += -DNEED_STRLCPY' >>$CONFIG
-		echo "yes"
-	fi
-    fi
-    rm -f $TMPDIR/strtest.c $TMPDIR/strtest
-}
-
-check_cap()
-{
-	if ${PKG_CONFIG} libcap --exists; then
-		echo "HAVE_CAP:=y" >>$CONFIG
-		echo "yes"
-
-		echo 'CFLAGS += -DHAVE_LIBCAP' `${PKG_CONFIG} libcap --cflags` >>$CONFIG
-		echo 'LDLIBS +=' `${PKG_CONFIG} libcap --libs` >> $CONFIG
-	else
-		echo "no"
-	fi
-}
-
-quiet_config()
-{
-	cat <<EOF
-# user can control verbosity similar to kernel builds (e.g., V=1)
-ifeq ("\$(origin V)", "command line")
-  VERBOSE = \$(V)
-endif
-ifndef VERBOSE
-  VERBOSE = 0
-endif
-ifeq (\$(VERBOSE),1)
-  Q =
-else
-  Q = @
-endif
-
-ifeq (\$(VERBOSE), 0)
-    QUIET_CC       = @echo '    CC       '\$@;
-    QUIET_AR       = @echo '    AR       '\$@;
-    QUIET_LINK     = @echo '    LINK     '\$@;
-    QUIET_YACC     = @echo '    YACC     '\$@;
-    QUIET_LEX      = @echo '    LEX      '\$@;
-endif
-EOF
-}
-
-echo "# Generated config based on" $INCLUDE >$CONFIG
-quiet_config >> $CONFIG
-
+echo "# Generated config based on" $INCLUDE >Config
 check_toolchain
 
 echo "TC schedulers"
@@ -359,23 +325,18 @@
 echo -n " ATM	"
 check_atm
 
-check_xtables
-if ! grep -q TC_CONFIG_NO_XT $CONFIG; then
-	echo -n " IPT	"
-	check_xt
-	check_xt_old
-	check_xt_old_internal_h
-	check_ipt
+echo -n " IPT	"
+check_xt
+check_xt_old
+check_xt_old_internal_h
+check_ipt
 
-	echo -n " IPSET  "
-	check_ipset
-fi
+echo -n " IPSET  "
+check_ipset
 
 echo
-if ! grep -q TC_CONFIG_NO_XT $CONFIG; then
-	echo -n "iptables modules directory: "
-	check_ipt_lib_dir
-fi
+echo -n "iptables modules directory: "
+check_ipt_lib_dir
 
 echo -n "libc has setns: "
 check_setns
@@ -392,12 +353,7 @@
 echo -n "Berkeley DB: "
 check_berkeley_db
 
-echo -n "need for strlcpy: "
-check_strlcpy
-
-echo -n "libcap support: "
-check_cap
-
-echo >> $CONFIG
-echo "%.o: %.c" >> $CONFIG
-echo '	$(QUIET_CC)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CPPFLAGS) -c -o $@ $<' >> $CONFIG
+echo
+echo -n "docs:"
+check_docs
+echo
diff --git a/devlink/.gitignore b/devlink/.gitignore
deleted file mode 100644
index 08d175f..0000000
--- a/devlink/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-devlink
diff --git a/devlink/Makefile b/devlink/Makefile
deleted file mode 100644
index 7da7d1f..0000000
--- a/devlink/Makefile
+++ /dev/null
@@ -1,24 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-include ../config.mk
-
-TARGETS :=
-
-ifeq ($(HAVE_MNL),y)
-
-DEVLINKOBJ = devlink.o mnlg.o
-TARGETS += devlink
-
-endif
-
-all: $(TARGETS) $(LIBS)
-
-devlink: $(DEVLINKOBJ)
-	$(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
-
-install: all
-	for i in $(TARGETS); \
-	do install -m 0755 $$i $(DESTDIR)$(SBINDIR); \
-	done
-
-clean:
-	rm -f $(DEVLINKOBJ) $(TARGETS)
diff --git a/devlink/devlink.c b/devlink/devlink.c
deleted file mode 100644
index 056ac95..0000000
--- a/devlink/devlink.c
+++ /dev/null
@@ -1,7257 +0,0 @@
-/*
- * devlink.c	Devlink tool
- *
- *              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:     Jiri Pirko <jiri@mellanox.com>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdbool.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <limits.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <sys/sysinfo.h>
-#define _LINUX_SYSINFO_H /* avoid collision with musl header */
-#include <linux/genetlink.h>
-#include <linux/devlink.h>
-#include <libmnl/libmnl.h>
-#include <netinet/ether.h>
-#include <sys/types.h>
-
-#include "SNAPSHOT.h"
-#include "list.h"
-#include "mnlg.h"
-#include "json_writer.h"
-#include "utils.h"
-
-#define ESWITCH_MODE_LEGACY "legacy"
-#define ESWITCH_MODE_SWITCHDEV "switchdev"
-#define ESWITCH_INLINE_MODE_NONE "none"
-#define ESWITCH_INLINE_MODE_LINK "link"
-#define ESWITCH_INLINE_MODE_NETWORK "network"
-#define ESWITCH_INLINE_MODE_TRANSPORT "transport"
-
-#define PARAM_CMODE_RUNTIME_STR "runtime"
-#define PARAM_CMODE_DRIVERINIT_STR "driverinit"
-#define PARAM_CMODE_PERMANENT_STR "permanent"
-#define DL_ARGS_REQUIRED_MAX_ERR_LEN 80
-
-#define HEALTH_REPORTER_STATE_HEALTHY_STR "healthy"
-#define HEALTH_REPORTER_STATE_ERROR_STR "error"
-#define HEALTH_REPORTER_TIMESTAMP_FMT_LEN 80
-
-static int g_new_line_count;
-static int g_indent_level;
-static bool g_indent_newline;
-
-#define INDENT_STR_STEP 2
-#define INDENT_STR_MAXLEN 32
-static char g_indent_str[INDENT_STR_MAXLEN + 1] = "";
-
-static void __attribute__((format(printf, 1, 2)))
-pr_err(const char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	vfprintf(stderr, fmt, ap);
-	va_end(ap);
-}
-
-static void __attribute__((format(printf, 1, 2)))
-pr_out(const char *fmt, ...)
-{
-	va_list ap;
-
-	if (g_indent_newline) {
-		printf("%s", g_indent_str);
-		g_indent_newline = false;
-	}
-	va_start(ap, fmt);
-	vprintf(fmt, ap);
-	va_end(ap);
-	g_new_line_count = 0;
-}
-
-static void __attribute__((format(printf, 2, 3)))
-pr_out_sp(unsigned int num, const char *fmt, ...)
-{
-	va_list ap;
-	int ret;
-
-	va_start(ap, fmt);
-	ret = vprintf(fmt, ap);
-	va_end(ap);
-
-	if (ret < num)
-		printf("%*s", num - ret, "");
-	g_new_line_count = 0;			\
-}
-
-static void __attribute__((format(printf, 1, 2)))
-pr_out_tty(const char *fmt, ...)
-{
-	va_list ap;
-
-	if (!isatty(STDOUT_FILENO))
-		return;
-	va_start(ap, fmt);
-	vprintf(fmt, ap);
-	va_end(ap);
-}
-
-static void __pr_out_indent_inc(void)
-{
-	if (g_indent_level + INDENT_STR_STEP > INDENT_STR_MAXLEN)
-		return;
-	g_indent_level += INDENT_STR_STEP;
-	memset(g_indent_str, ' ', sizeof(g_indent_str));
-	g_indent_str[g_indent_level] = '\0';
-}
-
-static void __pr_out_indent_dec(void)
-{
-	if (g_indent_level - INDENT_STR_STEP < 0)
-		return;
-	g_indent_level -= INDENT_STR_STEP;
-	g_indent_str[g_indent_level] = '\0';
-}
-
-static void __pr_out_newline(void)
-{
-	if (g_new_line_count < 1) {
-		pr_out("\n");
-		g_indent_newline = true;
-	}
-	g_new_line_count++;
-}
-
-static int _mnlg_socket_recv_run(struct mnlg_socket *nlg,
-				 mnl_cb_t data_cb, void *data)
-{
-	int err;
-
-	err = mnlg_socket_recv_run(nlg, data_cb, data);
-	if (err < 0) {
-		pr_err("devlink answers: %s\n", strerror(errno));
-		return -errno;
-	}
-	return 0;
-}
-
-static int _mnlg_socket_send(struct mnlg_socket *nlg,
-			     const struct nlmsghdr *nlh)
-{
-	int err;
-
-	err = mnlg_socket_send(nlg, nlh);
-	if (err < 0) {
-		pr_err("Failed to call mnlg_socket_send\n");
-		return -errno;
-	}
-	return 0;
-}
-
-static int _mnlg_socket_sndrcv(struct mnlg_socket *nlg,
-			       const struct nlmsghdr *nlh,
-			       mnl_cb_t data_cb, void *data)
-{
-	int err;
-
-	err = _mnlg_socket_send(nlg, nlh);
-	if (err)
-		return err;
-	return _mnlg_socket_recv_run(nlg, data_cb, data);
-}
-
-static int _mnlg_socket_group_add(struct mnlg_socket *nlg,
-				  const char *group_name)
-{
-	int err;
-
-	err = mnlg_socket_group_add(nlg, group_name);
-	if (err < 0) {
-		pr_err("Failed to call mnlg_socket_group_add\n");
-		return -errno;
-	}
-	return 0;
-}
-
-struct ifname_map {
-	struct list_head list;
-	char *bus_name;
-	char *dev_name;
-	uint32_t port_index;
-	char *ifname;
-};
-
-static struct ifname_map *ifname_map_alloc(const char *bus_name,
-					   const char *dev_name,
-					   uint32_t port_index,
-					   const char *ifname)
-{
-	struct ifname_map *ifname_map;
-
-	ifname_map = calloc(1, sizeof(*ifname_map));
-	if (!ifname_map)
-		return NULL;
-	ifname_map->bus_name = strdup(bus_name);
-	ifname_map->dev_name = strdup(dev_name);
-	ifname_map->port_index = port_index;
-	ifname_map->ifname = strdup(ifname);
-	if (!ifname_map->bus_name || !ifname_map->dev_name ||
-	    !ifname_map->ifname) {
-		free(ifname_map->ifname);
-		free(ifname_map->dev_name);
-		free(ifname_map->bus_name);
-		free(ifname_map);
-		return NULL;
-	}
-	return ifname_map;
-}
-
-static void ifname_map_free(struct ifname_map *ifname_map)
-{
-	free(ifname_map->ifname);
-	free(ifname_map->dev_name);
-	free(ifname_map->bus_name);
-	free(ifname_map);
-}
-
-#define DL_OPT_HANDLE		BIT(0)
-#define DL_OPT_HANDLEP		BIT(1)
-#define DL_OPT_PORT_TYPE	BIT(2)
-#define DL_OPT_PORT_COUNT	BIT(3)
-#define DL_OPT_SB		BIT(4)
-#define DL_OPT_SB_POOL		BIT(5)
-#define DL_OPT_SB_SIZE		BIT(6)
-#define DL_OPT_SB_TYPE		BIT(7)
-#define DL_OPT_SB_THTYPE	BIT(8)
-#define DL_OPT_SB_TH		BIT(9)
-#define DL_OPT_SB_TC		BIT(10)
-#define DL_OPT_ESWITCH_MODE	BIT(11)
-#define DL_OPT_ESWITCH_INLINE_MODE	BIT(12)
-#define DL_OPT_DPIPE_TABLE_NAME	BIT(13)
-#define DL_OPT_DPIPE_TABLE_COUNTERS	BIT(14)
-#define DL_OPT_ESWITCH_ENCAP_MODE	BIT(15)
-#define DL_OPT_RESOURCE_PATH	BIT(16)
-#define DL_OPT_RESOURCE_SIZE	BIT(17)
-#define DL_OPT_PARAM_NAME	BIT(18)
-#define DL_OPT_PARAM_VALUE	BIT(19)
-#define DL_OPT_PARAM_CMODE	BIT(20)
-#define DL_OPT_HANDLE_REGION		BIT(21)
-#define DL_OPT_REGION_SNAPSHOT_ID	BIT(22)
-#define DL_OPT_REGION_ADDRESS		BIT(23)
-#define DL_OPT_REGION_LENGTH		BIT(24)
-#define DL_OPT_FLASH_FILE_NAME	BIT(25)
-#define DL_OPT_FLASH_COMPONENT	BIT(26)
-#define DL_OPT_HEALTH_REPORTER_NAME	BIT(27)
-#define DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD	BIT(28)
-#define DL_OPT_HEALTH_REPORTER_AUTO_RECOVER	BIT(29)
-#define DL_OPT_TRAP_NAME		BIT(30)
-#define DL_OPT_TRAP_ACTION		BIT(31)
-#define DL_OPT_TRAP_GROUP_NAME		BIT(32)
-
-struct dl_opts {
-	uint64_t present; /* flags of present items */
-	char *bus_name;
-	char *dev_name;
-	uint32_t port_index;
-	enum devlink_port_type port_type;
-	uint32_t port_count;
-	uint32_t sb_index;
-	uint16_t sb_pool_index;
-	uint32_t sb_pool_size;
-	enum devlink_sb_pool_type sb_pool_type;
-	enum devlink_sb_threshold_type sb_pool_thtype;
-	uint32_t sb_threshold;
-	uint16_t sb_tc_index;
-	enum devlink_eswitch_mode eswitch_mode;
-	enum devlink_eswitch_inline_mode eswitch_inline_mode;
-	const char *dpipe_table_name;
-	bool dpipe_counters_enable;
-	bool eswitch_encap_mode;
-	const char *resource_path;
-	uint32_t resource_size;
-	uint32_t resource_id;
-	bool resource_id_valid;
-	const char *param_name;
-	const char *param_value;
-	enum devlink_param_cmode cmode;
-	char *region_name;
-	uint32_t region_snapshot_id;
-	uint64_t region_address;
-	uint64_t region_length;
-	const char *flash_file_name;
-	const char *flash_component;
-	const char *reporter_name;
-	uint64_t reporter_graceful_period;
-	bool reporter_auto_recover;
-	const char *trap_name;
-	const char *trap_group_name;
-	enum devlink_trap_action trap_action;
-};
-
-struct dl {
-	struct mnlg_socket *nlg;
-	struct list_head ifname_map_list;
-	int argc;
-	char **argv;
-	bool no_nice_names;
-	struct dl_opts opts;
-	json_writer_t *jw;
-	bool json_output;
-	bool pretty_output;
-	bool verbose;
-	bool stats;
-	struct {
-		bool present;
-		char *bus_name;
-		char *dev_name;
-		uint32_t port_index;
-	} arr_last;
-};
-
-static int dl_argc(struct dl *dl)
-{
-	return dl->argc;
-}
-
-static char *dl_argv(struct dl *dl)
-{
-	if (dl_argc(dl) == 0)
-		return NULL;
-	return *dl->argv;
-}
-
-static void dl_arg_inc(struct dl *dl)
-{
-	if (dl_argc(dl) == 0)
-		return;
-	dl->argc--;
-	dl->argv++;
-}
-
-static char *dl_argv_next(struct dl *dl)
-{
-	char *ret;
-
-	if (dl_argc(dl) == 0)
-		return NULL;
-
-	ret = *dl->argv;
-	dl_arg_inc(dl);
-	return ret;
-}
-
-static char *dl_argv_index(struct dl *dl, unsigned int index)
-{
-	if (index >= dl_argc(dl))
-		return NULL;
-	return dl->argv[index];
-}
-
-static int strcmpx(const char *str1, const char *str2)
-{
-	if (strlen(str1) > strlen(str2))
-		return -1;
-	return strncmp(str1, str2, strlen(str1));
-}
-
-static bool dl_argv_match(struct dl *dl, const char *pattern)
-{
-	if (dl_argc(dl) == 0)
-		return false;
-	return strcmpx(dl_argv(dl), pattern) == 0;
-}
-
-static bool dl_no_arg(struct dl *dl)
-{
-	return dl_argc(dl) == 0;
-}
-
-static void __pr_out_indent_newline(struct dl *dl)
-{
-	if (!g_indent_newline && !dl->json_output)
-		pr_out(" ");
-}
-
-static const enum mnl_attr_data_type devlink_policy[DEVLINK_ATTR_MAX + 1] = {
-	[DEVLINK_ATTR_BUS_NAME] = MNL_TYPE_NUL_STRING,
-	[DEVLINK_ATTR_DEV_NAME] = MNL_TYPE_NUL_STRING,
-	[DEVLINK_ATTR_PORT_INDEX] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_PORT_TYPE] = MNL_TYPE_U16,
-	[DEVLINK_ATTR_PORT_DESIRED_TYPE] = MNL_TYPE_U16,
-	[DEVLINK_ATTR_PORT_NETDEV_IFINDEX] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_PORT_NETDEV_NAME] = MNL_TYPE_NUL_STRING,
-	[DEVLINK_ATTR_PORT_IBDEV_NAME] = MNL_TYPE_NUL_STRING,
-	[DEVLINK_ATTR_SB_INDEX] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_SB_SIZE] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT] = MNL_TYPE_U16,
-	[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT] = MNL_TYPE_U16,
-	[DEVLINK_ATTR_SB_INGRESS_TC_COUNT] = MNL_TYPE_U16,
-	[DEVLINK_ATTR_SB_EGRESS_TC_COUNT] = MNL_TYPE_U16,
-	[DEVLINK_ATTR_SB_POOL_INDEX] = MNL_TYPE_U16,
-	[DEVLINK_ATTR_SB_POOL_TYPE] = MNL_TYPE_U8,
-	[DEVLINK_ATTR_SB_POOL_SIZE] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE] = MNL_TYPE_U8,
-	[DEVLINK_ATTR_SB_THRESHOLD] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_SB_TC_INDEX] = MNL_TYPE_U16,
-	[DEVLINK_ATTR_SB_OCC_CUR] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_SB_OCC_MAX] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_ESWITCH_MODE] = MNL_TYPE_U16,
-	[DEVLINK_ATTR_ESWITCH_INLINE_MODE] = MNL_TYPE_U8,
-	[DEVLINK_ATTR_ESWITCH_ENCAP_MODE] = MNL_TYPE_U8,
-	[DEVLINK_ATTR_DPIPE_TABLES] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_TABLE] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_TABLE_NAME] = MNL_TYPE_STRING,
-	[DEVLINK_ATTR_DPIPE_TABLE_SIZE] = MNL_TYPE_U64,
-	[DEVLINK_ATTR_DPIPE_TABLE_MATCHES] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_TABLE_ACTIONS] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] =  MNL_TYPE_U8,
-	[DEVLINK_ATTR_DPIPE_ENTRIES] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_ENTRY] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_ENTRY_INDEX] = MNL_TYPE_U64,
-	[DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER] = MNL_TYPE_U64,
-	[DEVLINK_ATTR_DPIPE_MATCH] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_MATCH_VALUE] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_MATCH_TYPE] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_DPIPE_ACTION] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_ACTION_VALUE] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_ACTION_TYPE] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_DPIPE_VALUE_MAPPING] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_DPIPE_HEADERS] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_HEADER] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_HEADER_NAME] = MNL_TYPE_STRING,
-	[DEVLINK_ATTR_DPIPE_HEADER_ID] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_DPIPE_HEADER_FIELDS] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL] = MNL_TYPE_U8,
-	[DEVLINK_ATTR_DPIPE_HEADER_INDEX] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_DPIPE_FIELD] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_DPIPE_FIELD_NAME] = MNL_TYPE_STRING,
-	[DEVLINK_ATTR_DPIPE_FIELD_ID] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_PARAM] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_PARAM_NAME] = MNL_TYPE_STRING,
-	[DEVLINK_ATTR_PARAM_TYPE] = MNL_TYPE_U8,
-	[DEVLINK_ATTR_PARAM_VALUES_LIST] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_PARAM_VALUE] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_PARAM_VALUE_CMODE] = MNL_TYPE_U8,
-	[DEVLINK_ATTR_REGION_NAME] = MNL_TYPE_STRING,
-	[DEVLINK_ATTR_REGION_SIZE] = MNL_TYPE_U64,
-	[DEVLINK_ATTR_REGION_SNAPSHOTS] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_REGION_SNAPSHOT] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_REGION_SNAPSHOT_ID] = MNL_TYPE_U32,
-	[DEVLINK_ATTR_REGION_CHUNKS] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_REGION_CHUNK] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_REGION_CHUNK_DATA] = MNL_TYPE_BINARY,
-	[DEVLINK_ATTR_REGION_CHUNK_ADDR] = MNL_TYPE_U64,
-	[DEVLINK_ATTR_REGION_CHUNK_LEN] = MNL_TYPE_U64,
-	[DEVLINK_ATTR_INFO_DRIVER_NAME] = MNL_TYPE_STRING,
-	[DEVLINK_ATTR_INFO_SERIAL_NUMBER] = MNL_TYPE_STRING,
-	[DEVLINK_ATTR_INFO_VERSION_FIXED] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_INFO_VERSION_RUNNING] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_INFO_VERSION_STORED] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_INFO_VERSION_NAME] = MNL_TYPE_STRING,
-	[DEVLINK_ATTR_INFO_VERSION_VALUE] = MNL_TYPE_STRING,
-	[DEVLINK_ATTR_HEALTH_REPORTER] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_HEALTH_REPORTER_NAME] = MNL_TYPE_STRING,
-	[DEVLINK_ATTR_HEALTH_REPORTER_STATE] = MNL_TYPE_U8,
-	[DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT] = MNL_TYPE_U64,
-	[DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT] = MNL_TYPE_U64,
-	[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS] = MNL_TYPE_U64,
-	[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] = MNL_TYPE_U64,
-	[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT] = MNL_TYPE_STRING,
-	[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG] = MNL_TYPE_STRING,
-	[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE] = MNL_TYPE_U64,
-	[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL] = MNL_TYPE_U64,
-	[DEVLINK_ATTR_STATS] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_TRAP_NAME] = MNL_TYPE_STRING,
-	[DEVLINK_ATTR_TRAP_ACTION] = MNL_TYPE_U8,
-	[DEVLINK_ATTR_TRAP_TYPE] = MNL_TYPE_U8,
-	[DEVLINK_ATTR_TRAP_GENERIC] = MNL_TYPE_FLAG,
-	[DEVLINK_ATTR_TRAP_METADATA] = MNL_TYPE_NESTED,
-	[DEVLINK_ATTR_TRAP_GROUP_NAME] = MNL_TYPE_STRING,
-	[DEVLINK_ATTR_RELOAD_FAILED] = MNL_TYPE_U8,
-};
-
-static const enum mnl_attr_data_type
-devlink_stats_policy[DEVLINK_ATTR_STATS_MAX + 1] = {
-	[DEVLINK_ATTR_STATS_RX_PACKETS] = MNL_TYPE_U64,
-	[DEVLINK_ATTR_STATS_RX_BYTES] = MNL_TYPE_U64,
-};
-
-static int attr_cb(const struct nlattr *attr, void *data)
-{
-	const struct nlattr **tb = data;
-	int type;
-
-	if (mnl_attr_type_valid(attr, DEVLINK_ATTR_MAX) < 0)
-		return MNL_CB_OK;
-
-	type = mnl_attr_get_type(attr);
-	if (mnl_attr_validate(attr, devlink_policy[type]) < 0)
-		return MNL_CB_ERROR;
-
-	tb[type] = attr;
-	return MNL_CB_OK;
-}
-
-static int attr_stats_cb(const struct nlattr *attr, void *data)
-{
-	const struct nlattr **tb = data;
-	int type;
-
-	/* Allow the tool to work on top of newer kernels that might contain
-	 * more attributes.
-	 */
-	if (mnl_attr_type_valid(attr, DEVLINK_ATTR_STATS_MAX) < 0)
-		return MNL_CB_OK;
-
-	type = mnl_attr_get_type(attr);
-	if (mnl_attr_validate(attr, devlink_stats_policy[type]) < 0)
-		return MNL_CB_ERROR;
-
-	tb[type] = attr;
-	return MNL_CB_OK;
-}
-
-static int ifname_map_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	struct dl *dl = data;
-	struct ifname_map *ifname_map;
-	const char *bus_name;
-	const char *dev_name;
-	uint32_t port_ifindex;
-	const char *port_ifname;
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_PORT_INDEX])
-		return MNL_CB_ERROR;
-
-	if (!tb[DEVLINK_ATTR_PORT_NETDEV_NAME])
-		return MNL_CB_OK;
-
-	bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
-	dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
-	port_ifindex = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]);
-	port_ifname = mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_NETDEV_NAME]);
-	ifname_map = ifname_map_alloc(bus_name, dev_name,
-				      port_ifindex, port_ifname);
-	if (!ifname_map)
-		return MNL_CB_ERROR;
-	list_add(&ifname_map->list, &dl->ifname_map_list);
-
-	return MNL_CB_OK;
-}
-
-static void ifname_map_fini(struct dl *dl)
-{
-	struct ifname_map *ifname_map, *tmp;
-
-	list_for_each_entry_safe(ifname_map, tmp,
-				 &dl->ifname_map_list, list) {
-		list_del(&ifname_map->list);
-		ifname_map_free(ifname_map);
-	}
-}
-
-static int ifname_map_init(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	INIT_LIST_HEAD(&dl->ifname_map_list);
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_GET,
-			       NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
-
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, ifname_map_cb, dl);
-	if (err) {
-		ifname_map_fini(dl);
-		return err;
-	}
-	return 0;
-}
-
-static int ifname_map_lookup(struct dl *dl, const char *ifname,
-			     char **p_bus_name, char **p_dev_name,
-			     uint32_t *p_port_index)
-{
-	struct ifname_map *ifname_map;
-
-	list_for_each_entry(ifname_map, &dl->ifname_map_list, list) {
-		if (strcmp(ifname, ifname_map->ifname) == 0) {
-			*p_bus_name = ifname_map->bus_name;
-			*p_dev_name = ifname_map->dev_name;
-			*p_port_index = ifname_map->port_index;
-			return 0;
-		}
-	}
-	return -ENOENT;
-}
-
-static int ifname_map_rev_lookup(struct dl *dl, const char *bus_name,
-				 const char *dev_name, uint32_t port_index,
-				 char **p_ifname)
-{
-	struct ifname_map *ifname_map;
-
-	list_for_each_entry(ifname_map, &dl->ifname_map_list, list) {
-		if (strcmp(bus_name, ifname_map->bus_name) == 0 &&
-		    strcmp(dev_name, ifname_map->dev_name) == 0 &&
-		    port_index == ifname_map->port_index) {
-			*p_ifname = ifname_map->ifname;
-			return 0;
-		}
-	}
-	return -ENOENT;
-}
-
-static unsigned int strslashcount(char *str)
-{
-	unsigned int count = 0;
-	char *pos = str;
-
-	while ((pos = strchr(pos, '/'))) {
-		count++;
-		pos++;
-	}
-	return count;
-}
-
-static int strslashrsplit(char *str, char **before, char **after)
-{
-	char *slash;
-
-	slash = strrchr(str, '/');
-	if (!slash)
-		return -EINVAL;
-	*slash = '\0';
-	*before = str;
-	*after = slash + 1;
-	return 0;
-}
-
-static int strtouint64_t(const char *str, uint64_t *p_val)
-{
-	char *endptr;
-	unsigned long long int val;
-
-	val = strtoull(str, &endptr, 10);
-	if (endptr == str || *endptr != '\0')
-		return -EINVAL;
-	if (val > ULONG_MAX)
-		return -ERANGE;
-	*p_val = val;
-	return 0;
-}
-
-static int strtouint32_t(const char *str, uint32_t *p_val)
-{
-	char *endptr;
-	unsigned long int val;
-
-	val = strtoul(str, &endptr, 10);
-	if (endptr == str || *endptr != '\0')
-		return -EINVAL;
-	if (val > UINT_MAX)
-		return -ERANGE;
-	*p_val = val;
-	return 0;
-}
-
-static int strtouint16_t(const char *str, uint16_t *p_val)
-{
-	char *endptr;
-	unsigned long int val;
-
-	val = strtoul(str, &endptr, 10);
-	if (endptr == str || *endptr != '\0')
-		return -EINVAL;
-	if (val > USHRT_MAX)
-		return -ERANGE;
-	*p_val = val;
-	return 0;
-}
-
-static int strtouint8_t(const char *str, uint8_t *p_val)
-{
-	char *endptr;
-	unsigned long int val;
-
-	val = strtoul(str, &endptr, 10);
-	if (endptr == str || *endptr != '\0')
-		return -EINVAL;
-	if (val > UCHAR_MAX)
-		return -ERANGE;
-	*p_val = val;
-	return 0;
-}
-
-static int strtobool(const char *str, bool *p_val)
-{
-	bool val;
-
-	if (!strcmp(str, "true") || !strcmp(str, "1"))
-		val = true;
-	else if (!strcmp(str, "false") || !strcmp(str, "0"))
-		val = false;
-	else
-		return -EINVAL;
-	*p_val = val;
-	return 0;
-}
-
-static int __dl_argv_handle(char *str, char **p_bus_name, char **p_dev_name)
-{
-	strslashrsplit(str, p_bus_name, p_dev_name);
-	return 0;
-}
-
-static int dl_argv_handle(struct dl *dl, char **p_bus_name, char **p_dev_name)
-{
-	char *str = dl_argv_next(dl);
-
-	if (!str) {
-		pr_err("Devlink identification (\"bus_name/dev_name\") expected\n");
-		return -EINVAL;
-	}
-	if (strslashcount(str) != 1) {
-		pr_err("Wrong devlink identification string format.\n");
-		pr_err("Expected \"bus_name/dev_name\".\n");
-		return -EINVAL;
-	}
-	return __dl_argv_handle(str, p_bus_name, p_dev_name);
-}
-
-static int __dl_argv_handle_port(char *str,
-				 char **p_bus_name, char **p_dev_name,
-				 uint32_t *p_port_index)
-{
-	char *handlestr;
-	char *portstr;
-	int err;
-
-	err = strslashrsplit(str, &handlestr, &portstr);
-	if (err) {
-		pr_err("Port identification \"%s\" is invalid\n", str);
-		return err;
-	}
-	err = strtouint32_t(portstr, p_port_index);
-	if (err) {
-		pr_err("Port index \"%s\" is not a number or not within range\n",
-		       portstr);
-		return err;
-	}
-	err = strslashrsplit(handlestr, p_bus_name, p_dev_name);
-	if (err) {
-		pr_err("Port identification \"%s\" is invalid\n", str);
-		return err;
-	}
-	return 0;
-}
-
-static int __dl_argv_handle_port_ifname(struct dl *dl, char *str,
-					char **p_bus_name, char **p_dev_name,
-					uint32_t *p_port_index)
-{
-	int err;
-
-	err = ifname_map_lookup(dl, str, p_bus_name, p_dev_name,
-				p_port_index);
-	if (err) {
-		pr_err("Netdevice \"%s\" not found\n", str);
-		return err;
-	}
-	return 0;
-}
-
-static int dl_argv_handle_port(struct dl *dl, char **p_bus_name,
-			       char **p_dev_name, uint32_t *p_port_index)
-{
-	char *str = dl_argv_next(dl);
-	unsigned int slash_count;
-
-	if (!str) {
-		pr_err("Port identification (\"bus_name/dev_name/port_index\" or \"netdev ifname\") expected.\n");
-		return -EINVAL;
-	}
-	slash_count = strslashcount(str);
-	switch (slash_count) {
-	case 0:
-		return __dl_argv_handle_port_ifname(dl, str, p_bus_name,
-						    p_dev_name, p_port_index);
-	case 2:
-		return __dl_argv_handle_port(str, p_bus_name,
-					     p_dev_name, p_port_index);
-	default:
-		pr_err("Wrong port identification string format.\n");
-		pr_err("Expected \"bus_name/dev_name/port_index\" or \"netdev_ifname\".\n");
-		return -EINVAL;
-	}
-}
-
-static int dl_argv_handle_both(struct dl *dl, char **p_bus_name,
-			       char **p_dev_name, uint32_t *p_port_index,
-			       uint64_t *p_handle_bit)
-{
-	char *str = dl_argv_next(dl);
-	unsigned int slash_count;
-	int err;
-
-	if (!str) {
-		pr_err("One of following identifications expected:\n"
-		       "Devlink identification (\"bus_name/dev_name\")\n"
-		       "Port identification (\"bus_name/dev_name/port_index\" or \"netdev ifname\")\n");
-		return -EINVAL;
-	}
-	slash_count = strslashcount(str);
-	if (slash_count == 1) {
-		err = __dl_argv_handle(str, p_bus_name, p_dev_name);
-		if (err)
-			return err;
-		*p_handle_bit = DL_OPT_HANDLE;
-	} else if (slash_count == 2) {
-		err = __dl_argv_handle_port(str, p_bus_name,
-					    p_dev_name, p_port_index);
-		if (err)
-			return err;
-		*p_handle_bit = DL_OPT_HANDLEP;
-	} else if (slash_count == 0) {
-		err = __dl_argv_handle_port_ifname(dl, str, p_bus_name,
-						   p_dev_name, p_port_index);
-		if (err)
-			return err;
-		*p_handle_bit = DL_OPT_HANDLEP;
-	} else {
-		pr_err("Wrong port identification string format.\n");
-		pr_err("Expected \"bus_name/dev_name\" or \"bus_name/dev_name/port_index\" or \"netdev_ifname\".\n");
-		return -EINVAL;
-	}
-	return 0;
-}
-
-static int __dl_argv_handle_region(char *str, char **p_bus_name,
-				   char **p_dev_name, char **p_region)
-{
-	char *handlestr;
-	int err;
-
-	err = strslashrsplit(str, &handlestr, p_region);
-	if (err) {
-		pr_err("Region identification \"%s\" is invalid\n", str);
-		return err;
-	}
-	err = strslashrsplit(handlestr, p_bus_name, p_dev_name);
-	if (err) {
-		pr_err("Region identification \"%s\" is invalid\n", str);
-		return err;
-	}
-	return 0;
-}
-
-static int dl_argv_handle_region(struct dl *dl, char **p_bus_name,
-					char **p_dev_name, char **p_region)
-{
-	char *str = dl_argv_next(dl);
-	unsigned int slash_count;
-
-	if (!str) {
-		pr_err("Expected \"bus_name/dev_name/region\" identification.\n");
-		return -EINVAL;
-	}
-
-	slash_count = strslashcount(str);
-	if (slash_count != 2) {
-		pr_err("Wrong region identification string format.\n");
-		pr_err("Expected \"bus_name/dev_name/region\" identification.\n"".\n");
-		return -EINVAL;
-	}
-
-	return __dl_argv_handle_region(str, p_bus_name, p_dev_name, p_region);
-}
-
-static int dl_argv_uint64_t(struct dl *dl, uint64_t *p_val)
-{
-	char *str = dl_argv_next(dl);
-	int err;
-
-	if (!str) {
-		pr_err("Unsigned number argument expected\n");
-		return -EINVAL;
-	}
-
-	err = strtouint64_t(str, p_val);
-	if (err) {
-		pr_err("\"%s\" is not a number or not within range\n", str);
-		return err;
-	}
-	return 0;
-}
-
-static int dl_argv_uint32_t(struct dl *dl, uint32_t *p_val)
-{
-	char *str = dl_argv_next(dl);
-	int err;
-
-	if (!str) {
-		pr_err("Unsigned number argument expected\n");
-		return -EINVAL;
-	}
-
-	err = strtouint32_t(str, p_val);
-	if (err) {
-		pr_err("\"%s\" is not a number or not within range\n", str);
-		return err;
-	}
-	return 0;
-}
-
-static int dl_argv_uint16_t(struct dl *dl, uint16_t *p_val)
-{
-	char *str = dl_argv_next(dl);
-	int err;
-
-	if (!str) {
-		pr_err("Unsigned number argument expected\n");
-		return -EINVAL;
-	}
-
-	err = strtouint16_t(str, p_val);
-	if (err) {
-		pr_err("\"%s\" is not a number or not within range\n", str);
-		return err;
-	}
-	return 0;
-}
-
-static int dl_argv_bool(struct dl *dl, bool *p_val)
-{
-	char *str = dl_argv_next(dl);
-	int err;
-
-	if (!str) {
-		pr_err("Boolean argument expected\n");
-		return -EINVAL;
-	}
-
-	err = strtobool(str, p_val);
-	if (err) {
-		pr_err("\"%s\" is not a valid boolean value\n", str);
-		return err;
-	}
-	return 0;
-}
-
-static int dl_argv_str(struct dl *dl, const char **p_str)
-{
-	const char *str = dl_argv_next(dl);
-
-	if (!str) {
-		pr_err("String parameter expected\n");
-		return -EINVAL;
-	}
-	*p_str = str;
-	return 0;
-}
-
-static int port_type_get(const char *typestr, enum devlink_port_type *p_type)
-{
-	if (strcmp(typestr, "auto") == 0) {
-		*p_type = DEVLINK_PORT_TYPE_AUTO;
-	} else if (strcmp(typestr, "eth") == 0) {
-		*p_type = DEVLINK_PORT_TYPE_ETH;
-	} else if (strcmp(typestr, "ib") == 0) {
-		*p_type = DEVLINK_PORT_TYPE_IB;
-	} else {
-		pr_err("Unknown port type \"%s\"\n", typestr);
-		return -EINVAL;
-	}
-	return 0;
-}
-
-static int pool_type_get(const char *typestr, enum devlink_sb_pool_type *p_type)
-{
-	if (strcmp(typestr, "ingress") == 0) {
-		*p_type = DEVLINK_SB_POOL_TYPE_INGRESS;
-	} else if (strcmp(typestr, "egress") == 0) {
-		*p_type = DEVLINK_SB_POOL_TYPE_EGRESS;
-	} else {
-		pr_err("Unknown pool type \"%s\"\n", typestr);
-		return -EINVAL;
-	}
-	return 0;
-}
-
-static int threshold_type_get(const char *typestr,
-			      enum devlink_sb_threshold_type *p_type)
-{
-	if (strcmp(typestr, "static") == 0) {
-		*p_type = DEVLINK_SB_THRESHOLD_TYPE_STATIC;
-	} else if (strcmp(typestr, "dynamic") == 0) {
-		*p_type = DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC;
-	} else {
-		pr_err("Unknown threshold type \"%s\"\n", typestr);
-		return -EINVAL;
-	}
-	return 0;
-}
-
-static int eswitch_mode_get(const char *typestr,
-			    enum devlink_eswitch_mode *p_mode)
-{
-	if (strcmp(typestr, ESWITCH_MODE_LEGACY) == 0) {
-		*p_mode = DEVLINK_ESWITCH_MODE_LEGACY;
-	} else if (strcmp(typestr, ESWITCH_MODE_SWITCHDEV) == 0) {
-		*p_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
-	} else {
-		pr_err("Unknown eswitch mode \"%s\"\n", typestr);
-		return -EINVAL;
-	}
-	return 0;
-}
-
-static int eswitch_inline_mode_get(const char *typestr,
-				   enum devlink_eswitch_inline_mode *p_mode)
-{
-	if (strcmp(typestr, ESWITCH_INLINE_MODE_NONE) == 0) {
-		*p_mode = DEVLINK_ESWITCH_INLINE_MODE_NONE;
-	} else if (strcmp(typestr, ESWITCH_INLINE_MODE_LINK) == 0) {
-		*p_mode = DEVLINK_ESWITCH_INLINE_MODE_LINK;
-	} else if (strcmp(typestr, ESWITCH_INLINE_MODE_NETWORK) == 0) {
-		*p_mode = DEVLINK_ESWITCH_INLINE_MODE_NETWORK;
-	} else if (strcmp(typestr, ESWITCH_INLINE_MODE_TRANSPORT) == 0) {
-		*p_mode = DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT;
-	} else {
-		pr_err("Unknown eswitch inline mode \"%s\"\n", typestr);
-		return -EINVAL;
-	}
-	return 0;
-}
-
-static int dpipe_counters_enable_get(const char *typestr,
-				     bool *counters_enable)
-{
-	if (strcmp(typestr, "enable") == 0) {
-		*counters_enable = 1;
-	} else if (strcmp(typestr, "disable") == 0) {
-		*counters_enable = 0;
-	} else {
-		pr_err("Unknown counter_state \"%s\"\n", typestr);
-		return -EINVAL;
-	}
-	return 0;
-}
-
-static int eswitch_encap_mode_get(const char *typestr, bool *p_mode)
-{
-	if (strcmp(typestr, "enable") == 0) {
-		*p_mode = true;
-	} else if (strcmp(typestr, "disable") == 0) {
-		*p_mode = false;
-	} else {
-		pr_err("Unknown eswitch encap mode \"%s\"\n", typestr);
-		return -EINVAL;
-	}
-	return 0;
-}
-
-static int param_cmode_get(const char *cmodestr,
-			   enum devlink_param_cmode *cmode)
-{
-	if (strcmp(cmodestr, PARAM_CMODE_RUNTIME_STR) == 0) {
-		*cmode = DEVLINK_PARAM_CMODE_RUNTIME;
-	} else if (strcmp(cmodestr, PARAM_CMODE_DRIVERINIT_STR) == 0) {
-		*cmode = DEVLINK_PARAM_CMODE_DRIVERINIT;
-	} else if (strcmp(cmodestr, PARAM_CMODE_PERMANENT_STR) == 0) {
-		*cmode = DEVLINK_PARAM_CMODE_PERMANENT;
-	} else {
-		pr_err("Unknown configuration mode \"%s\"\n", cmodestr);
-		return -EINVAL;
-	}
-	return 0;
-}
-
-static int trap_action_get(const char *actionstr,
-			   enum devlink_trap_action *p_action)
-{
-	if (strcmp(actionstr, "drop") == 0) {
-		*p_action = DEVLINK_TRAP_ACTION_DROP;
-	} else if (strcmp(actionstr, "trap") == 0) {
-		*p_action = DEVLINK_TRAP_ACTION_TRAP;
-	} else {
-		pr_err("Unknown trap action \"%s\"\n", actionstr);
-		return -EINVAL;
-	}
-	return 0;
-}
-
-struct dl_args_metadata {
-	uint64_t o_flag;
-	char err_msg[DL_ARGS_REQUIRED_MAX_ERR_LEN];
-};
-
-static const struct dl_args_metadata dl_args_required[] = {
-	{DL_OPT_PORT_TYPE,	      "Port type not set."},
-	{DL_OPT_PORT_COUNT,	      "Port split count option expected."},
-	{DL_OPT_SB_POOL,	      "Pool index option expected."},
-	{DL_OPT_SB_SIZE,	      "Pool size option expected."},
-	{DL_OPT_SB_TYPE,	      "Pool type option expected."},
-	{DL_OPT_SB_THTYPE,	      "Pool threshold type option expected."},
-	{DL_OPT_SB_TH,		      "Threshold option expected."},
-	{DL_OPT_SB_TC,		      "TC index option expected."},
-	{DL_OPT_ESWITCH_MODE,	      "E-Switch mode option expected."},
-	{DL_OPT_ESWITCH_INLINE_MODE,  "E-Switch inline-mode option expected."},
-	{DL_OPT_DPIPE_TABLE_NAME,     "Dpipe table name expected."},
-	{DL_OPT_DPIPE_TABLE_COUNTERS, "Dpipe table counter state expected."},
-	{DL_OPT_ESWITCH_ENCAP_MODE,   "E-Switch encapsulation option expected."},
-	{DL_OPT_RESOURCE_PATH,	      "Resource path expected."},
-	{DL_OPT_RESOURCE_SIZE,	      "Resource size expected."},
-	{DL_OPT_PARAM_NAME,	      "Parameter name expected."},
-	{DL_OPT_PARAM_VALUE,	      "Value to set expected."},
-	{DL_OPT_PARAM_CMODE,	      "Configuration mode expected."},
-	{DL_OPT_REGION_SNAPSHOT_ID,   "Region snapshot id expected."},
-	{DL_OPT_REGION_ADDRESS,	      "Region address value expected."},
-	{DL_OPT_REGION_LENGTH,	      "Region length value expected."},
-	{DL_OPT_HEALTH_REPORTER_NAME, "Reporter's name is expected."},
-	{DL_OPT_TRAP_NAME,            "Trap's name is expected."},
-	{DL_OPT_TRAP_GROUP_NAME,      "Trap group's name is expected."},
-};
-
-static int dl_args_finding_required_validate(uint64_t o_required,
-					     uint64_t o_found)
-{
-	uint64_t o_flag;
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(dl_args_required); i++) {
-		o_flag = dl_args_required[i].o_flag;
-		if ((o_required & o_flag) && !(o_found & o_flag)) {
-			pr_err("%s\n", dl_args_required[i].err_msg);
-			return -EINVAL;
-		}
-	}
-	return 0;
-}
-
-static int dl_argv_parse(struct dl *dl, uint64_t o_required,
-			 uint64_t o_optional)
-{
-	struct dl_opts *opts = &dl->opts;
-	uint64_t o_all = o_required | o_optional;
-	uint64_t o_found = 0;
-	int err;
-
-	if (o_required & DL_OPT_HANDLE && o_required & DL_OPT_HANDLEP) {
-		uint64_t handle_bit;
-
-		err = dl_argv_handle_both(dl, &opts->bus_name, &opts->dev_name,
-					  &opts->port_index, &handle_bit);
-		if (err)
-			return err;
-		o_found |= handle_bit;
-	} else if (o_required & DL_OPT_HANDLE) {
-		err = dl_argv_handle(dl, &opts->bus_name, &opts->dev_name);
-		if (err)
-			return err;
-		o_found |= DL_OPT_HANDLE;
-	} else if (o_required & DL_OPT_HANDLEP) {
-		err = dl_argv_handle_port(dl, &opts->bus_name, &opts->dev_name,
-					  &opts->port_index);
-		if (err)
-			return err;
-		o_found |= DL_OPT_HANDLEP;
-	} else if (o_required & DL_OPT_HANDLE_REGION) {
-		err = dl_argv_handle_region(dl, &opts->bus_name,
-					    &opts->dev_name,
-					    &opts->region_name);
-		if (err)
-			return err;
-		o_found |= DL_OPT_HANDLE_REGION;
-	}
-
-	while (dl_argc(dl)) {
-		if (dl_argv_match(dl, "type") &&
-		    (o_all & DL_OPT_PORT_TYPE)) {
-			const char *typestr;
-
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &typestr);
-			if (err)
-				return err;
-			err = port_type_get(typestr, &opts->port_type);
-			if (err)
-				return err;
-			o_found |= DL_OPT_PORT_TYPE;
-		} else if (dl_argv_match(dl, "count") &&
-			   (o_all & DL_OPT_PORT_COUNT)) {
-			dl_arg_inc(dl);
-			err = dl_argv_uint32_t(dl, &opts->port_count);
-			if (err)
-				return err;
-			o_found |= DL_OPT_PORT_COUNT;
-		} else if (dl_argv_match(dl, "sb") &&
-			   (o_all & DL_OPT_SB)) {
-			dl_arg_inc(dl);
-			err = dl_argv_uint32_t(dl, &opts->sb_index);
-			if (err)
-				return err;
-			o_found |= DL_OPT_SB;
-		} else if (dl_argv_match(dl, "pool") &&
-			   (o_all & DL_OPT_SB_POOL)) {
-			dl_arg_inc(dl);
-			err = dl_argv_uint16_t(dl, &opts->sb_pool_index);
-			if (err)
-				return err;
-			o_found |= DL_OPT_SB_POOL;
-		} else if (dl_argv_match(dl, "size") &&
-			   (o_all & DL_OPT_SB_SIZE)) {
-			dl_arg_inc(dl);
-			err = dl_argv_uint32_t(dl, &opts->sb_pool_size);
-			if (err)
-				return err;
-			o_found |= DL_OPT_SB_SIZE;
-		} else if (dl_argv_match(dl, "type") &&
-			   (o_all & DL_OPT_SB_TYPE)) {
-			const char *typestr;
-
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &typestr);
-			if (err)
-				return err;
-			err = pool_type_get(typestr, &opts->sb_pool_type);
-			if (err)
-				return err;
-			o_found |= DL_OPT_SB_TYPE;
-		} else if (dl_argv_match(dl, "thtype") &&
-			   (o_all & DL_OPT_SB_THTYPE)) {
-			const char *typestr;
-
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &typestr);
-			if (err)
-				return err;
-			err = threshold_type_get(typestr,
-						 &opts->sb_pool_thtype);
-			if (err)
-				return err;
-			o_found |= DL_OPT_SB_THTYPE;
-		} else if (dl_argv_match(dl, "th") &&
-			   (o_all & DL_OPT_SB_TH)) {
-			dl_arg_inc(dl);
-			err = dl_argv_uint32_t(dl, &opts->sb_threshold);
-			if (err)
-				return err;
-			o_found |= DL_OPT_SB_TH;
-		} else if (dl_argv_match(dl, "tc") &&
-			   (o_all & DL_OPT_SB_TC)) {
-			dl_arg_inc(dl);
-			err = dl_argv_uint16_t(dl, &opts->sb_tc_index);
-			if (err)
-				return err;
-			o_found |= DL_OPT_SB_TC;
-		} else if (dl_argv_match(dl, "mode") &&
-			   (o_all & DL_OPT_ESWITCH_MODE)) {
-			const char *typestr;
-
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &typestr);
-			if (err)
-				return err;
-			err = eswitch_mode_get(typestr, &opts->eswitch_mode);
-			if (err)
-				return err;
-			o_found |= DL_OPT_ESWITCH_MODE;
-		} else if (dl_argv_match(dl, "inline-mode") &&
-			   (o_all & DL_OPT_ESWITCH_INLINE_MODE)) {
-			const char *typestr;
-
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &typestr);
-			if (err)
-				return err;
-			err = eswitch_inline_mode_get(
-				typestr, &opts->eswitch_inline_mode);
-			if (err)
-				return err;
-			o_found |= DL_OPT_ESWITCH_INLINE_MODE;
-		} else if (dl_argv_match(dl, "name") &&
-			   (o_all & DL_OPT_DPIPE_TABLE_NAME)) {
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &opts->dpipe_table_name);
-			if (err)
-				return err;
-			o_found |= DL_OPT_DPIPE_TABLE_NAME;
-		} else if (dl_argv_match(dl, "counters") &&
-			   (o_all & DL_OPT_DPIPE_TABLE_COUNTERS)) {
-			const char *typestr;
-
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &typestr);
-			if (err)
-				return err;
-			err = dpipe_counters_enable_get(typestr,
-							&opts->dpipe_counters_enable);
-			if (err)
-				return err;
-			o_found |= DL_OPT_DPIPE_TABLE_COUNTERS;
-		} else if (dl_argv_match(dl, "encap") &&
-			   (o_all & DL_OPT_ESWITCH_ENCAP_MODE)) {
-			const char *typestr;
-
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &typestr);
-			if (err)
-				return err;
-			err = eswitch_encap_mode_get(typestr,
-						     &opts->eswitch_encap_mode);
-			if (err)
-				return err;
-			o_found |= DL_OPT_ESWITCH_ENCAP_MODE;
-		} else if (dl_argv_match(dl, "path") &&
-			   (o_all & DL_OPT_RESOURCE_PATH)) {
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &opts->resource_path);
-			if (err)
-				return err;
-			o_found |= DL_OPT_RESOURCE_PATH;
-		} else if (dl_argv_match(dl, "size") &&
-			   (o_all & DL_OPT_RESOURCE_SIZE)) {
-			dl_arg_inc(dl);
-			err = dl_argv_uint32_t(dl, &opts->resource_size);
-			if (err)
-				return err;
-			o_found |= DL_OPT_RESOURCE_SIZE;
-		} else if (dl_argv_match(dl, "name") &&
-			   (o_all & DL_OPT_PARAM_NAME)) {
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &opts->param_name);
-			if (err)
-				return err;
-			o_found |= DL_OPT_PARAM_NAME;
-		} else if (dl_argv_match(dl, "value") &&
-			   (o_all & DL_OPT_PARAM_VALUE)) {
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &opts->param_value);
-			if (err)
-				return err;
-			o_found |= DL_OPT_PARAM_VALUE;
-		} else if (dl_argv_match(dl, "cmode") &&
-			   (o_all & DL_OPT_PARAM_CMODE)) {
-			const char *cmodestr;
-
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &cmodestr);
-			if (err)
-				return err;
-			err = param_cmode_get(cmodestr, &opts->cmode);
-			if (err)
-				return err;
-			o_found |= DL_OPT_PARAM_CMODE;
-		} else if (dl_argv_match(dl, "snapshot") &&
-			   (o_all & DL_OPT_REGION_SNAPSHOT_ID)) {
-			dl_arg_inc(dl);
-			err = dl_argv_uint32_t(dl, &opts->region_snapshot_id);
-			if (err)
-				return err;
-			o_found |= DL_OPT_REGION_SNAPSHOT_ID;
-		} else if (dl_argv_match(dl, "address") &&
-			   (o_all & DL_OPT_REGION_ADDRESS)) {
-			dl_arg_inc(dl);
-			err = dl_argv_uint64_t(dl, &opts->region_address);
-			if (err)
-				return err;
-			o_found |= DL_OPT_REGION_ADDRESS;
-		} else if (dl_argv_match(dl, "length") &&
-			   (o_all & DL_OPT_REGION_LENGTH)) {
-			dl_arg_inc(dl);
-			err = dl_argv_uint64_t(dl, &opts->region_length);
-			if (err)
-				return err;
-			o_found |= DL_OPT_REGION_LENGTH;
-		} else if (dl_argv_match(dl, "file") &&
-			   (o_all & DL_OPT_FLASH_FILE_NAME)) {
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &opts->flash_file_name);
-			if (err)
-				return err;
-			o_found |= DL_OPT_FLASH_FILE_NAME;
-		} else if (dl_argv_match(dl, "component") &&
-			   (o_all & DL_OPT_FLASH_COMPONENT)) {
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &opts->flash_component);
-			if (err)
-				return err;
-			o_found |= DL_OPT_FLASH_COMPONENT;
-		} else if (dl_argv_match(dl, "reporter") &&
-			   (o_all & DL_OPT_HEALTH_REPORTER_NAME)) {
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &opts->reporter_name);
-			if (err)
-				return err;
-			o_found |= DL_OPT_HEALTH_REPORTER_NAME;
-		} else if (dl_argv_match(dl, "grace_period") &&
-			   (o_all & DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD)) {
-			dl_arg_inc(dl);
-			err = dl_argv_uint64_t(dl,
-					       &opts->reporter_graceful_period);
-			if (err)
-				return err;
-			o_found |= DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD;
-		} else if (dl_argv_match(dl, "auto_recover") &&
-			(o_all & DL_OPT_HEALTH_REPORTER_AUTO_RECOVER)) {
-			dl_arg_inc(dl);
-			err = dl_argv_bool(dl, &opts->reporter_auto_recover);
-			if (err)
-				return err;
-			o_found |= DL_OPT_HEALTH_REPORTER_AUTO_RECOVER;
-		} else if (dl_argv_match(dl, "trap") &&
-			   (o_all & DL_OPT_TRAP_NAME)) {
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &opts->trap_name);
-			if (err)
-				return err;
-			o_found |= DL_OPT_TRAP_NAME;
-		} else if (dl_argv_match(dl, "group") &&
-			   (o_all & DL_OPT_TRAP_GROUP_NAME)) {
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &opts->trap_group_name);
-			if (err)
-				return err;
-			o_found |= DL_OPT_TRAP_GROUP_NAME;
-		} else if (dl_argv_match(dl, "action") &&
-			   (o_all & DL_OPT_TRAP_ACTION)) {
-			const char *actionstr;
-
-			dl_arg_inc(dl);
-			err = dl_argv_str(dl, &actionstr);
-			if (err)
-				return err;
-			err = trap_action_get(actionstr, &opts->trap_action);
-			if (err)
-				return err;
-			o_found |= DL_OPT_TRAP_ACTION;
-		} else {
-			pr_err("Unknown option \"%s\"\n", dl_argv(dl));
-			return -EINVAL;
-		}
-	}
-
-	opts->present = o_found;
-
-	if ((o_optional & DL_OPT_SB) && !(o_found & DL_OPT_SB)) {
-		opts->sb_index = 0;
-		opts->present |= DL_OPT_SB;
-	}
-
-	return dl_args_finding_required_validate(o_required, o_found);
-}
-
-static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
-{
-	struct dl_opts *opts = &dl->opts;
-
-	if (opts->present & DL_OPT_HANDLE) {
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, opts->bus_name);
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, opts->dev_name);
-	} else if (opts->present & DL_OPT_HANDLEP) {
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, opts->bus_name);
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, opts->dev_name);
-		mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_INDEX,
-				 opts->port_index);
-	} else if (opts->present & DL_OPT_HANDLE_REGION) {
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, opts->bus_name);
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, opts->dev_name);
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_REGION_NAME,
-				  opts->region_name);
-	}
-	if (opts->present & DL_OPT_PORT_TYPE)
-		mnl_attr_put_u16(nlh, DEVLINK_ATTR_PORT_TYPE,
-				 opts->port_type);
-	if (opts->present & DL_OPT_PORT_COUNT)
-		mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_SPLIT_COUNT,
-				 opts->port_count);
-	if (opts->present & DL_OPT_SB)
-		mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_INDEX,
-				 opts->sb_index);
-	if (opts->present & DL_OPT_SB_POOL)
-		mnl_attr_put_u16(nlh, DEVLINK_ATTR_SB_POOL_INDEX,
-				 opts->sb_pool_index);
-	if (opts->present & DL_OPT_SB_SIZE)
-		mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_POOL_SIZE,
-				 opts->sb_pool_size);
-	if (opts->present & DL_OPT_SB_TYPE)
-		mnl_attr_put_u8(nlh, DEVLINK_ATTR_SB_POOL_TYPE,
-				opts->sb_pool_type);
-	if (opts->present & DL_OPT_SB_THTYPE)
-		mnl_attr_put_u8(nlh, DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE,
-				opts->sb_pool_thtype);
-	if (opts->present & DL_OPT_SB_TH)
-		mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_THRESHOLD,
-				 opts->sb_threshold);
-	if (opts->present & DL_OPT_SB_TC)
-		mnl_attr_put_u16(nlh, DEVLINK_ATTR_SB_TC_INDEX,
-				 opts->sb_tc_index);
-	if (opts->present & DL_OPT_ESWITCH_MODE)
-		mnl_attr_put_u16(nlh, DEVLINK_ATTR_ESWITCH_MODE,
-				 opts->eswitch_mode);
-	if (opts->present & DL_OPT_ESWITCH_INLINE_MODE)
-		mnl_attr_put_u8(nlh, DEVLINK_ATTR_ESWITCH_INLINE_MODE,
-				opts->eswitch_inline_mode);
-	if (opts->present & DL_OPT_DPIPE_TABLE_NAME)
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_DPIPE_TABLE_NAME,
-				  opts->dpipe_table_name);
-	if (opts->present & DL_OPT_DPIPE_TABLE_COUNTERS)
-		mnl_attr_put_u8(nlh, DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED,
-				opts->dpipe_counters_enable);
-	if (opts->present & DL_OPT_ESWITCH_ENCAP_MODE)
-		mnl_attr_put_u8(nlh, DEVLINK_ATTR_ESWITCH_ENCAP_MODE,
-				opts->eswitch_encap_mode);
-	if ((opts->present & DL_OPT_RESOURCE_PATH) && opts->resource_id_valid)
-		mnl_attr_put_u64(nlh, DEVLINK_ATTR_RESOURCE_ID,
-				 opts->resource_id);
-	if (opts->present & DL_OPT_RESOURCE_SIZE)
-		mnl_attr_put_u64(nlh, DEVLINK_ATTR_RESOURCE_SIZE,
-				 opts->resource_size);
-	if (opts->present & DL_OPT_PARAM_NAME)
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_PARAM_NAME,
-				  opts->param_name);
-	if (opts->present & DL_OPT_PARAM_CMODE)
-		mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_VALUE_CMODE,
-				opts->cmode);
-	if (opts->present & DL_OPT_REGION_SNAPSHOT_ID)
-		mnl_attr_put_u32(nlh, DEVLINK_ATTR_REGION_SNAPSHOT_ID,
-				 opts->region_snapshot_id);
-	if (opts->present & DL_OPT_REGION_ADDRESS)
-		mnl_attr_put_u64(nlh, DEVLINK_ATTR_REGION_CHUNK_ADDR,
-				 opts->region_address);
-	if (opts->present & DL_OPT_REGION_LENGTH)
-		mnl_attr_put_u64(nlh, DEVLINK_ATTR_REGION_CHUNK_LEN,
-				 opts->region_length);
-	if (opts->present & DL_OPT_FLASH_FILE_NAME)
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME,
-				  opts->flash_file_name);
-	if (opts->present & DL_OPT_FLASH_COMPONENT)
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_FLASH_UPDATE_COMPONENT,
-				  opts->flash_component);
-	if (opts->present & DL_OPT_HEALTH_REPORTER_NAME)
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_HEALTH_REPORTER_NAME,
-				  opts->reporter_name);
-	if (opts->present & DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD)
-		mnl_attr_put_u64(nlh,
-				 DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,
-				 opts->reporter_graceful_period);
-	if (opts->present & DL_OPT_HEALTH_REPORTER_AUTO_RECOVER)
-		mnl_attr_put_u8(nlh, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
-				opts->reporter_auto_recover);
-	if (opts->present & DL_OPT_TRAP_NAME)
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_TRAP_NAME,
-				  opts->trap_name);
-	if (opts->present & DL_OPT_TRAP_GROUP_NAME)
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_TRAP_GROUP_NAME,
-				  opts->trap_group_name);
-	if (opts->present & DL_OPT_TRAP_ACTION)
-		mnl_attr_put_u8(nlh, DEVLINK_ATTR_TRAP_ACTION,
-				opts->trap_action);
-
-}
-
-static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
-			     uint64_t o_required, uint64_t o_optional)
-{
-	int err;
-
-	err = dl_argv_parse(dl, o_required, o_optional);
-	if (err)
-		return err;
-	dl_opts_put(nlh, dl);
-	return 0;
-}
-
-static bool dl_dump_filter(struct dl *dl, struct nlattr **tb)
-{
-	struct dl_opts *opts = &dl->opts;
-	struct nlattr *attr_bus_name = tb[DEVLINK_ATTR_BUS_NAME];
-	struct nlattr *attr_dev_name = tb[DEVLINK_ATTR_DEV_NAME];
-	struct nlattr *attr_port_index = tb[DEVLINK_ATTR_PORT_INDEX];
-	struct nlattr *attr_sb_index = tb[DEVLINK_ATTR_SB_INDEX];
-
-	if (opts->present & DL_OPT_HANDLE &&
-	    attr_bus_name && attr_dev_name) {
-		const char *bus_name = mnl_attr_get_str(attr_bus_name);
-		const char *dev_name = mnl_attr_get_str(attr_dev_name);
-
-		if (strcmp(bus_name, opts->bus_name) != 0 ||
-		    strcmp(dev_name, opts->dev_name) != 0)
-			return false;
-	}
-	if (opts->present & DL_OPT_HANDLEP &&
-	    attr_bus_name && attr_dev_name && attr_port_index) {
-		const char *bus_name = mnl_attr_get_str(attr_bus_name);
-		const char *dev_name = mnl_attr_get_str(attr_dev_name);
-		uint32_t port_index = mnl_attr_get_u32(attr_port_index);
-
-		if (strcmp(bus_name, opts->bus_name) != 0 ||
-		    strcmp(dev_name, opts->dev_name) != 0 ||
-		    port_index != opts->port_index)
-			return false;
-	}
-	if (opts->present & DL_OPT_SB && attr_sb_index) {
-		uint32_t sb_index = mnl_attr_get_u32(attr_sb_index);
-
-		if (sb_index != opts->sb_index)
-			return false;
-	}
-	return true;
-}
-
-static void cmd_dev_help(void)
-{
-	pr_err("Usage: devlink dev show [ DEV ]\n");
-	pr_err("       devlink dev eswitch set DEV [ mode { legacy | switchdev } ]\n");
-	pr_err("                               [ inline-mode { none | link | network | transport } ]\n");
-	pr_err("                               [ encap { disable | enable } ]\n");
-	pr_err("       devlink dev eswitch show DEV\n");
-	pr_err("       devlink dev param set DEV name PARAMETER value VALUE cmode { permanent | driverinit | runtime }\n");
-	pr_err("       devlink dev param show [DEV name PARAMETER]\n");
-	pr_err("       devlink dev reload DEV\n");
-	pr_err("       devlink dev info [ DEV ]\n");
-	pr_err("       devlink dev flash DEV file PATH [ component NAME ]\n");
-}
-
-static bool cmp_arr_last_handle(struct dl *dl, const char *bus_name,
-				const char *dev_name)
-{
-	if (!dl->arr_last.present)
-		return false;
-	return strcmp(dl->arr_last.bus_name, bus_name) == 0 &&
-	       strcmp(dl->arr_last.dev_name, dev_name) == 0;
-}
-
-static void arr_last_handle_set(struct dl *dl, const char *bus_name,
-				const char *dev_name)
-{
-	dl->arr_last.present = true;
-	free(dl->arr_last.dev_name);
-	free(dl->arr_last.bus_name);
-	dl->arr_last.bus_name = strdup(bus_name);
-	dl->arr_last.dev_name = strdup(dev_name);
-}
-
-static bool should_arr_last_handle_start(struct dl *dl, const char *bus_name,
-					 const char *dev_name)
-{
-	return !cmp_arr_last_handle(dl, bus_name, dev_name);
-}
-
-static bool should_arr_last_handle_end(struct dl *dl, const char *bus_name,
-				       const char *dev_name)
-{
-	return dl->arr_last.present &&
-	       !cmp_arr_last_handle(dl, bus_name, dev_name);
-}
-
-static void __pr_out_handle_start(struct dl *dl, struct nlattr **tb,
-				  bool content, bool array)
-{
-	const char *bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
-	const char *dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
-	char buf[64];
-
-	sprintf(buf, "%s/%s", bus_name, dev_name);
-
-	if (dl->json_output) {
-		if (array) {
-			if (should_arr_last_handle_end(dl, bus_name, dev_name))
-				jsonw_end_array(dl->jw);
-			if (should_arr_last_handle_start(dl, bus_name,
-							 dev_name)) {
-				jsonw_name(dl->jw, buf);
-				jsonw_start_array(dl->jw);
-				jsonw_start_object(dl->jw);
-				arr_last_handle_set(dl, bus_name, dev_name);
-			} else {
-				jsonw_start_object(dl->jw);
-			}
-		} else {
-			jsonw_name(dl->jw, buf);
-			jsonw_start_object(dl->jw);
-		}
-	} else {
-		if (array) {
-			if (should_arr_last_handle_end(dl, bus_name, dev_name))
-				__pr_out_indent_dec();
-			if (should_arr_last_handle_start(dl, bus_name,
-							 dev_name)) {
-				pr_out("%s%s", buf, content ? ":" : "");
-				__pr_out_newline();
-				__pr_out_indent_inc();
-				arr_last_handle_set(dl, bus_name, dev_name);
-			}
-		} else {
-			pr_out("%s%s", buf, content ? ":" : "");
-		}
-	}
-}
-
-static void pr_out_handle_start_arr(struct dl *dl, struct nlattr **tb)
-{
-	__pr_out_handle_start(dl, tb, true, true);
-}
-
-static void pr_out_handle_end(struct dl *dl)
-{
-	if (dl->json_output)
-		jsonw_end_object(dl->jw);
-	else
-		__pr_out_newline();
-}
-
-static void pr_out_handle(struct dl *dl, struct nlattr **tb)
-{
-	__pr_out_handle_start(dl, tb, false, false);
-	pr_out_handle_end(dl);
-}
-
-static bool cmp_arr_last_port_handle(struct dl *dl, const char *bus_name,
-				     const char *dev_name, uint32_t port_index)
-{
-	return cmp_arr_last_handle(dl, bus_name, dev_name) &&
-	       dl->arr_last.port_index == port_index;
-}
-
-static void arr_last_port_handle_set(struct dl *dl, const char *bus_name,
-				     const char *dev_name, uint32_t port_index)
-{
-	arr_last_handle_set(dl, bus_name, dev_name);
-	dl->arr_last.port_index = port_index;
-}
-
-static bool should_arr_last_port_handle_start(struct dl *dl,
-					      const char *bus_name,
-					      const char *dev_name,
-					      uint32_t port_index)
-{
-	return !cmp_arr_last_port_handle(dl, bus_name, dev_name, port_index);
-}
-
-static bool should_arr_last_port_handle_end(struct dl *dl,
-					    const char *bus_name,
-					    const char *dev_name,
-					    uint32_t port_index)
-{
-	return dl->arr_last.present &&
-	       !cmp_arr_last_port_handle(dl, bus_name, dev_name, port_index);
-}
-
-static void __pr_out_port_handle_start(struct dl *dl, const char *bus_name,
-				       const char *dev_name,
-				       uint32_t port_index, bool try_nice,
-				       bool array)
-{
-	static char buf[64];
-	char *ifname = NULL;
-
-	if (dl->no_nice_names || !try_nice ||
-	    ifname_map_rev_lookup(dl, bus_name, dev_name,
-				  port_index, &ifname) != 0)
-		sprintf(buf, "%s/%s/%d", bus_name, dev_name, port_index);
-	else
-		sprintf(buf, "%s", ifname);
-
-	if (dl->json_output) {
-		if (array) {
-			if (should_arr_last_port_handle_end(dl, bus_name,
-							    dev_name,
-							    port_index))
-				jsonw_end_array(dl->jw);
-			if (should_arr_last_port_handle_start(dl, bus_name,
-							      dev_name,
-							      port_index)) {
-				jsonw_name(dl->jw, buf);
-				jsonw_start_array(dl->jw);
-				jsonw_start_object(dl->jw);
-				arr_last_port_handle_set(dl, bus_name, dev_name,
-							 port_index);
-			} else {
-				jsonw_start_object(dl->jw);
-			}
-		} else {
-			jsonw_name(dl->jw, buf);
-			jsonw_start_object(dl->jw);
-		}
-	} else {
-		pr_out("%s:", buf);
-	}
-}
-
-static void pr_out_port_handle_start(struct dl *dl, struct nlattr **tb, bool try_nice)
-{
-	const char *bus_name;
-	const char *dev_name;
-	uint32_t port_index;
-
-	bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
-	dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
-	port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]);
-	__pr_out_port_handle_start(dl, bus_name, dev_name, port_index, try_nice, false);
-}
-
-static void pr_out_port_handle_start_arr(struct dl *dl, struct nlattr **tb, bool try_nice)
-{
-	const char *bus_name;
-	const char *dev_name;
-	uint32_t port_index;
-
-	bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
-	dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
-	port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]);
-	__pr_out_port_handle_start(dl, bus_name, dev_name, port_index, try_nice, true);
-}
-
-static void pr_out_port_handle_end(struct dl *dl)
-{
-	if (dl->json_output)
-		jsonw_end_object(dl->jw);
-	else
-		pr_out("\n");
-}
-
-
-static void pr_out_str(struct dl *dl, const char *name, const char *val)
-{
-	__pr_out_indent_newline(dl);
-	if (dl->json_output)
-		jsonw_string_field(dl->jw, name, val);
-	else
-		pr_out("%s %s", name, val);
-}
-
-static void pr_out_bool(struct dl *dl, const char *name, bool val)
-{
-	if (dl->json_output)
-		jsonw_bool_field(dl->jw, name, val);
-	else
-		pr_out_str(dl, name, val ? "true" : "false");
-}
-
-static void pr_out_uint(struct dl *dl, const char *name, unsigned int val)
-{
-	__pr_out_indent_newline(dl);
-	if (dl->json_output)
-		jsonw_uint_field(dl->jw, name, val);
-	else
-		pr_out("%s %u", name, val);
-}
-
-static void pr_out_u64(struct dl *dl, const char *name, uint64_t val)
-{
-	__pr_out_indent_newline(dl);
-	if (val == (uint64_t) -1)
-		return pr_out_str(dl, name, "unlimited");
-
-	if (dl->json_output)
-		jsonw_u64_field(dl->jw, name, val);
-	else
-		pr_out("%s %"PRIu64, name, val);
-}
-
-static void pr_out_bool_value(struct dl *dl, bool value)
-{
-	__pr_out_indent_newline(dl);
-	if (dl->json_output)
-		jsonw_bool(dl->jw, value);
-	else
-		pr_out("%s", value ? "true" : "false");
-}
-
-static void pr_out_uint_value(struct dl *dl, unsigned int value)
-{
-	__pr_out_indent_newline(dl);
-	if (dl->json_output)
-		jsonw_uint(dl->jw, value);
-	else
-		pr_out("%u", value);
-}
-
-static void pr_out_uint64_value(struct dl *dl, uint64_t value)
-{
-	__pr_out_indent_newline(dl);
-	if (dl->json_output)
-		jsonw_u64(dl->jw, value);
-	else
-		pr_out("%"PRIu64, value);
-}
-
-static bool is_binary_eol(int i)
-{
-	return !(i%16);
-}
-
-static void pr_out_binary_value(struct dl *dl, uint8_t *data, uint32_t len)
-{
-	int i = 0;
-
-	while (i < len) {
-		if (dl->json_output)
-			jsonw_printf(dl->jw, "%d", data[i]);
-		else
-			pr_out("%02x ", data[i]);
-		i++;
-		if (!dl->json_output && is_binary_eol(i))
-			__pr_out_newline();
-	}
-	if (!dl->json_output && !is_binary_eol(i))
-		__pr_out_newline();
-}
-
-static void pr_out_str_value(struct dl *dl, const char *value)
-{
-	__pr_out_indent_newline(dl);
-	if (dl->json_output)
-		jsonw_string(dl->jw, value);
-	else
-		pr_out("%s", value);
-}
-
-static void pr_out_name(struct dl *dl, const char *name)
-{
-	__pr_out_indent_newline(dl);
-	if (dl->json_output)
-		jsonw_name(dl->jw, name);
-	else
-		pr_out("%s:", name);
-}
-
-static void pr_out_region_chunk_start(struct dl *dl, uint64_t addr)
-{
-	if (dl->json_output) {
-		jsonw_name(dl->jw, "address");
-		jsonw_uint(dl->jw, addr);
-		jsonw_name(dl->jw, "data");
-		jsonw_start_array(dl->jw);
-	}
-}
-
-static void pr_out_region_chunk_end(struct dl *dl)
-{
-	if (dl->json_output)
-		jsonw_end_array(dl->jw);
-}
-
-static void pr_out_region_chunk(struct dl *dl, uint8_t *data, uint32_t len,
-				uint64_t addr)
-{
-	static uint64_t align_val;
-	uint32_t i = 0;
-
-	pr_out_region_chunk_start(dl, addr);
-	while (i < len) {
-		if (!dl->json_output)
-			if (!(align_val % 16))
-				pr_out("%s%016"PRIx64" ",
-				       align_val ? "\n" : "",
-				       addr);
-
-		align_val++;
-
-		if (dl->json_output)
-			jsonw_printf(dl->jw, "%d", data[i]);
-		else
-			pr_out("%02x ", data[i]);
-
-		addr++;
-		i++;
-	}
-	pr_out_region_chunk_end(dl);
-}
-
-static void pr_out_section_start(struct dl *dl, const char *name)
-{
-	if (dl->json_output) {
-		jsonw_start_object(dl->jw);
-		jsonw_name(dl->jw, name);
-		jsonw_start_object(dl->jw);
-	}
-}
-
-static void pr_out_section_end(struct dl *dl)
-{
-	if (dl->json_output) {
-		if (dl->arr_last.present)
-			jsonw_end_array(dl->jw);
-		jsonw_end_object(dl->jw);
-		jsonw_end_object(dl->jw);
-	}
-}
-
-static void pr_out_array_start(struct dl *dl, const char *name)
-{
-	if (dl->json_output) {
-		jsonw_name(dl->jw, name);
-		jsonw_start_array(dl->jw);
-	} else {
-		__pr_out_indent_inc();
-		__pr_out_newline();
-		pr_out("%s:", name);
-		__pr_out_indent_inc();
-		__pr_out_newline();
-	}
-}
-
-static void pr_out_array_end(struct dl *dl)
-{
-	if (dl->json_output) {
-		jsonw_end_array(dl->jw);
-	} else {
-		__pr_out_indent_dec();
-		__pr_out_indent_dec();
-	}
-}
-
-static void pr_out_object_start(struct dl *dl, const char *name)
-{
-	if (dl->json_output) {
-		jsonw_name(dl->jw, name);
-		jsonw_start_object(dl->jw);
-	} else {
-		__pr_out_indent_inc();
-		__pr_out_newline();
-		pr_out("%s:", name);
-		__pr_out_indent_inc();
-		__pr_out_newline();
-	}
-}
-
-static void pr_out_object_end(struct dl *dl)
-{
-	if (dl->json_output) {
-		jsonw_end_object(dl->jw);
-	} else {
-		__pr_out_indent_dec();
-		__pr_out_indent_dec();
-	}
-}
-
-static void pr_out_entry_start(struct dl *dl)
-{
-	if (dl->json_output)
-		jsonw_start_object(dl->jw);
-}
-
-static void pr_out_entry_end(struct dl *dl)
-{
-	if (dl->json_output)
-		jsonw_end_object(dl->jw);
-	else
-		__pr_out_newline();
-}
-
-static void pr_out_stats(struct dl *dl, struct nlattr *nla_stats)
-{
-	struct nlattr *tb[DEVLINK_ATTR_STATS_MAX + 1] = {};
-	int err;
-
-	if (!dl->stats)
-		return;
-
-	err = mnl_attr_parse_nested(nla_stats, attr_stats_cb, tb);
-	if (err != MNL_CB_OK)
-		return;
-
-	pr_out_object_start(dl, "stats");
-	pr_out_object_start(dl, "rx");
-	if (tb[DEVLINK_ATTR_STATS_RX_BYTES])
-		pr_out_u64(dl, "bytes",
-			   mnl_attr_get_u64(tb[DEVLINK_ATTR_STATS_RX_BYTES]));
-	if (tb[DEVLINK_ATTR_STATS_RX_PACKETS])
-		pr_out_u64(dl, "packets",
-			   mnl_attr_get_u64(tb[DEVLINK_ATTR_STATS_RX_PACKETS]));
-	pr_out_object_end(dl);
-	pr_out_object_end(dl);
-}
-
-static const char *param_cmode_name(uint8_t cmode)
-{
-	switch (cmode) {
-	case DEVLINK_PARAM_CMODE_RUNTIME:
-		return PARAM_CMODE_RUNTIME_STR;
-	case DEVLINK_PARAM_CMODE_DRIVERINIT:
-		return PARAM_CMODE_DRIVERINIT_STR;
-	case DEVLINK_PARAM_CMODE_PERMANENT:
-		return PARAM_CMODE_PERMANENT_STR;
-	default: return "<unknown type>";
-	}
-}
-
-static const char *eswitch_mode_name(uint32_t mode)
-{
-	switch (mode) {
-	case DEVLINK_ESWITCH_MODE_LEGACY: return ESWITCH_MODE_LEGACY;
-	case DEVLINK_ESWITCH_MODE_SWITCHDEV: return ESWITCH_MODE_SWITCHDEV;
-	default: return "<unknown mode>";
-	}
-}
-
-static const char *eswitch_inline_mode_name(uint32_t mode)
-{
-	switch (mode) {
-	case DEVLINK_ESWITCH_INLINE_MODE_NONE:
-		return ESWITCH_INLINE_MODE_NONE;
-	case DEVLINK_ESWITCH_INLINE_MODE_LINK:
-		return ESWITCH_INLINE_MODE_LINK;
-	case DEVLINK_ESWITCH_INLINE_MODE_NETWORK:
-		return ESWITCH_INLINE_MODE_NETWORK;
-	case DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT:
-		return ESWITCH_INLINE_MODE_TRANSPORT;
-	default:
-		return "<unknown mode>";
-	}
-}
-
-static void pr_out_eswitch(struct dl *dl, struct nlattr **tb)
-{
-	__pr_out_handle_start(dl, tb, true, false);
-
-	if (tb[DEVLINK_ATTR_ESWITCH_MODE])
-		pr_out_str(dl, "mode",
-			   eswitch_mode_name(mnl_attr_get_u16(tb[DEVLINK_ATTR_ESWITCH_MODE])));
-
-	if (tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE])
-		pr_out_str(dl, "inline-mode",
-			   eswitch_inline_mode_name(mnl_attr_get_u8(
-				   tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE])));
-
-	if (tb[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]) {
-		bool encap_mode = !!mnl_attr_get_u8(tb[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]);
-
-		pr_out_str(dl, "encap", encap_mode ? "enable" : "disable");
-	}
-
-	pr_out_handle_end(dl);
-}
-
-static int cmd_dev_eswitch_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct dl *dl = data;
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
-		return MNL_CB_ERROR;
-	pr_out_eswitch(dl, tb);
-	return MNL_CB_OK;
-}
-
-static int cmd_dev_eswitch_show(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_ESWITCH_GET,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0);
-	if (err)
-		return err;
-
-	pr_out_section_start(dl, "dev");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_eswitch_show_cb, dl);
-	pr_out_section_end(dl);
-	return err;
-}
-
-static int cmd_dev_eswitch_set(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_ESWITCH_SET,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE,
-				DL_OPT_ESWITCH_MODE |
-				DL_OPT_ESWITCH_INLINE_MODE |
-				DL_OPT_ESWITCH_ENCAP_MODE);
-
-	if (err)
-		return err;
-
-	if (dl->opts.present == 1) {
-		pr_err("Need to set at least one option\n");
-		return -ENOENT;
-	}
-
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static int cmd_dev_eswitch(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
-		cmd_dev_help();
-		return 0;
-	} else if (dl_argv_match(dl, "set")) {
-		dl_arg_inc(dl);
-		return cmd_dev_eswitch_set(dl);
-	} else if (dl_argv_match(dl, "show")) {
-		dl_arg_inc(dl);
-		return cmd_dev_eswitch_show(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-struct param_val_conv {
-	const char *name;
-	const char *vstr;
-	uint32_t vuint;
-};
-
-static bool param_val_conv_exists(const struct param_val_conv *param_val_conv,
-				  uint32_t len, const char *name)
-{
-	uint32_t i;
-
-	for (i = 0; i < len; i++)
-		if (!strcmp(param_val_conv[i].name, name))
-			return true;
-
-	return false;
-}
-
-static int
-param_val_conv_uint_get(const struct param_val_conv *param_val_conv,
-			uint32_t len, const char *name, const char *vstr,
-			uint32_t *vuint)
-{
-	uint32_t i;
-
-	for (i = 0; i < len; i++)
-		if (!strcmp(param_val_conv[i].name, name) &&
-		    !strcmp(param_val_conv[i].vstr, vstr)) {
-			*vuint = param_val_conv[i].vuint;
-			return 0;
-		}
-
-	return -ENOENT;
-}
-
-static int
-param_val_conv_str_get(const struct param_val_conv *param_val_conv,
-		       uint32_t len, const char *name, uint32_t vuint,
-		       const char **vstr)
-{
-	uint32_t i;
-
-	for (i = 0; i < len; i++)
-		if (!strcmp(param_val_conv[i].name, name) &&
-		    param_val_conv[i].vuint == vuint) {
-			*vstr = param_val_conv[i].vstr;
-			return 0;
-		}
-
-	return -ENOENT;
-}
-
-static const struct param_val_conv param_val_conv[] = {
-	{
-		.name = "fw_load_policy",
-		.vstr = "driver",
-		.vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER,
-	},
-	{
-		.name = "fw_load_policy",
-		.vstr = "flash",
-		.vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH,
-	},
-	{
-		.name = "reset_dev_on_drv_probe",
-		.vstr = "unknown",
-		.vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_UNKNOWN,
-	},
-	{
-		.name = "fw_load_policy",
-		.vstr = "unknown",
-		.vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_UNKNOWN,
-	},
-	{
-		.name = "reset_dev_on_drv_probe",
-		.vstr = "always",
-		.vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_ALWAYS,
-	},
-	{
-		.name = "reset_dev_on_drv_probe",
-		.vstr = "never",
-		.vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_NEVER,
-	},
-	{
-		.name = "reset_dev_on_drv_probe",
-		.vstr = "disk",
-		.vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_DISK,
-	},
-};
-
-#define PARAM_VAL_CONV_LEN ARRAY_SIZE(param_val_conv)
-
-static void pr_out_param_value(struct dl *dl, const char *nla_name,
-			       int nla_type, struct nlattr *nl)
-{
-	struct nlattr *nla_value[DEVLINK_ATTR_MAX + 1] = {};
-	struct nlattr *val_attr;
-	const char *vstr;
-	bool conv_exists;
-	int err;
-
-	err = mnl_attr_parse_nested(nl, attr_cb, nla_value);
-	if (err != MNL_CB_OK)
-		return;
-
-	if (!nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE] ||
-	    (nla_type != MNL_TYPE_FLAG &&
-	     !nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA]))
-		return;
-
-	pr_out_str(dl, "cmode",
-		   param_cmode_name(mnl_attr_get_u8(nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE])));
-	val_attr = nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA];
-
-	conv_exists = param_val_conv_exists(param_val_conv, PARAM_VAL_CONV_LEN,
-					    nla_name);
-
-	switch (nla_type) {
-	case MNL_TYPE_U8:
-		if (conv_exists) {
-			err = param_val_conv_str_get(param_val_conv,
-						     PARAM_VAL_CONV_LEN,
-						     nla_name,
-						     mnl_attr_get_u8(val_attr),
-						     &vstr);
-			if (err)
-				return;
-			pr_out_str(dl, "value", vstr);
-		} else {
-			pr_out_uint(dl, "value", mnl_attr_get_u8(val_attr));
-		}
-		break;
-	case MNL_TYPE_U16:
-		if (conv_exists) {
-			err = param_val_conv_str_get(param_val_conv,
-						     PARAM_VAL_CONV_LEN,
-						     nla_name,
-						     mnl_attr_get_u16(val_attr),
-						     &vstr);
-			if (err)
-				return;
-			pr_out_str(dl, "value", vstr);
-		} else {
-			pr_out_uint(dl, "value", mnl_attr_get_u16(val_attr));
-		}
-		break;
-	case MNL_TYPE_U32:
-		if (conv_exists) {
-			err = param_val_conv_str_get(param_val_conv,
-						     PARAM_VAL_CONV_LEN,
-						     nla_name,
-						     mnl_attr_get_u32(val_attr),
-						     &vstr);
-			if (err)
-				return;
-			pr_out_str(dl, "value", vstr);
-		} else {
-			pr_out_uint(dl, "value", mnl_attr_get_u32(val_attr));
-		}
-		break;
-	case MNL_TYPE_STRING:
-		pr_out_str(dl, "value", mnl_attr_get_str(val_attr));
-		break;
-	case MNL_TYPE_FLAG:
-		pr_out_bool(dl, "value", val_attr ? true : false);
-		break;
-	}
-}
-
-static void pr_out_param(struct dl *dl, struct nlattr **tb, bool array)
-{
-	struct nlattr *nla_param[DEVLINK_ATTR_MAX + 1] = {};
-	struct nlattr *param_value_attr;
-	const char *nla_name;
-	int nla_type;
-	int err;
-
-	err = mnl_attr_parse_nested(tb[DEVLINK_ATTR_PARAM], attr_cb, nla_param);
-	if (err != MNL_CB_OK)
-		return;
-	if (!nla_param[DEVLINK_ATTR_PARAM_NAME] ||
-	    !nla_param[DEVLINK_ATTR_PARAM_TYPE] ||
-	    !nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST])
-		return;
-
-	if (array)
-		pr_out_handle_start_arr(dl, tb);
-	else
-		__pr_out_handle_start(dl, tb, true, false);
-
-	nla_type = mnl_attr_get_u8(nla_param[DEVLINK_ATTR_PARAM_TYPE]);
-
-	nla_name = mnl_attr_get_str(nla_param[DEVLINK_ATTR_PARAM_NAME]);
-	pr_out_str(dl, "name", nla_name);
-
-	if (!nla_param[DEVLINK_ATTR_PARAM_GENERIC])
-		pr_out_str(dl, "type", "driver-specific");
-	else
-		pr_out_str(dl, "type", "generic");
-
-	pr_out_array_start(dl, "values");
-	mnl_attr_for_each_nested(param_value_attr,
-				 nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST]) {
-		pr_out_entry_start(dl);
-		pr_out_param_value(dl, nla_name, nla_type, param_value_attr);
-		pr_out_entry_end(dl);
-	}
-	pr_out_array_end(dl);
-	pr_out_handle_end(dl);
-}
-
-static int cmd_dev_param_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct dl *dl = data;
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_PARAM])
-		return MNL_CB_ERROR;
-	pr_out_param(dl, tb, true);
-	return MNL_CB_OK;
-}
-
-struct param_ctx {
-	struct dl *dl;
-	int nla_type;
-	union {
-		uint8_t vu8;
-		uint16_t vu16;
-		uint32_t vu32;
-		const char *vstr;
-		bool vbool;
-	} value;
-};
-
-static int cmd_dev_param_set_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	struct nlattr *nla_param[DEVLINK_ATTR_MAX + 1] = {};
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct nlattr *param_value_attr;
-	enum devlink_param_cmode cmode;
-	struct param_ctx *ctx = data;
-	struct dl *dl = ctx->dl;
-	int nla_type;
-	int err;
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_PARAM])
-		return MNL_CB_ERROR;
-
-	err = mnl_attr_parse_nested(tb[DEVLINK_ATTR_PARAM], attr_cb, nla_param);
-	if (err != MNL_CB_OK)
-		return MNL_CB_ERROR;
-
-	if (!nla_param[DEVLINK_ATTR_PARAM_TYPE] ||
-	    !nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST])
-		return MNL_CB_ERROR;
-
-	nla_type = mnl_attr_get_u8(nla_param[DEVLINK_ATTR_PARAM_TYPE]);
-	mnl_attr_for_each_nested(param_value_attr,
-				 nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST]) {
-		struct nlattr *nla_value[DEVLINK_ATTR_MAX + 1] = {};
-		struct nlattr *val_attr;
-
-		err = mnl_attr_parse_nested(param_value_attr,
-					    attr_cb, nla_value);
-		if (err != MNL_CB_OK)
-			return MNL_CB_ERROR;
-
-		if (!nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE] ||
-		    (nla_type != MNL_TYPE_FLAG &&
-		     !nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA]))
-			return MNL_CB_ERROR;
-
-		cmode = mnl_attr_get_u8(nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE]);
-		if (cmode == dl->opts.cmode) {
-			val_attr = nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA];
-			switch (nla_type) {
-			case MNL_TYPE_U8:
-				ctx->value.vu8 = mnl_attr_get_u8(val_attr);
-				break;
-			case MNL_TYPE_U16:
-				ctx->value.vu16 = mnl_attr_get_u16(val_attr);
-				break;
-			case MNL_TYPE_U32:
-				ctx->value.vu32 = mnl_attr_get_u32(val_attr);
-				break;
-			case MNL_TYPE_STRING:
-				ctx->value.vstr = mnl_attr_get_str(val_attr);
-				break;
-			case MNL_TYPE_FLAG:
-				ctx->value.vbool = val_attr ? true : false;
-				break;
-			}
-			break;
-		}
-	}
-	ctx->nla_type = nla_type;
-	return MNL_CB_OK;
-}
-
-static int cmd_dev_param_set(struct dl *dl)
-{
-	struct param_ctx ctx = {};
-	struct nlmsghdr *nlh;
-	bool conv_exists;
-	uint32_t val_u32;
-	uint16_t val_u16;
-	uint8_t val_u8;
-	bool val_bool;
-	int err;
-
-	err = dl_argv_parse(dl, DL_OPT_HANDLE |
-			    DL_OPT_PARAM_NAME |
-			    DL_OPT_PARAM_VALUE |
-			    DL_OPT_PARAM_CMODE, 0);
-	if (err)
-		return err;
-
-	/* Get value type */
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PARAM_GET,
-			       NLM_F_REQUEST | NLM_F_ACK);
-	dl_opts_put(nlh, dl);
-
-	ctx.dl = dl;
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_param_set_cb, &ctx);
-	if (err)
-		return err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PARAM_SET,
-			       NLM_F_REQUEST | NLM_F_ACK);
-	dl_opts_put(nlh, dl);
-
-	conv_exists = param_val_conv_exists(param_val_conv, PARAM_VAL_CONV_LEN,
-					    dl->opts.param_name);
-
-	mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_TYPE, ctx.nla_type);
-	switch (ctx.nla_type) {
-	case MNL_TYPE_U8:
-		if (conv_exists) {
-			err = param_val_conv_uint_get(param_val_conv,
-						      PARAM_VAL_CONV_LEN,
-						      dl->opts.param_name,
-						      dl->opts.param_value,
-						      &val_u32);
-			val_u8 = val_u32;
-		} else {
-			err = strtouint8_t(dl->opts.param_value, &val_u8);
-		}
-		if (err)
-			goto err_param_value_parse;
-		if (val_u8 == ctx.value.vu8)
-			return 0;
-		mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u8);
-		break;
-	case MNL_TYPE_U16:
-		if (conv_exists) {
-			err = param_val_conv_uint_get(param_val_conv,
-						      PARAM_VAL_CONV_LEN,
-						      dl->opts.param_name,
-						      dl->opts.param_value,
-						      &val_u32);
-			val_u16 = val_u32;
-		} else {
-			err = strtouint16_t(dl->opts.param_value, &val_u16);
-		}
-		if (err)
-			goto err_param_value_parse;
-		if (val_u16 == ctx.value.vu16)
-			return 0;
-		mnl_attr_put_u16(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u16);
-		break;
-	case MNL_TYPE_U32:
-		if (conv_exists)
-			err = param_val_conv_uint_get(param_val_conv,
-						      PARAM_VAL_CONV_LEN,
-						      dl->opts.param_name,
-						      dl->opts.param_value,
-						      &val_u32);
-		else
-			err = strtouint32_t(dl->opts.param_value, &val_u32);
-		if (err)
-			goto err_param_value_parse;
-		if (val_u32 == ctx.value.vu32)
-			return 0;
-		mnl_attr_put_u32(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u32);
-		break;
-	case MNL_TYPE_FLAG:
-		err = strtobool(dl->opts.param_value, &val_bool);
-		if (err)
-			goto err_param_value_parse;
-		if (val_bool == ctx.value.vbool)
-			return 0;
-		if (val_bool)
-			mnl_attr_put(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA,
-				     0, NULL);
-		break;
-	case MNL_TYPE_STRING:
-		mnl_attr_put_strz(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA,
-				  dl->opts.param_value);
-		if (!strcmp(dl->opts.param_value, ctx.value.vstr))
-			return 0;
-		break;
-	default:
-		printf("Value type not supported\n");
-		return -ENOTSUP;
-	}
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-
-err_param_value_parse:
-	pr_err("Value \"%s\" is not a number or not within range\n",
-	       dl->opts.param_value);
-	return err;
-}
-
-static int cmd_dev_param_show(struct dl *dl)
-{
-	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
-	struct nlmsghdr *nlh;
-	int err;
-
-	if (dl_argc(dl) == 0)
-		flags |= NLM_F_DUMP;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PARAM_GET, flags);
-
-	if (dl_argc(dl) > 0) {
-		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE |
-					DL_OPT_PARAM_NAME, 0);
-		if (err)
-			return err;
-	}
-
-	pr_out_section_start(dl, "param");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_param_show_cb, dl);
-	pr_out_section_end(dl);
-	return err;
-}
-
-static int cmd_dev_param(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help")) {
-		cmd_dev_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show") ||
-		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
-		dl_arg_inc(dl);
-		return cmd_dev_param_show(dl);
-	} else if (dl_argv_match(dl, "set")) {
-		dl_arg_inc(dl);
-		return cmd_dev_param_set(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct dl *dl = data;
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	uint8_t reload_failed = 0;
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
-		return MNL_CB_ERROR;
-
-	if (tb[DEVLINK_ATTR_RELOAD_FAILED])
-		reload_failed = mnl_attr_get_u8(tb[DEVLINK_ATTR_RELOAD_FAILED]);
-
-	if (reload_failed) {
-		__pr_out_handle_start(dl, tb, true, false);
-		pr_out_bool(dl, "reload_failed", true);
-		pr_out_handle_end(dl);
-	} else {
-		pr_out_handle(dl, tb);
-	}
-
-	return MNL_CB_OK;
-}
-
-static int cmd_dev_show(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
-	int err;
-
-	if (dl_argc(dl) == 0)
-		flags |= NLM_F_DUMP;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_GET, flags);
-
-	if (dl_argc(dl) > 0) {
-		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0);
-		if (err)
-			return err;
-	}
-
-	pr_out_section_start(dl, "dev");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_show_cb, dl);
-	pr_out_section_end(dl);
-	return err;
-}
-
-static void cmd_dev_reload_help(void)
-{
-	pr_err("Usage: devlink dev reload [ DEV ]\n");
-}
-
-static int cmd_dev_reload(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
-		cmd_dev_reload_help();
-		return 0;
-	}
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RELOAD,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0);
-	if (err)
-		return err;
-
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static void pr_out_versions_single(struct dl *dl, const struct nlmsghdr *nlh,
-				   const char *name, int type)
-{
-	struct nlattr *version;
-
-	mnl_attr_for_each(version, nlh, sizeof(struct genlmsghdr)) {
-		struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-		const char *ver_value;
-		const char *ver_name;
-		int err;
-
-		if (mnl_attr_get_type(version) != type)
-			continue;
-
-		err = mnl_attr_parse_nested(version, attr_cb, tb);
-		if (err != MNL_CB_OK)
-			continue;
-
-		if (!tb[DEVLINK_ATTR_INFO_VERSION_NAME] ||
-		    !tb[DEVLINK_ATTR_INFO_VERSION_VALUE])
-			continue;
-
-		if (name) {
-			pr_out_object_start(dl, name);
-			name = NULL;
-		}
-
-		ver_name = mnl_attr_get_str(tb[DEVLINK_ATTR_INFO_VERSION_NAME]);
-		ver_value = mnl_attr_get_str(tb[DEVLINK_ATTR_INFO_VERSION_VALUE]);
-
-		pr_out_str(dl, ver_name, ver_value);
-		if (!dl->json_output)
-			__pr_out_newline();
-	}
-
-	if (!name)
-		pr_out_object_end(dl);
-}
-
-static void pr_out_info(struct dl *dl, const struct nlmsghdr *nlh,
-			struct nlattr **tb, bool has_versions)
-{
-	__pr_out_handle_start(dl, tb, true, false);
-
-	__pr_out_indent_inc();
-	if (tb[DEVLINK_ATTR_INFO_DRIVER_NAME]) {
-		struct nlattr *nla_drv = tb[DEVLINK_ATTR_INFO_DRIVER_NAME];
-
-		if (!dl->json_output)
-			__pr_out_newline();
-		pr_out_str(dl, "driver", mnl_attr_get_str(nla_drv));
-	}
-
-	if (tb[DEVLINK_ATTR_INFO_SERIAL_NUMBER]) {
-		struct nlattr *nla_sn = tb[DEVLINK_ATTR_INFO_SERIAL_NUMBER];
-
-		if (!dl->json_output)
-			__pr_out_newline();
-		pr_out_str(dl, "serial_number", mnl_attr_get_str(nla_sn));
-	}
-	__pr_out_indent_dec();
-
-	if (has_versions) {
-		pr_out_object_start(dl, "versions");
-
-		pr_out_versions_single(dl, nlh, "fixed",
-				       DEVLINK_ATTR_INFO_VERSION_FIXED);
-		pr_out_versions_single(dl, nlh, "running",
-				       DEVLINK_ATTR_INFO_VERSION_RUNNING);
-		pr_out_versions_single(dl, nlh, "stored",
-				       DEVLINK_ATTR_INFO_VERSION_STORED);
-
-		pr_out_object_end(dl);
-	}
-
-	pr_out_handle_end(dl);
-}
-
-static int cmd_versions_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	bool has_versions, has_info;
-	struct dl *dl = data;
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
-		return MNL_CB_ERROR;
-
-	has_versions = tb[DEVLINK_ATTR_INFO_VERSION_FIXED] ||
-		tb[DEVLINK_ATTR_INFO_VERSION_RUNNING] ||
-		tb[DEVLINK_ATTR_INFO_VERSION_STORED];
-	has_info = tb[DEVLINK_ATTR_INFO_DRIVER_NAME] ||
-		tb[DEVLINK_ATTR_INFO_SERIAL_NUMBER] ||
-		has_versions;
-
-	if (has_info)
-		pr_out_info(dl, nlh, tb, has_versions);
-
-	return MNL_CB_OK;
-}
-
-static void cmd_dev_info_help(void)
-{
-	pr_err("Usage: devlink dev info [ DEV ]\n");
-}
-
-static int cmd_dev_info(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
-	int err;
-
-	if (dl_argv_match(dl, "help")) {
-		cmd_dev_info_help();
-		return 0;
-	}
-
-	if (dl_argc(dl) == 0)
-		flags |= NLM_F_DUMP;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_INFO_GET, flags);
-
-	if (dl_argc(dl) > 0) {
-		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0);
-		if (err)
-			return err;
-	}
-
-	pr_out_section_start(dl, "info");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_versions_show_cb, dl);
-	pr_out_section_end(dl);
-	return err;
-}
-
-static void cmd_dev_flash_help(void)
-{
-	pr_err("Usage: devlink dev flash DEV file PATH [ component NAME ]\n");
-}
-
-
-struct cmd_dev_flash_status_ctx {
-	struct dl *dl;
-	char *last_msg;
-	char *last_component;
-	uint8_t not_first:1,
-		last_pc:1,
-		received_end:1,
-		flash_done:1;
-};
-
-static int nullstrcmp(const char *str1, const char *str2)
-{
-	if (str1 && str2)
-		return strcmp(str1, str2);
-	if (!str1 && !str2)
-		return 0;
-	return str1 ? 1 : -1;
-}
-
-static int cmd_dev_flash_status_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct cmd_dev_flash_status_ctx *ctx = data;
-	struct dl_opts *opts = &ctx->dl->opts;
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	const char *component = NULL;
-	uint64_t done = 0, total = 0;
-	const char *msg = NULL;
-	const char *bus_name;
-	const char *dev_name;
-
-	if (genl->cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS &&
-	    genl->cmd != DEVLINK_CMD_FLASH_UPDATE_END)
-		return MNL_CB_STOP;
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
-		return MNL_CB_ERROR;
-	bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
-	dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
-	if (strcmp(bus_name, opts->bus_name) ||
-	    strcmp(dev_name, opts->dev_name))
-		return MNL_CB_ERROR;
-
-	if (genl->cmd == DEVLINK_CMD_FLASH_UPDATE_END && ctx->not_first) {
-		pr_out("\n");
-		free(ctx->last_msg);
-		free(ctx->last_component);
-		ctx->received_end = 1;
-		return MNL_CB_STOP;
-	}
-
-	if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG])
-		msg = mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG]);
-	if (tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT])
-		component = mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]);
-	if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE])
-		done = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]);
-	if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL])
-		total = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]);
-
-	if (!nullstrcmp(msg, ctx->last_msg) &&
-	    !nullstrcmp(component, ctx->last_component) &&
-	    ctx->last_pc && ctx->not_first) {
-		pr_out_tty("\b\b\b\b\b"); /* clean percentage */
-	} else {
-		if (ctx->not_first)
-			pr_out("\n");
-		if (component) {
-			pr_out("[%s] ", component);
-			free(ctx->last_component);
-			ctx->last_component = strdup(component);
-		}
-		if (msg) {
-			pr_out("%s", msg);
-			free(ctx->last_msg);
-			ctx->last_msg = strdup(msg);
-		}
-	}
-	if (total) {
-		pr_out_tty(" %3lu%%", (done * 100) / total);
-		ctx->last_pc = 1;
-	} else {
-		ctx->last_pc = 0;
-	}
-	fflush(stdout);
-	ctx->not_first = 1;
-
-	return MNL_CB_STOP;
-}
-
-static int cmd_dev_flash_fds_process(struct cmd_dev_flash_status_ctx *ctx,
-				     struct mnlg_socket *nlg_ntf,
-				     int pipe_r)
-{
-	int nlfd = mnlg_socket_get_fd(nlg_ntf);
-	fd_set fds[3];
-	int fdmax;
-	int i;
-	int err;
-	int err2;
-
-	for (i = 0; i < 3; i++)
-		FD_ZERO(&fds[i]);
-	FD_SET(pipe_r, &fds[0]);
-	fdmax = pipe_r + 1;
-	FD_SET(nlfd, &fds[0]);
-	if (nlfd >= fdmax)
-		fdmax = nlfd + 1;
-
-	while (select(fdmax, &fds[0], &fds[1], &fds[2], NULL) < 0) {
-		if (errno == EINTR)
-			continue;
-		pr_err("select() failed\n");
-		return -errno;
-	}
-	if (FD_ISSET(nlfd, &fds[0])) {
-		err = _mnlg_socket_recv_run(nlg_ntf,
-					    cmd_dev_flash_status_cb, ctx);
-		if (err)
-			return err;
-	}
-	if (FD_ISSET(pipe_r, &fds[0])) {
-		err = read(pipe_r, &err2, sizeof(err2));
-		if (err == -1) {
-			pr_err("Failed to read pipe\n");
-			return -errno;
-		}
-		if (err2)
-			return err2;
-		ctx->flash_done = 1;
-	}
-	return 0;
-}
-
-
-static int cmd_dev_flash(struct dl *dl)
-{
-	struct cmd_dev_flash_status_ctx ctx = {.dl = dl,};
-	struct mnlg_socket *nlg_ntf;
-	struct nlmsghdr *nlh;
-	int pipe_r, pipe_w;
-	int pipe_fds[2];
-	pid_t pid;
-	int err;
-
-	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
-		cmd_dev_flash_help();
-		return 0;
-	}
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_FLASH_UPDATE,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_FLASH_FILE_NAME,
-				DL_OPT_FLASH_COMPONENT);
-	if (err)
-		return err;
-
-	nlg_ntf = mnlg_socket_open(DEVLINK_GENL_NAME, DEVLINK_GENL_VERSION);
-	if (!nlg_ntf)
-		return err;
-
-	err = _mnlg_socket_group_add(nlg_ntf, DEVLINK_GENL_MCGRP_CONFIG_NAME);
-	if (err)
-		return err;
-
-	err = pipe(pipe_fds);
-	if (err == -1)
-		return -errno;
-	pipe_r = pipe_fds[0];
-	pipe_w = pipe_fds[1];
-
-	pid = fork();
-	if (pid == -1) {
-		close(pipe_r);
-		close(pipe_w);
-		return -errno;
-	} else if (!pid) {
-		/* In child, just execute the flash and pass returned
-		 * value through pipe once it is done.
-		 */
-		close(pipe_r);
-		err = _mnlg_socket_send(dl->nlg, nlh);
-		write(pipe_w, &err, sizeof(err));
-		close(pipe_w);
-		exit(0);
-	}
-	close(pipe_w);
-
-	do {
-		err = cmd_dev_flash_fds_process(&ctx, nlg_ntf, pipe_r);
-		if (err)
-			goto out;
-	} while (!ctx.flash_done || (ctx.not_first && !ctx.received_end));
-
-	err = _mnlg_socket_recv_run(dl->nlg, NULL, NULL);
-out:
-	close(pipe_r);
-	mnlg_socket_close(nlg_ntf);
-	return err;
-}
-
-static int cmd_dev(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help")) {
-		cmd_dev_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show") ||
-		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
-		dl_arg_inc(dl);
-		return cmd_dev_show(dl);
-	} else if (dl_argv_match(dl, "eswitch")) {
-		dl_arg_inc(dl);
-		return cmd_dev_eswitch(dl);
-	} else if (dl_argv_match(dl, "reload")) {
-		dl_arg_inc(dl);
-		return cmd_dev_reload(dl);
-	} else if (dl_argv_match(dl, "param")) {
-		dl_arg_inc(dl);
-		return cmd_dev_param(dl);
-	} else if (dl_argv_match(dl, "info")) {
-		dl_arg_inc(dl);
-		return cmd_dev_info(dl);
-	} else if (dl_argv_match(dl, "flash")) {
-		dl_arg_inc(dl);
-		return cmd_dev_flash(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static void cmd_port_help(void)
-{
-	pr_err("Usage: devlink port show [ DEV/PORT_INDEX ]\n");
-	pr_err("       devlink port set DEV/PORT_INDEX [ type { eth | ib | auto} ]\n");
-	pr_err("       devlink port split DEV/PORT_INDEX count COUNT\n");
-	pr_err("       devlink port unsplit DEV/PORT_INDEX\n");
-}
-
-static const char *port_type_name(uint32_t type)
-{
-	switch (type) {
-	case DEVLINK_PORT_TYPE_NOTSET: return "notset";
-	case DEVLINK_PORT_TYPE_AUTO: return "auto";
-	case DEVLINK_PORT_TYPE_ETH: return "eth";
-	case DEVLINK_PORT_TYPE_IB: return "ib";
-	default: return "<unknown type>";
-	}
-}
-
-static const char *port_flavour_name(uint16_t flavour)
-{
-	switch (flavour) {
-	case DEVLINK_PORT_FLAVOUR_PHYSICAL:
-		return "physical";
-	case DEVLINK_PORT_FLAVOUR_CPU:
-		return "cpu";
-	case DEVLINK_PORT_FLAVOUR_DSA:
-		return "dsa";
-	case DEVLINK_PORT_FLAVOUR_PCI_PF:
-		return "pcipf";
-	case DEVLINK_PORT_FLAVOUR_PCI_VF:
-		return "pcivf";
-	default:
-		return "<unknown flavour>";
-	}
-}
-
-static void pr_out_port_pfvf_num(struct dl *dl, struct nlattr **tb)
-{
-	uint16_t fn_num;
-
-	if (tb[DEVLINK_ATTR_PORT_PCI_PF_NUMBER]) {
-		fn_num = mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_PCI_PF_NUMBER]);
-		pr_out_uint(dl, "pfnum", fn_num);
-	}
-	if (tb[DEVLINK_ATTR_PORT_PCI_VF_NUMBER]) {
-		fn_num = mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_PCI_VF_NUMBER]);
-		pr_out_uint(dl, "vfnum", fn_num);
-	}
-}
-
-static void pr_out_port(struct dl *dl, struct nlattr **tb)
-{
-	struct nlattr *pt_attr = tb[DEVLINK_ATTR_PORT_TYPE];
-	struct nlattr *dpt_attr = tb[DEVLINK_ATTR_PORT_DESIRED_TYPE];
-
-	pr_out_port_handle_start(dl, tb, false);
-	if (pt_attr) {
-		uint16_t port_type = mnl_attr_get_u16(pt_attr);
-
-		pr_out_str(dl, "type", port_type_name(port_type));
-		if (dpt_attr) {
-			uint16_t des_port_type = mnl_attr_get_u16(dpt_attr);
-
-			if (port_type != des_port_type)
-				pr_out_str(dl, "des_type",
-					   port_type_name(des_port_type));
-		}
-	}
-	if (tb[DEVLINK_ATTR_PORT_NETDEV_NAME])
-		pr_out_str(dl, "netdev",
-			   mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_NETDEV_NAME]));
-	if (tb[DEVLINK_ATTR_PORT_IBDEV_NAME])
-		pr_out_str(dl, "ibdev",
-			   mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_IBDEV_NAME]));
-	if (tb[DEVLINK_ATTR_PORT_FLAVOUR]) {
-		uint16_t port_flavour =
-				mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_FLAVOUR]);
-
-		pr_out_str(dl, "flavour", port_flavour_name(port_flavour));
-
-		switch (port_flavour) {
-		case DEVLINK_PORT_FLAVOUR_PCI_PF:
-		case DEVLINK_PORT_FLAVOUR_PCI_VF:
-			pr_out_port_pfvf_num(dl, tb);
-			break;
-		default:
-			break;
-		}
-	}
-	if (tb[DEVLINK_ATTR_PORT_NUMBER]) {
-		uint32_t port_number;
-
-		port_number = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_NUMBER]);
-		pr_out_uint(dl, "port", port_number);
-	}
-	if (tb[DEVLINK_ATTR_PORT_SPLIT_GROUP])
-		pr_out_uint(dl, "split_group",
-			    mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_SPLIT_GROUP]));
-	pr_out_port_handle_end(dl);
-}
-
-static int cmd_port_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct dl *dl = data;
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_PORT_INDEX])
-		return MNL_CB_ERROR;
-	pr_out_port(dl, tb);
-	return MNL_CB_OK;
-}
-
-static int cmd_port_show(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
-	int err;
-
-	if (dl_argc(dl) == 0)
-		flags |= NLM_F_DUMP;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_GET, flags);
-
-	if (dl_argc(dl) > 0) {
-		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP, 0);
-		if (err)
-			return err;
-	}
-
-	pr_out_section_start(dl, "port");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_port_show_cb, dl);
-	pr_out_section_end(dl);
-	return err;
-}
-
-static int cmd_port_set(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_SET,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_PORT_TYPE, 0);
-	if (err)
-		return err;
-
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static int cmd_port_split(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_SPLIT,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_PORT_COUNT, 0);
-	if (err)
-		return err;
-
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static int cmd_port_unsplit(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_UNSPLIT,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP, 0);
-	if (err)
-		return err;
-
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static int cmd_port(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help")) {
-		cmd_port_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show") ||
-		   dl_argv_match(dl, "list") ||  dl_no_arg(dl)) {
-		dl_arg_inc(dl);
-		return cmd_port_show(dl);
-	} else if (dl_argv_match(dl, "set")) {
-		dl_arg_inc(dl);
-		return cmd_port_set(dl);
-	} else if (dl_argv_match(dl, "split")) {
-		dl_arg_inc(dl);
-		return cmd_port_split(dl);
-	} else if (dl_argv_match(dl, "unsplit")) {
-		dl_arg_inc(dl);
-		return cmd_port_unsplit(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static void cmd_sb_help(void)
-{
-	pr_err("Usage: devlink sb show [ DEV [ sb SB_INDEX ] ]\n");
-	pr_err("       devlink sb pool show [ DEV [ sb SB_INDEX ] pool POOL_INDEX ]\n");
-	pr_err("       devlink sb pool set DEV [ sb SB_INDEX ] pool POOL_INDEX\n");
-	pr_err("                           size POOL_SIZE thtype { static | dynamic }\n");
-	pr_err("       devlink sb port pool show [ DEV/PORT_INDEX [ sb SB_INDEX ]\n");
-	pr_err("                                   pool POOL_INDEX ]\n");
-	pr_err("       devlink sb port pool set DEV/PORT_INDEX [ sb SB_INDEX ]\n");
-	pr_err("                                pool POOL_INDEX th THRESHOLD\n");
-	pr_err("       devlink sb tc bind show [ DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n");
-	pr_err("                                 type { ingress | egress } ]\n");
-	pr_err("       devlink sb tc bind set DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n");
-	pr_err("                              type { ingress | egress } pool POOL_INDEX\n");
-	pr_err("                              th THRESHOLD\n");
-	pr_err("       devlink sb occupancy show { DEV | DEV/PORT_INDEX } [ sb SB_INDEX ]\n");
-	pr_err("       devlink sb occupancy snapshot DEV [ sb SB_INDEX ]\n");
-	pr_err("       devlink sb occupancy clearmax DEV [ sb SB_INDEX ]\n");
-}
-
-static void pr_out_sb(struct dl *dl, struct nlattr **tb)
-{
-	pr_out_handle_start_arr(dl, tb);
-	pr_out_uint(dl, "sb",
-		    mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]));
-	pr_out_uint(dl, "size",
-		    mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_SIZE]));
-	pr_out_uint(dl, "ing_pools",
-		    mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT]));
-	pr_out_uint(dl, "eg_pools",
-		    mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT]));
-	pr_out_uint(dl, "ing_tcs",
-		    mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT]));
-	pr_out_uint(dl, "eg_tcs",
-		    mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT]));
-	pr_out_handle_end(dl);
-}
-
-static int cmd_sb_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct dl *dl = data;
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_SB_INDEX] || !tb[DEVLINK_ATTR_SB_SIZE] ||
-	    !tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT] ||
-	    !tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT] ||
-	    !tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT] ||
-	    !tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT])
-		return MNL_CB_ERROR;
-	pr_out_sb(dl, tb);
-	return MNL_CB_OK;
-}
-
-static int cmd_sb_show(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
-	int err;
-
-	if (dl_argc(dl) == 0)
-		flags |= NLM_F_DUMP;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_GET, flags);
-
-	if (dl_argc(dl) > 0) {
-		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB);
-		if (err)
-			return err;
-	}
-
-	pr_out_section_start(dl, "sb");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_show_cb, dl);
-	pr_out_section_end(dl);
-	return err;
-}
-
-static const char *pool_type_name(uint8_t type)
-{
-	switch (type) {
-	case DEVLINK_SB_POOL_TYPE_INGRESS: return "ingress";
-	case DEVLINK_SB_POOL_TYPE_EGRESS: return "egress";
-	default: return "<unknown type>";
-	}
-}
-
-static const char *threshold_type_name(uint8_t type)
-{
-	switch (type) {
-	case DEVLINK_SB_THRESHOLD_TYPE_STATIC: return "static";
-	case DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC: return "dynamic";
-	default: return "<unknown type>";
-	}
-}
-
-static void pr_out_sb_pool(struct dl *dl, struct nlattr **tb)
-{
-	pr_out_handle_start_arr(dl, tb);
-	pr_out_uint(dl, "sb",
-		    mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]));
-	pr_out_uint(dl, "pool",
-		    mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]));
-	pr_out_str(dl, "type",
-		   pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE])));
-	pr_out_uint(dl, "size",
-		    mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_SIZE]));
-	pr_out_str(dl, "thtype",
-		   threshold_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])));
-	if (tb[DEVLINK_ATTR_SB_POOL_CELL_SIZE])
-		pr_out_uint(dl, "cell_size",
-			    mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_CELL_SIZE]));
-	pr_out_handle_end(dl);
-}
-
-static int cmd_sb_pool_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct dl *dl = data;
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_SB_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_INDEX] ||
-	    !tb[DEVLINK_ATTR_SB_POOL_TYPE] || !tb[DEVLINK_ATTR_SB_POOL_SIZE] ||
-	    !tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])
-		return MNL_CB_ERROR;
-	pr_out_sb_pool(dl, tb);
-	return MNL_CB_OK;
-}
-
-static int cmd_sb_pool_show(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
-	int err;
-
-	if (dl_argc(dl) == 0)
-		flags |= NLM_F_DUMP;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_GET, flags);
-
-	if (dl_argc(dl) > 0) {
-		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL,
-					DL_OPT_SB);
-		if (err)
-			return err;
-	}
-
-	pr_out_section_start(dl, "pool");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_pool_show_cb, dl);
-	pr_out_section_end(dl);
-	return err;
-}
-
-static int cmd_sb_pool_set(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_SET,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL |
-				DL_OPT_SB_SIZE | DL_OPT_SB_THTYPE, DL_OPT_SB);
-	if (err)
-		return err;
-
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static int cmd_sb_pool(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help")) {
-		cmd_sb_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show") ||
-		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
-		dl_arg_inc(dl);
-		return cmd_sb_pool_show(dl);
-	} else if (dl_argv_match(dl, "set")) {
-		dl_arg_inc(dl);
-		return cmd_sb_pool_set(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static void pr_out_sb_port_pool(struct dl *dl, struct nlattr **tb)
-{
-	pr_out_port_handle_start_arr(dl, tb, true);
-	pr_out_uint(dl, "sb",
-		    mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]));
-	pr_out_uint(dl, "pool",
-		    mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]));
-	pr_out_uint(dl, "threshold",
-		    mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD]));
-	pr_out_port_handle_end(dl);
-}
-
-static int cmd_sb_port_pool_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct dl *dl = data;
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] ||
-	    !tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD])
-		return MNL_CB_ERROR;
-	pr_out_sb_port_pool(dl, tb);
-	return MNL_CB_OK;
-}
-
-static int cmd_sb_port_pool_show(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
-	int err;
-
-	if (dl_argc(dl) == 0)
-		flags |= NLM_F_DUMP;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_GET, flags);
-
-	if (dl_argc(dl) > 0) {
-		err = dl_argv_parse_put(nlh, dl,
-					DL_OPT_HANDLEP | DL_OPT_SB_POOL,
-					DL_OPT_SB);
-		if (err)
-			return err;
-	}
-
-	pr_out_section_start(dl, "port_pool");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_port_pool_show_cb, dl);
-	pr_out_section_end(dl);
-	return 0;
-}
-
-static int cmd_sb_port_pool_set(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_SET,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_POOL |
-				DL_OPT_SB_TH, DL_OPT_SB);
-	if (err)
-		return err;
-
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static int cmd_sb_port_pool(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help")) {
-		cmd_sb_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show") ||
-		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
-		dl_arg_inc(dl);
-		return cmd_sb_port_pool_show(dl);
-	} else if (dl_argv_match(dl, "set")) {
-		dl_arg_inc(dl);
-		return cmd_sb_port_pool_set(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static int cmd_sb_port(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
-		cmd_sb_help();
-		return 0;
-	} else if (dl_argv_match(dl, "pool")) {
-		dl_arg_inc(dl);
-		return cmd_sb_port_pool(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static void pr_out_sb_tc_bind(struct dl *dl, struct nlattr **tb)
-{
-	pr_out_port_handle_start_arr(dl, tb, true);
-	pr_out_uint(dl, "sb",
-	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]));
-	pr_out_uint(dl, "tc",
-	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX]));
-	pr_out_str(dl, "type",
-	       pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE])));
-	pr_out_uint(dl, "pool",
-	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]));
-	pr_out_uint(dl, "threshold",
-	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD]));
-	pr_out_port_handle_end(dl);
-}
-
-static int cmd_sb_tc_bind_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct dl *dl = data;
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] ||
-	    !tb[DEVLINK_ATTR_SB_TC_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_TYPE] ||
-	    !tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD])
-		return MNL_CB_ERROR;
-	pr_out_sb_tc_bind(dl, tb);
-	return MNL_CB_OK;
-}
-
-static int cmd_sb_tc_bind_show(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
-	int err;
-
-	if (dl_argc(dl) == 0)
-		flags |= NLM_F_DUMP;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_GET, flags);
-
-	if (dl_argc(dl) > 0) {
-		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC |
-					DL_OPT_SB_TYPE, DL_OPT_SB);
-		if (err)
-			return err;
-	}
-
-	pr_out_section_start(dl, "tc_bind");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_tc_bind_show_cb, dl);
-	pr_out_section_end(dl);
-	return err;
-}
-
-static int cmd_sb_tc_bind_set(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_SET,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC |
-				DL_OPT_SB_TYPE | DL_OPT_SB_POOL | DL_OPT_SB_TH,
-				DL_OPT_SB);
-	if (err)
-		return err;
-
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static int cmd_sb_tc_bind(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help")) {
-		cmd_sb_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show") ||
-		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
-		dl_arg_inc(dl);
-		return cmd_sb_tc_bind_show(dl);
-	} else if (dl_argv_match(dl, "set")) {
-		dl_arg_inc(dl);
-		return cmd_sb_tc_bind_set(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static int cmd_sb_tc(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
-		cmd_sb_help();
-		return 0;
-	} else if (dl_argv_match(dl, "bind")) {
-		dl_arg_inc(dl);
-		return cmd_sb_tc_bind(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-struct occ_item {
-	struct list_head list;
-	uint32_t index;
-	uint32_t cur;
-	uint32_t max;
-	uint32_t bound_pool_index;
-};
-
-struct occ_port {
-	struct list_head list;
-	char *bus_name;
-	char *dev_name;
-	uint32_t port_index;
-	uint32_t sb_index;
-	struct list_head pool_list;
-	struct list_head ing_tc_list;
-	struct list_head eg_tc_list;
-};
-
-struct occ_show {
-	struct dl *dl;
-	int err;
-	struct list_head port_list;
-};
-
-static struct occ_item *occ_item_alloc(void)
-{
-	return calloc(1, sizeof(struct occ_item));
-}
-
-static void occ_item_free(struct occ_item *occ_item)
-{
-	free(occ_item);
-}
-
-static struct occ_port *occ_port_alloc(uint32_t port_index)
-{
-	struct occ_port *occ_port;
-
-	occ_port = calloc(1, sizeof(*occ_port));
-	if (!occ_port)
-		return NULL;
-	occ_port->port_index = port_index;
-	INIT_LIST_HEAD(&occ_port->pool_list);
-	INIT_LIST_HEAD(&occ_port->ing_tc_list);
-	INIT_LIST_HEAD(&occ_port->eg_tc_list);
-	return occ_port;
-}
-
-static void occ_port_free(struct occ_port *occ_port)
-{
-	struct occ_item *occ_item, *tmp;
-
-	list_for_each_entry_safe(occ_item, tmp, &occ_port->pool_list, list)
-		occ_item_free(occ_item);
-	list_for_each_entry_safe(occ_item, tmp, &occ_port->ing_tc_list, list)
-		occ_item_free(occ_item);
-	list_for_each_entry_safe(occ_item, tmp, &occ_port->eg_tc_list, list)
-		occ_item_free(occ_item);
-}
-
-static struct occ_show *occ_show_alloc(struct dl *dl)
-{
-	struct occ_show *occ_show;
-
-	occ_show = calloc(1, sizeof(*occ_show));
-	if (!occ_show)
-		return NULL;
-	occ_show->dl = dl;
-	INIT_LIST_HEAD(&occ_show->port_list);
-	return occ_show;
-}
-
-static void occ_show_free(struct occ_show *occ_show)
-{
-	struct occ_port *occ_port, *tmp;
-
-	list_for_each_entry_safe(occ_port, tmp, &occ_show->port_list, list)
-		occ_port_free(occ_port);
-}
-
-static struct occ_port *occ_port_get(struct occ_show *occ_show,
-				     struct nlattr **tb)
-{
-	struct occ_port *occ_port;
-	uint32_t port_index;
-
-	port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]);
-
-	list_for_each_entry_reverse(occ_port, &occ_show->port_list, list) {
-		if (occ_port->port_index == port_index)
-			return occ_port;
-	}
-	occ_port = occ_port_alloc(port_index);
-	if (!occ_port)
-		return NULL;
-	list_add_tail(&occ_port->list, &occ_show->port_list);
-	return occ_port;
-}
-
-static void pr_out_occ_show_item_list(const char *label, struct list_head *list,
-				      bool bound_pool)
-{
-	struct occ_item *occ_item;
-	int i = 1;
-
-	pr_out_sp(7, "  %s:", label);
-	list_for_each_entry(occ_item, list, list) {
-		if ((i - 1) % 4 == 0 && i != 1)
-			pr_out_sp(7, " ");
-		if (bound_pool)
-			pr_out_sp(7, "%2u(%u):", occ_item->index,
-				  occ_item->bound_pool_index);
-		else
-			pr_out_sp(7, "%2u:", occ_item->index);
-		pr_out_sp(21, "%10u/%u", occ_item->cur, occ_item->max);
-		if (i++ % 4 == 0)
-			pr_out("\n");
-	}
-	if ((i - 1) % 4 != 0)
-		pr_out("\n");
-}
-
-static void pr_out_json_occ_show_item_list(struct dl *dl, const char *label,
-					   struct list_head *list,
-					   bool bound_pool)
-{
-	struct occ_item *occ_item;
-	char buf[32];
-
-	jsonw_name(dl->jw, label);
-	jsonw_start_object(dl->jw);
-	list_for_each_entry(occ_item, list, list) {
-		sprintf(buf, "%u", occ_item->index);
-		jsonw_name(dl->jw, buf);
-		jsonw_start_object(dl->jw);
-		if (bound_pool)
-			jsonw_uint_field(dl->jw, "bound_pool",
-					 occ_item->bound_pool_index);
-		jsonw_uint_field(dl->jw, "current", occ_item->cur);
-		jsonw_uint_field(dl->jw, "max", occ_item->max);
-		jsonw_end_object(dl->jw);
-	}
-	jsonw_end_object(dl->jw);
-}
-
-static void pr_out_occ_show_port(struct dl *dl, struct occ_port *occ_port)
-{
-	if (dl->json_output) {
-		pr_out_json_occ_show_item_list(dl, "pool",
-					       &occ_port->pool_list, false);
-		pr_out_json_occ_show_item_list(dl, "itc",
-					       &occ_port->ing_tc_list, true);
-		pr_out_json_occ_show_item_list(dl, "etc",
-					       &occ_port->eg_tc_list, true);
-	} else {
-		pr_out("\n");
-		pr_out_occ_show_item_list("pool", &occ_port->pool_list, false);
-		pr_out_occ_show_item_list("itc", &occ_port->ing_tc_list, true);
-		pr_out_occ_show_item_list("etc", &occ_port->eg_tc_list, true);
-	}
-}
-
-static void pr_out_occ_show(struct occ_show *occ_show)
-{
-	struct dl *dl = occ_show->dl;
-	struct dl_opts *opts = &dl->opts;
-	struct occ_port *occ_port;
-
-	list_for_each_entry(occ_port, &occ_show->port_list, list) {
-		__pr_out_port_handle_start(dl, opts->bus_name, opts->dev_name,
-					   occ_port->port_index, true, false);
-		pr_out_occ_show_port(dl, occ_port);
-		pr_out_port_handle_end(dl);
-	}
-}
-
-static void cmd_sb_occ_port_pool_process(struct occ_show *occ_show,
-					 struct nlattr **tb)
-{
-	struct occ_port *occ_port;
-	struct occ_item *occ_item;
-
-	if (occ_show->err || !dl_dump_filter(occ_show->dl, tb))
-		return;
-
-	occ_port = occ_port_get(occ_show, tb);
-	if (!occ_port) {
-		occ_show->err = -ENOMEM;
-		return;
-	}
-
-	occ_item = occ_item_alloc();
-	if (!occ_item) {
-		occ_show->err = -ENOMEM;
-		return;
-	}
-	occ_item->index = mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]);
-	occ_item->cur = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_CUR]);
-	occ_item->max = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_MAX]);
-	list_add_tail(&occ_item->list, &occ_port->pool_list);
-}
-
-static int cmd_sb_occ_port_pool_process_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct occ_show *occ_show = data;
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] ||
-	    !tb[DEVLINK_ATTR_SB_POOL_INDEX] ||
-	    !tb[DEVLINK_ATTR_SB_OCC_CUR] || !tb[DEVLINK_ATTR_SB_OCC_MAX])
-		return MNL_CB_ERROR;
-	cmd_sb_occ_port_pool_process(occ_show, tb);
-	return MNL_CB_OK;
-}
-
-static void cmd_sb_occ_tc_pool_process(struct occ_show *occ_show,
-				       struct nlattr **tb)
-{
-	struct occ_port *occ_port;
-	struct occ_item *occ_item;
-	uint8_t pool_type;
-
-	if (occ_show->err || !dl_dump_filter(occ_show->dl, tb))
-		return;
-
-	occ_port = occ_port_get(occ_show, tb);
-	if (!occ_port) {
-		occ_show->err = -ENOMEM;
-		return;
-	}
-
-	occ_item = occ_item_alloc();
-	if (!occ_item) {
-		occ_show->err = -ENOMEM;
-		return;
-	}
-	occ_item->index = mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX]);
-	occ_item->cur = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_CUR]);
-	occ_item->max = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_MAX]);
-	occ_item->bound_pool_index =
-			mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]);
-	pool_type = mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]);
-	if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS)
-		list_add_tail(&occ_item->list, &occ_port->ing_tc_list);
-	else if (pool_type == DEVLINK_SB_POOL_TYPE_EGRESS)
-		list_add_tail(&occ_item->list, &occ_port->eg_tc_list);
-	else
-		occ_item_free(occ_item);
-}
-
-static int cmd_sb_occ_tc_pool_process_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct occ_show *occ_show = data;
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] ||
-	    !tb[DEVLINK_ATTR_SB_TC_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_TYPE] ||
-	    !tb[DEVLINK_ATTR_SB_POOL_INDEX] ||
-	    !tb[DEVLINK_ATTR_SB_OCC_CUR] || !tb[DEVLINK_ATTR_SB_OCC_MAX])
-		return MNL_CB_ERROR;
-	cmd_sb_occ_tc_pool_process(occ_show, tb);
-	return MNL_CB_OK;
-}
-
-static int cmd_sb_occ_show(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	struct occ_show *occ_show;
-	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
-	int err;
-
-	err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_HANDLEP, DL_OPT_SB);
-	if (err)
-		return err;
-
-	occ_show = occ_show_alloc(dl);
-	if (!occ_show)
-		return -ENOMEM;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_GET, flags);
-
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh,
-				  cmd_sb_occ_port_pool_process_cb, occ_show);
-	if (err)
-		goto out;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_GET, flags);
-
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh,
-				  cmd_sb_occ_tc_pool_process_cb, occ_show);
-	if (err)
-		goto out;
-
-	pr_out_section_start(dl, "occupancy");
-	pr_out_occ_show(occ_show);
-	pr_out_section_end(dl);
-
-out:
-	occ_show_free(occ_show);
-	return err;
-}
-
-static int cmd_sb_occ_snapshot(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_OCC_SNAPSHOT,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB);
-	if (err)
-		return err;
-
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static int cmd_sb_occ_clearmax(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_OCC_MAX_CLEAR,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB);
-	if (err)
-		return err;
-
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static int cmd_sb_occ(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
-		cmd_sb_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show") ||
-		   dl_argv_match(dl, "list")) {
-		dl_arg_inc(dl);
-		return cmd_sb_occ_show(dl);
-	} else if (dl_argv_match(dl, "snapshot")) {
-		dl_arg_inc(dl);
-		return cmd_sb_occ_snapshot(dl);
-	} else if (dl_argv_match(dl, "clearmax")) {
-		dl_arg_inc(dl);
-		return cmd_sb_occ_clearmax(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static int cmd_sb(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help")) {
-		cmd_sb_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show") ||
-		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
-		dl_arg_inc(dl);
-		return cmd_sb_show(dl);
-	} else if (dl_argv_match(dl, "pool")) {
-		dl_arg_inc(dl);
-		return cmd_sb_pool(dl);
-	} else if (dl_argv_match(dl, "port")) {
-		dl_arg_inc(dl);
-		return cmd_sb_port(dl);
-	} else if (dl_argv_match(dl, "tc")) {
-		dl_arg_inc(dl);
-		return cmd_sb_tc(dl);
-	} else if (dl_argv_match(dl, "occupancy")) {
-		dl_arg_inc(dl);
-		return cmd_sb_occ(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static const char *cmd_name(uint8_t cmd)
-{
-	switch (cmd) {
-	case DEVLINK_CMD_UNSPEC: return "unspec";
-	case DEVLINK_CMD_GET: return "get";
-	case DEVLINK_CMD_SET: return "set";
-	case DEVLINK_CMD_NEW: return "new";
-	case DEVLINK_CMD_DEL: return "del";
-	case DEVLINK_CMD_PORT_GET: return "get";
-	case DEVLINK_CMD_PORT_SET: return "set";
-	case DEVLINK_CMD_PORT_NEW: return "new";
-	case DEVLINK_CMD_PORT_DEL: return "del";
-	case DEVLINK_CMD_PARAM_GET: return "get";
-	case DEVLINK_CMD_PARAM_SET: return "set";
-	case DEVLINK_CMD_PARAM_NEW: return "new";
-	case DEVLINK_CMD_PARAM_DEL: return "del";
-	case DEVLINK_CMD_REGION_GET: return "get";
-	case DEVLINK_CMD_REGION_SET: return "set";
-	case DEVLINK_CMD_REGION_NEW: return "new";
-	case DEVLINK_CMD_REGION_DEL: return "del";
-	case DEVLINK_CMD_FLASH_UPDATE: return "begin";
-	case DEVLINK_CMD_FLASH_UPDATE_END: return "end";
-	case DEVLINK_CMD_FLASH_UPDATE_STATUS: return "status";
-	case DEVLINK_CMD_TRAP_GET: return "get";
-	case DEVLINK_CMD_TRAP_SET: return "set";
-	case DEVLINK_CMD_TRAP_NEW: return "new";
-	case DEVLINK_CMD_TRAP_DEL: return "del";
-	case DEVLINK_CMD_TRAP_GROUP_GET: return "get";
-	case DEVLINK_CMD_TRAP_GROUP_SET: return "set";
-	case DEVLINK_CMD_TRAP_GROUP_NEW: return "new";
-	case DEVLINK_CMD_TRAP_GROUP_DEL: return "del";
-	default: return "<unknown cmd>";
-	}
-}
-
-static const char *cmd_obj(uint8_t cmd)
-{
-	switch (cmd) {
-	case DEVLINK_CMD_UNSPEC: return "unspec";
-	case DEVLINK_CMD_GET:
-	case DEVLINK_CMD_SET:
-	case DEVLINK_CMD_NEW:
-	case DEVLINK_CMD_DEL:
-		return "dev";
-	case DEVLINK_CMD_PORT_GET:
-	case DEVLINK_CMD_PORT_SET:
-	case DEVLINK_CMD_PORT_NEW:
-	case DEVLINK_CMD_PORT_DEL:
-		return "port";
-	case DEVLINK_CMD_PARAM_GET:
-	case DEVLINK_CMD_PARAM_SET:
-	case DEVLINK_CMD_PARAM_NEW:
-	case DEVLINK_CMD_PARAM_DEL:
-		return "param";
-	case DEVLINK_CMD_REGION_GET:
-	case DEVLINK_CMD_REGION_SET:
-	case DEVLINK_CMD_REGION_NEW:
-	case DEVLINK_CMD_REGION_DEL:
-		return "region";
-	case DEVLINK_CMD_FLASH_UPDATE:
-	case DEVLINK_CMD_FLASH_UPDATE_END:
-	case DEVLINK_CMD_FLASH_UPDATE_STATUS:
-		return "flash";
-	case DEVLINK_CMD_TRAP_GET:
-	case DEVLINK_CMD_TRAP_SET:
-	case DEVLINK_CMD_TRAP_NEW:
-	case DEVLINK_CMD_TRAP_DEL:
-		return "trap";
-	case DEVLINK_CMD_TRAP_GROUP_GET:
-	case DEVLINK_CMD_TRAP_GROUP_SET:
-	case DEVLINK_CMD_TRAP_GROUP_NEW:
-	case DEVLINK_CMD_TRAP_GROUP_DEL:
-		return "trap-group";
-	default: return "<unknown obj>";
-	}
-}
-
-static void pr_out_mon_header(uint8_t cmd)
-{
-	pr_out("[%s,%s] ", cmd_obj(cmd), cmd_name(cmd));
-}
-
-static bool cmd_filter_check(struct dl *dl, uint8_t cmd)
-{
-	const char *obj = cmd_obj(cmd);
-	unsigned int index = 0;
-	const char *cur_obj;
-
-	if (dl_no_arg(dl))
-		return true;
-	while ((cur_obj = dl_argv_index(dl, index++))) {
-		if (strcmp(cur_obj, obj) == 0 || strcmp(cur_obj, "all") == 0)
-			return true;
-	}
-	return false;
-}
-
-static void pr_out_flash_update(struct dl *dl, struct nlattr **tb)
-{
-	__pr_out_handle_start(dl, tb, true, false);
-
-	if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG])
-		pr_out_str(dl, "msg",
-			   mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG]));
-
-	if (tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT])
-		pr_out_str(dl, "component",
-			   mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]));
-
-	if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE])
-		pr_out_u64(dl, "done",
-			   mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]));
-
-	if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL])
-		pr_out_u64(dl, "total",
-			   mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]));
-
-	pr_out_handle_end(dl);
-}
-
-static void pr_out_region(struct dl *dl, struct nlattr **tb);
-static void pr_out_trap(struct dl *dl, struct nlattr **tb, bool array);
-static void pr_out_trap_group(struct dl *dl, struct nlattr **tb, bool array);
-
-static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct dl *dl = data;
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	uint8_t cmd = genl->cmd;
-
-	if (!cmd_filter_check(dl, cmd))
-		return MNL_CB_OK;
-
-	switch (cmd) {
-	case DEVLINK_CMD_GET: /* fall through */
-	case DEVLINK_CMD_SET: /* fall through */
-	case DEVLINK_CMD_NEW: /* fall through */
-	case DEVLINK_CMD_DEL:
-		mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-		if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
-			return MNL_CB_ERROR;
-		pr_out_mon_header(genl->cmd);
-		pr_out_handle(dl, tb);
-		break;
-	case DEVLINK_CMD_PORT_GET: /* fall through */
-	case DEVLINK_CMD_PORT_SET: /* fall through */
-	case DEVLINK_CMD_PORT_NEW: /* fall through */
-	case DEVLINK_CMD_PORT_DEL:
-		mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-		if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-		    !tb[DEVLINK_ATTR_PORT_INDEX])
-			return MNL_CB_ERROR;
-		pr_out_mon_header(genl->cmd);
-		pr_out_port(dl, tb);
-		break;
-	case DEVLINK_CMD_PARAM_GET: /* fall through */
-	case DEVLINK_CMD_PARAM_SET: /* fall through */
-	case DEVLINK_CMD_PARAM_NEW: /* fall through */
-	case DEVLINK_CMD_PARAM_DEL:
-		mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-		if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-		    !tb[DEVLINK_ATTR_PARAM])
-			return MNL_CB_ERROR;
-		pr_out_mon_header(genl->cmd);
-		pr_out_param(dl, tb, false);
-		break;
-	case DEVLINK_CMD_REGION_GET: /* fall through */
-	case DEVLINK_CMD_REGION_SET: /* fall through */
-	case DEVLINK_CMD_REGION_NEW: /* fall through */
-	case DEVLINK_CMD_REGION_DEL:
-		mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-		if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-		    !tb[DEVLINK_ATTR_REGION_NAME])
-			return MNL_CB_ERROR;
-		pr_out_mon_header(genl->cmd);
-		pr_out_region(dl, tb);
-		break;
-	case DEVLINK_CMD_FLASH_UPDATE: /* fall through */
-	case DEVLINK_CMD_FLASH_UPDATE_END: /* fall through */
-	case DEVLINK_CMD_FLASH_UPDATE_STATUS:
-		mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-		if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
-			return MNL_CB_ERROR;
-		pr_out_mon_header(genl->cmd);
-		pr_out_flash_update(dl, tb);
-		break;
-	case DEVLINK_CMD_TRAP_GET: /* fall through */
-	case DEVLINK_CMD_TRAP_SET: /* fall through */
-	case DEVLINK_CMD_TRAP_NEW: /* fall through */
-	case DEVLINK_CMD_TRAP_DEL:
-		mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-		if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-		    !tb[DEVLINK_ATTR_TRAP_NAME] ||
-		    !tb[DEVLINK_ATTR_TRAP_TYPE] ||
-		    !tb[DEVLINK_ATTR_TRAP_ACTION] ||
-		    !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] ||
-		    !tb[DEVLINK_ATTR_TRAP_METADATA] ||
-		    !tb[DEVLINK_ATTR_STATS])
-			return MNL_CB_ERROR;
-		pr_out_mon_header(genl->cmd);
-		pr_out_trap(dl, tb, false);
-		break;
-	case DEVLINK_CMD_TRAP_GROUP_GET: /* fall through */
-	case DEVLINK_CMD_TRAP_GROUP_SET: /* fall through */
-	case DEVLINK_CMD_TRAP_GROUP_NEW: /* fall through */
-	case DEVLINK_CMD_TRAP_GROUP_DEL:
-		mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-		if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-		    !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] ||
-		    !tb[DEVLINK_ATTR_STATS])
-			return MNL_CB_ERROR;
-		pr_out_mon_header(genl->cmd);
-		pr_out_trap_group(dl, tb, false);
-		break;
-	}
-	return MNL_CB_OK;
-}
-
-static int cmd_mon_show(struct dl *dl)
-{
-	int err;
-	unsigned int index = 0;
-	const char *cur_obj;
-
-	while ((cur_obj = dl_argv_index(dl, index++))) {
-		if (strcmp(cur_obj, "all") != 0 &&
-		    strcmp(cur_obj, "dev") != 0 &&
-		    strcmp(cur_obj, "port") != 0 &&
-		    strcmp(cur_obj, "trap") != 0 &&
-		    strcmp(cur_obj, "trap-group") != 0) {
-			pr_err("Unknown object \"%s\"\n", cur_obj);
-			return -EINVAL;
-		}
-	}
-	err = _mnlg_socket_group_add(dl->nlg, DEVLINK_GENL_MCGRP_CONFIG_NAME);
-	if (err)
-		return err;
-	err = _mnlg_socket_recv_run(dl->nlg, cmd_mon_show_cb, dl);
-	if (err)
-		return err;
-	return 0;
-}
-
-static void cmd_mon_help(void)
-{
-	pr_err("Usage: devlink monitor [ all | OBJECT-LIST ]\n"
-	       "where  OBJECT-LIST := { dev | port | trap | trap-group }\n");
-}
-
-static int cmd_mon(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help")) {
-		cmd_mon_help();
-		return 0;
-	}
-	return cmd_mon_show(dl);
-}
-
-struct dpipe_field {
-	char *name;
-	unsigned int id;
-	unsigned int bitwidth;
-	enum devlink_dpipe_field_mapping_type mapping_type;
-};
-
-struct dpipe_header {
-	struct list_head list;
-	char *name;
-	unsigned int id;
-	struct dpipe_field *fields;
-	unsigned int fields_count;
-};
-
-struct dpipe_table {
-	struct list_head list;
-	char *name;
-	unsigned int resource_id;
-	bool resource_valid;
-};
-
-struct dpipe_tables {
-	struct list_head table_list;
-};
-
-struct resource {
-	char *name;
-	uint64_t size;
-	uint64_t size_new;
-	uint64_t size_min;
-	uint64_t size_max;
-	uint64_t size_gran;
-	enum devlink_resource_unit unit;
-	bool size_valid;
-	uint64_t size_occ;
-	bool occ_valid;
-	uint64_t id;
-	struct list_head list;
-	struct list_head resource_list;
-	struct resource *parent;
-};
-
-struct resources {
-	struct list_head resource_list;
-};
-
-struct resource_ctx {
-	struct dl *dl;
-	int err;
-	struct resources *resources;
-	struct dpipe_tables *tables;
-	bool print_resources;
-	bool pending_change;
-};
-
-static struct resource *resource_alloc(void)
-{
-	struct resource *resource;
-
-	resource = calloc(1, sizeof(struct resource));
-	if (!resource)
-		return NULL;
-	INIT_LIST_HEAD(&resource->resource_list);
-	return resource;
-}
-
-static void resource_free(struct resource *resource)
-{
-	struct resource *child_resource, *tmp;
-
-	list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
-				 list) {
-		free(child_resource->name);
-		resource_free(child_resource);
-	}
-	free(resource);
-}
-
-static struct resources *resources_alloc(void)
-{
-	struct resources *resources;
-
-	resources = calloc(1, sizeof(struct resources));
-	if (!resources)
-		return NULL;
-	INIT_LIST_HEAD(&resources->resource_list);
-	return resources;
-}
-
-static void resources_free(struct resources *resources)
-{
-	struct resource *resource, *tmp;
-
-	list_for_each_entry_safe(resource, tmp, &resources->resource_list, list)
-		resource_free(resource);
-}
-
-static int resource_ctx_init(struct resource_ctx *ctx, struct dl *dl)
-{
-	ctx->resources = resources_alloc();
-	if (!ctx->resources)
-		return -ENOMEM;
-	ctx->dl = dl;
-	return 0;
-}
-
-static void resource_ctx_fini(struct resource_ctx *ctx)
-{
-	resources_free(ctx->resources);
-}
-
-struct dpipe_ctx {
-	struct dl *dl;
-	int err;
-	struct list_head global_headers;
-	struct list_head local_headers;
-	struct dpipe_tables *tables;
-	struct resources *resources;
-	bool print_headers;
-	bool print_tables;
-};
-
-static struct dpipe_header *dpipe_header_alloc(unsigned int fields_count)
-{
-	struct dpipe_header *header;
-
-	header = calloc(1, sizeof(struct dpipe_header));
-	if (!header)
-		return NULL;
-	header->fields = calloc(fields_count, sizeof(struct dpipe_field));
-	if (!header->fields)
-		goto err_fields_alloc;
-	header->fields_count = fields_count;
-	return header;
-
-err_fields_alloc:
-	free(header);
-	return NULL;
-}
-
-static void dpipe_header_free(struct dpipe_header *header)
-{
-	free(header->fields);
-	free(header);
-}
-
-static void dpipe_header_clear(struct dpipe_header *header)
-{
-	struct dpipe_field *field;
-	int i;
-
-	for (i = 0; i < header->fields_count; i++) {
-		field = &header->fields[i];
-		free(field->name);
-	}
-	free(header->name);
-}
-
-static void dpipe_header_add(struct dpipe_ctx *ctx,
-			     struct dpipe_header *header, bool global)
-{
-	if (global)
-		list_add(&header->list, &ctx->global_headers);
-	else
-		list_add(&header->list, &ctx->local_headers);
-}
-
-static void dpipe_header_del(struct dpipe_header *header)
-{
-	list_del(&header->list);
-}
-
-static struct dpipe_table *dpipe_table_alloc(void)
-{
-	return calloc(1, sizeof(struct dpipe_table));
-}
-
-static void dpipe_table_free(struct dpipe_table *table)
-{
-	free(table);
-}
-
-static struct dpipe_tables *dpipe_tables_alloc(void)
-{
-	struct dpipe_tables *tables;
-
-	tables = calloc(1, sizeof(struct dpipe_tables));
-	if (!tables)
-		return NULL;
-	INIT_LIST_HEAD(&tables->table_list);
-	return tables;
-}
-
-static void dpipe_tables_free(struct dpipe_tables *tables)
-{
-	struct dpipe_table *table, *tmp;
-
-	list_for_each_entry_safe(table, tmp, &tables->table_list, list)
-		dpipe_table_free(table);
-	free(tables);
-}
-
-static int dpipe_ctx_init(struct dpipe_ctx *ctx, struct dl *dl)
-{
-	ctx->tables = dpipe_tables_alloc();
-	if (!ctx->tables)
-		return -ENOMEM;
-
-	ctx->dl = dl;
-	INIT_LIST_HEAD(&ctx->global_headers);
-	INIT_LIST_HEAD(&ctx->local_headers);
-	return 0;
-}
-
-static void dpipe_ctx_fini(struct dpipe_ctx *ctx)
-{
-	struct dpipe_header *header, *tmp;
-
-	list_for_each_entry_safe(header, tmp, &ctx->global_headers,
-				 list) {
-		dpipe_header_del(header);
-		dpipe_header_clear(header);
-		dpipe_header_free(header);
-	}
-	list_for_each_entry_safe(header, tmp, &ctx->local_headers,
-				 list) {
-		dpipe_header_del(header);
-		dpipe_header_clear(header);
-		dpipe_header_free(header);
-	}
-	dpipe_tables_free(ctx->tables);
-}
-
-static const char *dpipe_header_id2s(struct dpipe_ctx *ctx,
-				     uint32_t header_id, bool global)
-{
-	struct list_head *header_list;
-	struct dpipe_header *header;
-
-	if (global)
-		header_list = &ctx->global_headers;
-	else
-		header_list = &ctx->local_headers;
-	list_for_each_entry(header, header_list, list) {
-		if (header->id != header_id)
-			continue;
-		return header->name;
-	}
-	return NULL;
-}
-
-static const char *dpipe_field_id2s(struct dpipe_ctx *ctx,
-				    uint32_t header_id,
-				    uint32_t field_id, bool global)
-{
-	struct list_head *header_list;
-	struct dpipe_header *header;
-
-	if (global)
-		header_list = &ctx->global_headers;
-	else
-		header_list = &ctx->local_headers;
-	list_for_each_entry(header, header_list, list) {
-		if (header->id != header_id)
-			continue;
-		return header->fields[field_id].name;
-	}
-	return NULL;
-}
-
-static const char *
-dpipe_field_mapping_e2s(enum devlink_dpipe_field_mapping_type mapping_type)
-{
-	switch (mapping_type) {
-	case DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE:
-		return NULL;
-	case DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX:
-		return "ifindex";
-	default:
-		return "<unknown>";
-	}
-}
-
-static const char *
-dpipe_mapping_get(struct dpipe_ctx *ctx, uint32_t header_id,
-		  uint32_t field_id, bool global)
-{
-	enum devlink_dpipe_field_mapping_type mapping_type;
-	struct list_head *header_list;
-	struct dpipe_header *header;
-
-	if (global)
-		header_list = &ctx->global_headers;
-	else
-		header_list = &ctx->local_headers;
-	list_for_each_entry(header, header_list, list) {
-		if (header->id != header_id)
-			continue;
-		mapping_type = header->fields[field_id].mapping_type;
-		return dpipe_field_mapping_e2s(mapping_type);
-	}
-	return NULL;
-}
-
-static void pr_out_dpipe_fields(struct dpipe_ctx *ctx,
-				struct dpipe_field *fields,
-				unsigned int field_count)
-{
-	struct dpipe_field *field;
-	int i;
-
-	for (i = 0; i < field_count; i++) {
-		field = &fields[i];
-		pr_out_entry_start(ctx->dl);
-		pr_out_str(ctx->dl, "name", field->name);
-		if (ctx->dl->verbose)
-			pr_out_uint(ctx->dl, "id", field->id);
-		pr_out_uint(ctx->dl, "bitwidth", field->bitwidth);
-		if (field->mapping_type)
-			pr_out_str(ctx->dl, "mapping_type",
-				   dpipe_field_mapping_e2s(field->mapping_type));
-		pr_out_entry_end(ctx->dl);
-	}
-}
-
-static void
-pr_out_dpipe_header(struct dpipe_ctx *ctx, struct nlattr **tb,
-		    struct dpipe_header *header, bool global)
-{
-	pr_out_handle_start_arr(ctx->dl, tb);
-	pr_out_str(ctx->dl, "name", header->name);
-	if (ctx->dl->verbose) {
-		pr_out_uint(ctx->dl, "id", header->id);
-		pr_out_str(ctx->dl, "global",
-			   global ? "true" : "false");
-	}
-	pr_out_array_start(ctx->dl, "field");
-	pr_out_dpipe_fields(ctx, header->fields,
-			    header->fields_count);
-	pr_out_array_end(ctx->dl);
-	pr_out_handle_end(ctx->dl);
-}
-
-static void pr_out_dpipe_headers(struct dpipe_ctx *ctx,
-				 struct nlattr **tb)
-{
-	struct dpipe_header *header;
-
-	list_for_each_entry(header, &ctx->local_headers, list)
-		pr_out_dpipe_header(ctx, tb, header, false);
-
-	list_for_each_entry(header, &ctx->global_headers, list)
-		pr_out_dpipe_header(ctx, tb, header, true);
-}
-
-static int dpipe_header_field_get(struct nlattr *nl, struct dpipe_field *field)
-{
-	struct nlattr *nla_field[DEVLINK_ATTR_MAX + 1] = {};
-	const char *name;
-	int err;
-
-	err = mnl_attr_parse_nested(nl, attr_cb, nla_field);
-	if (err != MNL_CB_OK)
-		return -EINVAL;
-	if (!nla_field[DEVLINK_ATTR_DPIPE_FIELD_ID] ||
-	    !nla_field[DEVLINK_ATTR_DPIPE_FIELD_NAME] ||
-	    !nla_field[DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH] ||
-	    !nla_field[DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE])
-		return -EINVAL;
-
-	name = mnl_attr_get_str(nla_field[DEVLINK_ATTR_DPIPE_FIELD_NAME]);
-	field->id = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_ID]);
-	field->bitwidth = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH]);
-	field->name = strdup(name);
-	if (!field->name)
-		return -ENOMEM;
-	field->mapping_type = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE]);
-	return 0;
-}
-
-static int dpipe_header_fields_get(struct nlattr *nla_fields,
-				   struct dpipe_field *fields)
-{
-	struct nlattr *nla_field;
-	int count = 0;
-	int err;
-
-	mnl_attr_for_each_nested(nla_field, nla_fields) {
-		err = dpipe_header_field_get(nla_field, &fields[count]);
-		if (err)
-			return err;
-		count++;
-	}
-	return 0;
-}
-
-static unsigned int dpipe_header_field_count_get(struct nlattr *nla_fields)
-{
-	struct nlattr *nla_field;
-	unsigned int count = 0;
-
-	mnl_attr_for_each_nested(nla_field, nla_fields)
-		count++;
-	return count;
-}
-
-static int dpipe_header_get(struct dpipe_ctx *ctx, struct nlattr *nl)
-{
-	struct nlattr *nla_header[DEVLINK_ATTR_MAX + 1] = {};
-	struct dpipe_header *header;
-	unsigned int fields_count;
-	const char *header_name;
-	bool global;
-	int err;
-
-	err = mnl_attr_parse_nested(nl, attr_cb, nla_header);
-	if (err != MNL_CB_OK)
-		return -EINVAL;
-
-	if (!nla_header[DEVLINK_ATTR_DPIPE_HEADER_NAME] ||
-	    !nla_header[DEVLINK_ATTR_DPIPE_HEADER_ID] ||
-	    !nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS])
-		return -EINVAL;
-
-	fields_count = dpipe_header_field_count_get(nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS]);
-	header = dpipe_header_alloc(fields_count);
-	if (!header)
-		return -ENOMEM;
-
-	header_name = mnl_attr_get_str(nla_header[DEVLINK_ATTR_DPIPE_HEADER_NAME]);
-	header->name = strdup(header_name);
-	header->id = mnl_attr_get_u32(nla_header[DEVLINK_ATTR_DPIPE_HEADER_ID]);
-	header->fields_count = fields_count;
-	global = !!mnl_attr_get_u8(nla_header[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]);
-
-	err = dpipe_header_fields_get(nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS],
-				      header->fields);
-	if (err)
-		goto err_field_get;
-	dpipe_header_add(ctx, header, global);
-	return 0;
-
-err_field_get:
-	dpipe_header_free(header);
-	return err;
-}
-
-static int dpipe_headers_get(struct dpipe_ctx *ctx, struct nlattr **tb)
-{
-	struct nlattr *nla_headers = tb[DEVLINK_ATTR_DPIPE_HEADERS];
-	struct nlattr *nla_header;
-	int err;
-
-	mnl_attr_for_each_nested(nla_header, nla_headers) {
-		err = dpipe_header_get(ctx, nla_header);
-		if (err)
-			return err;
-	}
-	return 0;
-}
-
-static int cmd_dpipe_header_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct dpipe_ctx *ctx = data;
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	int err;
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_DPIPE_HEADERS])
-		return MNL_CB_ERROR;
-	err = dpipe_headers_get(ctx, tb);
-	if (err) {
-		ctx->err = err;
-		return MNL_CB_ERROR;
-	}
-
-	if (ctx->print_headers)
-		pr_out_dpipe_headers(ctx, tb);
-	return MNL_CB_OK;
-}
-
-static int cmd_dpipe_headers_show(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	struct dpipe_ctx ctx = {};
-	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0);
-	if (err)
-		return err;
-
-	err = dpipe_ctx_init(&ctx, dl);
-	if (err)
-		return err;
-
-	ctx.print_headers = true;
-
-	pr_out_section_start(dl, "header");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, &ctx);
-	if (err)
-		pr_err("error get headers %s\n", strerror(ctx.err));
-	pr_out_section_end(dl);
-
-	dpipe_ctx_fini(&ctx);
-	return err;
-}
-
-static void cmd_dpipe_header_help(void)
-{
-	pr_err("Usage: devlink dpipe headers show DEV\n");
-}
-
-static int cmd_dpipe_header(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
-		cmd_dpipe_header_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show")) {
-		dl_arg_inc(dl);
-		return cmd_dpipe_headers_show(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static const char
-*dpipe_action_type_e2s(enum devlink_dpipe_action_type action_type)
-{
-	switch (action_type) {
-	case DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY:
-		return "field_modify";
-	default:
-		return "<unknown>";
-	}
-}
-
-struct dpipe_op_info {
-	uint32_t header_id;
-	uint32_t field_id;
-	bool header_global;
-};
-
-struct dpipe_action {
-	struct dpipe_op_info info;
-	uint32_t type;
-};
-
-static void pr_out_dpipe_action(struct dpipe_action *action,
-				struct dpipe_ctx *ctx)
-{
-	struct dpipe_op_info *op_info = &action->info;
-	const char *mapping;
-
-	pr_out_str(ctx->dl, "type",
-		   dpipe_action_type_e2s(action->type));
-	pr_out_str(ctx->dl, "header",
-		   dpipe_header_id2s(ctx, op_info->header_id,
-				     op_info->header_global));
-	pr_out_str(ctx->dl, "field",
-		   dpipe_field_id2s(ctx, op_info->header_id,
-				    op_info->field_id,
-				    op_info->header_global));
-	mapping = dpipe_mapping_get(ctx, op_info->header_id,
-				    op_info->field_id,
-				    op_info->header_global);
-	if (mapping)
-		pr_out_str(ctx->dl, "mapping", mapping);
-}
-
-static int dpipe_action_parse(struct dpipe_action *action, struct nlattr *nl)
-{
-	struct nlattr *nla_action[DEVLINK_ATTR_MAX + 1] = {};
-	int err;
-
-	err = mnl_attr_parse_nested(nl, attr_cb, nla_action);
-	if (err != MNL_CB_OK)
-		return -EINVAL;
-
-	if (!nla_action[DEVLINK_ATTR_DPIPE_ACTION_TYPE] ||
-	    !nla_action[DEVLINK_ATTR_DPIPE_HEADER_INDEX] ||
-	    !nla_action[DEVLINK_ATTR_DPIPE_HEADER_ID] ||
-	    !nla_action[DEVLINK_ATTR_DPIPE_FIELD_ID]) {
-		return -EINVAL;
-	}
-
-	action->type = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_ACTION_TYPE]);
-	action->info.header_id = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_HEADER_ID]);
-	action->info.field_id = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_FIELD_ID]);
-	action->info.header_global = !!mnl_attr_get_u8(nla_action[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]);
-
-	return 0;
-}
-
-static int dpipe_table_actions_show(struct dpipe_ctx *ctx,
-				    struct nlattr *nla_actions)
-{
-	struct nlattr *nla_action;
-	struct dpipe_action action;
-
-	mnl_attr_for_each_nested(nla_action, nla_actions) {
-		pr_out_entry_start(ctx->dl);
-		if (dpipe_action_parse(&action, nla_action))
-			goto err_action_parse;
-		pr_out_dpipe_action(&action, ctx);
-		pr_out_entry_end(ctx->dl);
-	}
-	return 0;
-
-err_action_parse:
-	pr_out_entry_end(ctx->dl);
-	return -EINVAL;
-}
-
-static const char *
-dpipe_match_type_e2s(enum devlink_dpipe_match_type match_type)
-{
-	switch (match_type) {
-	case DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT:
-		return "field_exact";
-	default:
-		return "<unknown>";
-	}
-}
-
-struct dpipe_match {
-	struct dpipe_op_info info;
-	uint32_t type;
-};
-
-static void pr_out_dpipe_match(struct dpipe_match *match,
-			       struct dpipe_ctx *ctx)
-{
-	struct dpipe_op_info *op_info = &match->info;
-	const char *mapping;
-
-	pr_out_str(ctx->dl, "type",
-		   dpipe_match_type_e2s(match->type));
-	pr_out_str(ctx->dl, "header",
-		   dpipe_header_id2s(ctx, op_info->header_id,
-				     op_info->header_global));
-	pr_out_str(ctx->dl, "field",
-		   dpipe_field_id2s(ctx, op_info->header_id,
-				    op_info->field_id,
-				    op_info->header_global));
-	mapping = dpipe_mapping_get(ctx, op_info->header_id,
-				    op_info->field_id,
-				    op_info->header_global);
-	if (mapping)
-		pr_out_str(ctx->dl, "mapping", mapping);
-}
-
-static int dpipe_match_parse(struct dpipe_match *match,
-			     struct nlattr *nl)
-
-{
-	struct nlattr *nla_match[DEVLINK_ATTR_MAX + 1] = {};
-	int err;
-
-	err = mnl_attr_parse_nested(nl, attr_cb, nla_match);
-	if (err != MNL_CB_OK)
-		return -EINVAL;
-
-	if (!nla_match[DEVLINK_ATTR_DPIPE_MATCH_TYPE] ||
-	    !nla_match[DEVLINK_ATTR_DPIPE_HEADER_INDEX] ||
-	    !nla_match[DEVLINK_ATTR_DPIPE_HEADER_ID] ||
-	    !nla_match[DEVLINK_ATTR_DPIPE_FIELD_ID]) {
-		return -EINVAL;
-	}
-
-	match->type = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_MATCH_TYPE]);
-	match->info.header_id = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_HEADER_ID]);
-	match->info.field_id = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_FIELD_ID]);
-	match->info.header_global = !!mnl_attr_get_u8(nla_match[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]);
-
-	return 0;
-}
-
-static int dpipe_table_matches_show(struct dpipe_ctx *ctx,
-				    struct nlattr *nla_matches)
-{
-	struct nlattr *nla_match;
-	struct dpipe_match match;
-
-	mnl_attr_for_each_nested(nla_match, nla_matches) {
-		pr_out_entry_start(ctx->dl);
-		if (dpipe_match_parse(&match, nla_match))
-			goto err_match_parse;
-		pr_out_dpipe_match(&match, ctx);
-		pr_out_entry_end(ctx->dl);
-	}
-	return 0;
-
-err_match_parse:
-	pr_out_entry_end(ctx->dl);
-	return -EINVAL;
-}
-
-static struct resource *
-resource_find(struct resources *resources, struct resource *resource,
-	      uint64_t resource_id)
-{
-	struct list_head *list_head;
-
-	if (!resource)
-		list_head = &resources->resource_list;
-	else
-		list_head = &resource->resource_list;
-
-	list_for_each_entry(resource, list_head, list) {
-		struct resource *child_resource;
-
-		if (resource->id == resource_id)
-			return resource;
-
-		child_resource = resource_find(resources, resource,
-					       resource_id);
-		if (child_resource)
-			return child_resource;
-	}
-	return NULL;
-}
-
-static void
-resource_path_print(struct dl *dl, struct resources *resources,
-		    uint64_t resource_id)
-{
-	struct resource *resource, *parent_resource;
-	const char del[] = "/";
-	int path_len = 0;
-	char *path;
-
-	resource = resource_find(resources, NULL, resource_id);
-	if (!resource)
-		return;
-
-	for (parent_resource = resource; parent_resource;
-	     parent_resource = parent_resource->parent)
-		path_len += strlen(parent_resource->name) + 1;
-
-	path_len++;
-	path = calloc(1, path_len);
-	if (!path)
-		return;
-
-	path += path_len - 1;
-	for (parent_resource = resource; parent_resource;
-		parent_resource = parent_resource->parent) {
-		path -= strlen(parent_resource->name);
-		memcpy(path, parent_resource->name,
-		       strlen(parent_resource->name));
-		path -= strlen(del);
-		memcpy(path, del, strlen(del));
-	}
-	pr_out_str(dl, "resource_path", path);
-	free(path);
-}
-
-static int dpipe_table_show(struct dpipe_ctx *ctx, struct nlattr *nl)
-{
-	struct nlattr *nla_table[DEVLINK_ATTR_MAX + 1] = {};
-	struct dpipe_table *table;
-	uint32_t resource_units;
-	bool counters_enabled;
-	bool resource_valid;
-	uint32_t size;
-	int err;
-
-	err = mnl_attr_parse_nested(nl, attr_cb, nla_table);
-	if (err != MNL_CB_OK)
-		return -EINVAL;
-
-	if (!nla_table[DEVLINK_ATTR_DPIPE_TABLE_NAME] ||
-	    !nla_table[DEVLINK_ATTR_DPIPE_TABLE_SIZE] ||
-	    !nla_table[DEVLINK_ATTR_DPIPE_TABLE_ACTIONS] ||
-	    !nla_table[DEVLINK_ATTR_DPIPE_TABLE_MATCHES] ||
-	    !nla_table[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]) {
-		return -EINVAL;
-	}
-
-	table = dpipe_table_alloc();
-	if (!table)
-		return -ENOMEM;
-
-	table->name = strdup(mnl_attr_get_str(nla_table[DEVLINK_ATTR_DPIPE_TABLE_NAME]));
-	size = mnl_attr_get_u32(nla_table[DEVLINK_ATTR_DPIPE_TABLE_SIZE]);
-	counters_enabled = !!mnl_attr_get_u8(nla_table[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]);
-
-	resource_valid = nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID] &&
-			 ctx->resources;
-	if (resource_valid) {
-		table->resource_id = mnl_attr_get_u64(nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID]);
-		table->resource_valid = true;
-	}
-
-	list_add_tail(&table->list, &ctx->tables->table_list);
-	if (!ctx->print_tables)
-		return 0;
-
-	pr_out_str(ctx->dl, "name", table->name);
-	pr_out_uint(ctx->dl, "size", size);
-	pr_out_str(ctx->dl, "counters_enabled",
-		   counters_enabled ? "true" : "false");
-
-	if (resource_valid) {
-		resource_units = mnl_attr_get_u32(nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS]);
-		resource_path_print(ctx->dl, ctx->resources,
-				    table->resource_id);
-		pr_out_uint(ctx->dl, "resource_units", resource_units);
-	}
-
-	pr_out_array_start(ctx->dl, "match");
-	if (dpipe_table_matches_show(ctx, nla_table[DEVLINK_ATTR_DPIPE_TABLE_MATCHES]))
-		goto err_matches_show;
-	pr_out_array_end(ctx->dl);
-
-	pr_out_array_start(ctx->dl, "action");
-	if (dpipe_table_actions_show(ctx, nla_table[DEVLINK_ATTR_DPIPE_TABLE_ACTIONS]))
-		goto err_actions_show;
-	pr_out_array_end(ctx->dl);
-
-	return 0;
-
-err_actions_show:
-err_matches_show:
-	pr_out_array_end(ctx->dl);
-	return -EINVAL;
-}
-
-static int dpipe_tables_show(struct dpipe_ctx *ctx, struct nlattr **tb)
-{
-	struct nlattr *nla_tables = tb[DEVLINK_ATTR_DPIPE_TABLES];
-	struct nlattr *nla_table;
-
-	mnl_attr_for_each_nested(nla_table, nla_tables) {
-		if (ctx->print_tables)
-			pr_out_handle_start_arr(ctx->dl, tb);
-		if (dpipe_table_show(ctx, nla_table))
-			goto err_table_show;
-		if (ctx->print_tables)
-			pr_out_handle_end(ctx->dl);
-	}
-	return 0;
-
-err_table_show:
-	if (ctx->print_tables)
-		pr_out_handle_end(ctx->dl);
-	return -EINVAL;
-}
-
-static int cmd_dpipe_table_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct dpipe_ctx *ctx = data;
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_DPIPE_TABLES])
-		return MNL_CB_ERROR;
-
-	if (dpipe_tables_show(ctx, tb))
-		return MNL_CB_ERROR;
-	return MNL_CB_OK;
-}
-
-static int cmd_resource_dump_cb(const struct nlmsghdr *nlh, void *data);
-
-static int cmd_dpipe_table_show(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	struct dpipe_ctx dpipe_ctx = {};
-	struct resource_ctx resource_ctx = {};
-	uint16_t flags = NLM_F_REQUEST;
-	int err;
-
-	err = dl_argv_parse(dl, DL_OPT_HANDLE, DL_OPT_DPIPE_TABLE_NAME);
-	if (err)
-		return err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags);
-
-	err = dpipe_ctx_init(&dpipe_ctx, dl);
-	if (err)
-		return err;
-
-	dpipe_ctx.print_tables = true;
-
-	dl_opts_put(nlh, dl);
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb,
-				  &dpipe_ctx);
-	if (err) {
-		pr_err("error get headers %s\n", strerror(dpipe_ctx.err));
-		goto err_headers_get;
-	}
-
-	err = resource_ctx_init(&resource_ctx, dl);
-	if (err)
-		goto err_resource_ctx_init;
-
-	resource_ctx.print_resources = false;
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP, flags);
-	dl_opts_put(nlh, dl);
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb,
-				  &resource_ctx);
-	if (!err)
-		dpipe_ctx.resources = resource_ctx.resources;
-
-	flags = NLM_F_REQUEST | NLM_F_ACK;
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_GET, flags);
-	dl_opts_put(nlh, dl);
-
-	pr_out_section_start(dl, "table");
-	_mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_show_cb, &dpipe_ctx);
-	pr_out_section_end(dl);
-
-	resource_ctx_fini(&resource_ctx);
-	dpipe_ctx_fini(&dpipe_ctx);
-	return 0;
-
-err_resource_ctx_init:
-err_headers_get:
-	dpipe_ctx_fini(&dpipe_ctx);
-	return err;
-}
-
-static int cmd_dpipe_table_set(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl,
-				DL_OPT_HANDLE | DL_OPT_DPIPE_TABLE_NAME |
-				DL_OPT_DPIPE_TABLE_COUNTERS, 0);
-	if (err)
-		return err;
-
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-enum dpipe_value_type {
-	DPIPE_VALUE_TYPE_VALUE,
-	DPIPE_VALUE_TYPE_MASK,
-};
-
-static const char *
-dpipe_value_type_e2s(enum dpipe_value_type type)
-{
-	switch (type) {
-	case DPIPE_VALUE_TYPE_VALUE:
-		return "value";
-	case DPIPE_VALUE_TYPE_MASK:
-		return "value_mask";
-	default:
-		return "<unknown>";
-	}
-}
-
-struct dpipe_field_printer {
-	unsigned int field_id;
-	void (*printer)(struct dpipe_ctx *, enum dpipe_value_type, void *);
-};
-
-struct dpipe_header_printer {
-	struct dpipe_field_printer *printers;
-	unsigned int printers_count;
-	unsigned int header_id;
-};
-
-static void dpipe_field_printer_ipv4_addr(struct dpipe_ctx *ctx,
-					  enum dpipe_value_type type,
-					  void *value)
-{
-	struct in_addr ip_addr;
-
-	ip_addr.s_addr = htonl(*(uint32_t *)value);
-	pr_out_str(ctx->dl, dpipe_value_type_e2s(type), inet_ntoa(ip_addr));
-}
-
-static void
-dpipe_field_printer_ethernet_addr(struct dpipe_ctx *ctx,
-				  enum dpipe_value_type type,
-				  void *value)
-{
-	pr_out_str(ctx->dl, dpipe_value_type_e2s(type),
-		   ether_ntoa((struct ether_addr *)value));
-}
-
-static void dpipe_field_printer_ipv6_addr(struct dpipe_ctx *ctx,
-					  enum dpipe_value_type type,
-					  void *value)
-{
-	char str[INET6_ADDRSTRLEN];
-
-	inet_ntop(AF_INET6, value, str, INET6_ADDRSTRLEN);
-	pr_out_str(ctx->dl, dpipe_value_type_e2s(type), str);
-}
-
-static struct dpipe_field_printer dpipe_field_printers_ipv4[] = {
-	{
-		.printer = dpipe_field_printer_ipv4_addr,
-		.field_id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP,
-	}
-};
-
-static struct dpipe_header_printer dpipe_header_printer_ipv4  = {
-	.printers = dpipe_field_printers_ipv4,
-	.printers_count = ARRAY_SIZE(dpipe_field_printers_ipv4),
-	.header_id = DEVLINK_DPIPE_HEADER_IPV4,
-};
-
-static struct dpipe_field_printer dpipe_field_printers_ethernet[] = {
-	{
-		.printer = dpipe_field_printer_ethernet_addr,
-		.field_id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC,
-	},
-};
-
-static struct dpipe_header_printer dpipe_header_printer_ethernet = {
-	.printers = dpipe_field_printers_ethernet,
-	.printers_count = ARRAY_SIZE(dpipe_field_printers_ethernet),
-	.header_id = DEVLINK_DPIPE_HEADER_ETHERNET,
-};
-
-static struct dpipe_field_printer dpipe_field_printers_ipv6[] = {
-	{
-		.printer = dpipe_field_printer_ipv6_addr,
-		.field_id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP,
-	}
-};
-
-static struct dpipe_header_printer dpipe_header_printer_ipv6 = {
-	.printers = dpipe_field_printers_ipv6,
-	.printers_count = ARRAY_SIZE(dpipe_field_printers_ipv6),
-	.header_id = DEVLINK_DPIPE_HEADER_IPV6,
-};
-
-static struct dpipe_header_printer *dpipe_header_printers[] = {
-	&dpipe_header_printer_ipv4,
-	&dpipe_header_printer_ethernet,
-	&dpipe_header_printer_ipv6,
-};
-
-static int dpipe_print_prot_header(struct dpipe_ctx *ctx,
-				   struct dpipe_op_info *info,
-				   enum dpipe_value_type type,
-				   void *value)
-{
-	unsigned int header_printers_count = ARRAY_SIZE(dpipe_header_printers);
-	struct dpipe_header_printer *header_printer;
-	struct dpipe_field_printer *field_printer;
-	unsigned int field_printers_count;
-	int j;
-	int i;
-
-	for (i = 0; i < header_printers_count; i++) {
-		header_printer = dpipe_header_printers[i];
-		if (header_printer->header_id != info->header_id)
-			continue;
-		field_printers_count = header_printer->printers_count;
-		for (j = 0; j < field_printers_count; j++) {
-			field_printer = &header_printer->printers[j];
-			if (field_printer->field_id != info->field_id)
-				continue;
-			field_printer->printer(ctx, type, value);
-			return 0;
-		}
-	}
-
-	return -EINVAL;
-}
-
-static void __pr_out_entry_value(struct dpipe_ctx *ctx,
-				 void *value,
-				 unsigned int value_len,
-				 struct dpipe_op_info *info,
-				 enum dpipe_value_type type)
-{
-	if (info->header_global &&
-	    !dpipe_print_prot_header(ctx, info, type, value))
-		return;
-
-	if (value_len == sizeof(uint32_t)) {
-		uint32_t *value_32 = value;
-
-		pr_out_uint(ctx->dl, dpipe_value_type_e2s(type), *value_32);
-	}
-}
-
-static void pr_out_dpipe_entry_value(struct dpipe_ctx *ctx,
-				     struct nlattr **nla_match_value,
-				     struct dpipe_op_info *info)
-{
-	void *value, *value_mask;
-	uint32_t value_mapping;
-	uint16_t value_len;
-	bool mask, mapping;
-
-	mask = !!nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MASK];
-	mapping = !!nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MAPPING];
-
-	value_len = mnl_attr_get_payload_len(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]);
-	value = mnl_attr_get_payload(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]);
-
-	if (mapping) {
-		value_mapping = mnl_attr_get_u32(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MAPPING]);
-		pr_out_uint(ctx->dl, "mapping_value", value_mapping);
-	}
-
-	if (mask) {
-		value_mask = mnl_attr_get_payload(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]);
-		__pr_out_entry_value(ctx, value_mask, value_len, info,
-				     DPIPE_VALUE_TYPE_MASK);
-	}
-
-	__pr_out_entry_value(ctx, value, value_len, info, DPIPE_VALUE_TYPE_VALUE);
-}
-
-static int dpipe_entry_match_value_show(struct dpipe_ctx *ctx,
-					struct nlattr *nl)
-{
-	struct nlattr *nla_match_value[DEVLINK_ATTR_MAX + 1] = {};
-	struct dpipe_match match;
-	int err;
-
-	err = mnl_attr_parse_nested(nl, attr_cb, nla_match_value);
-	if (err != MNL_CB_OK)
-		return -EINVAL;
-
-	if (!nla_match_value[DEVLINK_ATTR_DPIPE_MATCH] ||
-	    !nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]) {
-		return -EINVAL;
-	}
-
-	pr_out_entry_start(ctx->dl);
-	if (dpipe_match_parse(&match,
-			      nla_match_value[DEVLINK_ATTR_DPIPE_MATCH]))
-		goto err_match_parse;
-	pr_out_dpipe_match(&match, ctx);
-	pr_out_dpipe_entry_value(ctx, nla_match_value, &match.info);
-	pr_out_entry_end(ctx->dl);
-
-	return 0;
-
-err_match_parse:
-	pr_out_entry_end(ctx->dl);
-	return -EINVAL;
-}
-
-static int dpipe_entry_action_value_show(struct dpipe_ctx *ctx,
-					 struct nlattr *nl)
-{
-	struct nlattr *nla_action_value[DEVLINK_ATTR_MAX + 1] = {};
-	struct dpipe_action action;
-	int err;
-
-	err = mnl_attr_parse_nested(nl, attr_cb, nla_action_value);
-	if (err != MNL_CB_OK)
-		return -EINVAL;
-
-	if (!nla_action_value[DEVLINK_ATTR_DPIPE_ACTION] ||
-	    !nla_action_value[DEVLINK_ATTR_DPIPE_VALUE]) {
-		return -EINVAL;
-	}
-
-	pr_out_entry_start(ctx->dl);
-	if (dpipe_action_parse(&action,
-			       nla_action_value[DEVLINK_ATTR_DPIPE_ACTION]))
-		goto err_action_parse;
-	pr_out_dpipe_action(&action, ctx);
-	pr_out_dpipe_entry_value(ctx, nla_action_value, &action.info);
-	pr_out_entry_end(ctx->dl);
-
-	return 0;
-
-err_action_parse:
-	pr_out_entry_end(ctx->dl);
-	return -EINVAL;
-}
-
-static int
-dpipe_tables_action_values_show(struct dpipe_ctx *ctx,
-				struct nlattr *nla_action_values)
-{
-	struct nlattr *nla_action_value;
-
-	mnl_attr_for_each_nested(nla_action_value, nla_action_values) {
-		if (dpipe_entry_action_value_show(ctx, nla_action_value))
-			return -EINVAL;
-	}
-	return 0;
-}
-
-static int
-dpipe_tables_match_values_show(struct dpipe_ctx *ctx,
-			       struct nlattr *nla_match_values)
-{
-	struct nlattr *nla_match_value;
-
-	mnl_attr_for_each_nested(nla_match_value, nla_match_values) {
-		if (dpipe_entry_match_value_show(ctx, nla_match_value))
-			return -EINVAL;
-	}
-	return 0;
-}
-
-static int dpipe_entry_show(struct dpipe_ctx *ctx, struct nlattr *nl)
-{
-	struct nlattr *nla_entry[DEVLINK_ATTR_MAX + 1] = {};
-	uint32_t entry_index;
-	uint64_t counter;
-	int err;
-
-	err = mnl_attr_parse_nested(nl, attr_cb, nla_entry);
-	if (err != MNL_CB_OK)
-		return -EINVAL;
-
-	if (!nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_INDEX] ||
-	    !nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES] ||
-	    !nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES]) {
-		return -EINVAL;
-	}
-
-	entry_index = mnl_attr_get_u32(nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_INDEX]);
-	pr_out_uint(ctx->dl, "index", entry_index);
-
-	if (nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER]) {
-		counter = mnl_attr_get_u64(nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER]);
-		pr_out_uint(ctx->dl, "counter", counter);
-	}
-
-	pr_out_array_start(ctx->dl, "match_value");
-	if (dpipe_tables_match_values_show(ctx,
-					   nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES]))
-		goto err_match_values_show;
-	pr_out_array_end(ctx->dl);
-
-	pr_out_array_start(ctx->dl, "action_value");
-	if (dpipe_tables_action_values_show(ctx,
-					    nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES]))
-		goto err_action_values_show;
-	pr_out_array_end(ctx->dl);
-	return 0;
-
-err_action_values_show:
-err_match_values_show:
-	pr_out_array_end(ctx->dl);
-	return -EINVAL;
-}
-
-static int dpipe_table_entries_show(struct dpipe_ctx *ctx, struct nlattr **tb)
-{
-	struct nlattr *nla_entries = tb[DEVLINK_ATTR_DPIPE_ENTRIES];
-	struct nlattr *nla_entry;
-
-	mnl_attr_for_each_nested(nla_entry, nla_entries) {
-		pr_out_handle_start_arr(ctx->dl, tb);
-		if (dpipe_entry_show(ctx, nla_entry))
-			goto err_entry_show;
-		pr_out_handle_end(ctx->dl);
-	}
-	return 0;
-
-err_entry_show:
-	pr_out_handle_end(ctx->dl);
-	return -EINVAL;
-}
-
-static int cmd_dpipe_table_entry_dump_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct dpipe_ctx *ctx = data;
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_DPIPE_ENTRIES])
-		return MNL_CB_ERROR;
-
-	if (dpipe_table_entries_show(ctx, tb))
-		return MNL_CB_ERROR;
-	return MNL_CB_OK;
-}
-
-static int cmd_dpipe_table_dump(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	struct dpipe_ctx ctx = {};
-	uint16_t flags = NLM_F_REQUEST;
-	int err;
-
-	err = dpipe_ctx_init(&ctx, dl);
-	if (err)
-		return err;
-
-	err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_DPIPE_TABLE_NAME, 0);
-	if (err)
-		goto out;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags);
-	dl_opts_put(nlh, dl);
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, &ctx);
-	if (err) {
-		pr_err("error get headers %s\n", strerror(ctx.err));
-		goto out;
-	}
-
-	flags = NLM_F_REQUEST | NLM_F_ACK;
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_ENTRIES_GET, flags);
-	dl_opts_put(nlh, dl);
-
-	pr_out_section_start(dl, "table_entry");
-	_mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_entry_dump_cb, &ctx);
-	pr_out_section_end(dl);
-out:
-	dpipe_ctx_fini(&ctx);
-	return err;
-}
-
-static void cmd_dpipe_table_help(void)
-{
-	pr_err("Usage: devlink dpipe table [ OBJECT-LIST ]\n"
-	       "where  OBJECT-LIST := { show | set | dump }\n");
-}
-
-static int cmd_dpipe_table(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
-		cmd_dpipe_table_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show")) {
-		dl_arg_inc(dl);
-		return cmd_dpipe_table_show(dl);
-	} else if (dl_argv_match(dl, "set")) {
-		dl_arg_inc(dl);
-		return cmd_dpipe_table_set(dl);
-	}  else if (dl_argv_match(dl, "dump")) {
-		dl_arg_inc(dl);
-		return cmd_dpipe_table_dump(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static void cmd_dpipe_help(void)
-{
-	pr_err("Usage: devlink dpipe [ OBJECT-LIST ]\n"
-	       "where  OBJECT-LIST := { header | table }\n");
-}
-
-static int cmd_dpipe(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
-		cmd_dpipe_help();
-		return 0;
-	} else if (dl_argv_match(dl, "header")) {
-		dl_arg_inc(dl);
-		return cmd_dpipe_header(dl);
-	} else if (dl_argv_match(dl, "table")) {
-		dl_arg_inc(dl);
-		return cmd_dpipe_table(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static int
-resource_parse(struct resource_ctx *ctx, struct resource *resource,
-	       struct nlattr **nla_resource)
-{
-	if (!nla_resource[DEVLINK_ATTR_RESOURCE_NAME] ||
-	    !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE] ||
-	    !nla_resource[DEVLINK_ATTR_RESOURCE_ID] ||
-	    !nla_resource[DEVLINK_ATTR_RESOURCE_UNIT] ||
-	    !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MIN] ||
-	    !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MAX] ||
-	    !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_GRAN]) {
-		return -EINVAL;
-	}
-
-	resource->name = strdup(mnl_attr_get_str(nla_resource[DEVLINK_ATTR_RESOURCE_NAME]));
-	resource->size = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE]);
-	resource->id = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_ID]);
-	resource->unit = mnl_attr_get_u8(nla_resource[DEVLINK_ATTR_RESOURCE_UNIT]);
-	resource->size_min = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MIN]);
-	resource->size_max = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MAX]);
-	resource->size_gran = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_GRAN]);
-
-	if (nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_NEW])
-		resource->size_new = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_NEW]);
-	else
-		resource->size_new = resource->size;
-
-	if (nla_resource[DEVLINK_ATTR_RESOURCE_OCC]) {
-		resource->size_occ = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_OCC]);
-		resource->occ_valid = true;
-	}
-
-	if (resource->size_new != resource->size)
-		ctx->pending_change = true;
-
-	return 0;
-}
-
-static int
-resource_get(struct resource_ctx *ctx, struct resource *resource,
-	     struct resource *parent_resource, struct nlattr *nl)
-{
-	struct nlattr *nla_resource[DEVLINK_ATTR_MAX + 1] = {};
-	struct nlattr *nla_child_resource;
-	struct nlattr *nla_resources;
-	bool top = false;
-	int err;
-
-	if (!resource) {
-		nla_resources = nl;
-		top = true;
-		goto out;
-	}
-
-	err = mnl_attr_parse_nested(nl, attr_cb, nla_resource);
-	if (err != MNL_CB_OK)
-		return -EINVAL;
-
-	err = resource_parse(ctx, resource, nla_resource);
-	if (err)
-		return err;
-
-	resource->parent = parent_resource;
-	if (!nla_resource[DEVLINK_ATTR_RESOURCE_LIST])
-		return 0;
-
-	resource->size_valid = !!mnl_attr_get_u8(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_VALID]);
-	nla_resources = nla_resource[DEVLINK_ATTR_RESOURCE_LIST];
-out:
-	mnl_attr_for_each_nested(nla_child_resource, nla_resources) {
-		struct resource *child_resource;
-		struct list_head *list;
-
-		child_resource = resource_alloc();
-		if (!child_resource)
-			return -ENOMEM;
-
-		if (top)
-			list = &ctx->resources->resource_list;
-		else
-			list = &resource->resource_list;
-
-		list_add_tail(&child_resource->list, list);
-		err = resource_get(ctx, child_resource, resource,
-				   nla_child_resource);
-		if (err)
-			return err;
-	}
-
-	return 0;
-}
-
-static const char *resource_unit_str_get(enum devlink_resource_unit unit)
-{
-	switch (unit) {
-	case DEVLINK_RESOURCE_UNIT_ENTRY: return "entry";
-	default: return "<unknown unit>";
-	}
-}
-
-static void resource_show(struct resource *resource,
-			  struct resource_ctx *ctx)
-{
-	struct resource *child_resource;
-	struct dpipe_table *table;
-	struct dl *dl = ctx->dl;
-	bool array = false;
-
-	pr_out_str(dl, "name", resource->name);
-	if (dl->verbose)
-		resource_path_print(dl, ctx->resources, resource->id);
-	pr_out_u64(dl, "size", resource->size);
-	if (resource->size != resource->size_new)
-		pr_out_u64(dl, "size_new", resource->size_new);
-	if (resource->occ_valid)
-		pr_out_uint(dl, "occ", resource->size_occ);
-	pr_out_str(dl, "unit", resource_unit_str_get(resource->unit));
-
-	if (resource->size_min != resource->size_max) {
-		pr_out_uint(dl, "size_min", resource->size_min);
-		pr_out_u64(dl, "size_max", resource->size_max);
-		pr_out_uint(dl, "size_gran", resource->size_gran);
-	}
-
-	list_for_each_entry(table, &ctx->tables->table_list, list)
-		if (table->resource_id == resource->id &&
-		    table->resource_valid)
-			array = true;
-
-	if (array)
-		pr_out_array_start(dl, "dpipe_tables");
-	else
-		pr_out_str(dl, "dpipe_tables", "none");
-
-	list_for_each_entry(table, &ctx->tables->table_list, list) {
-		if (table->resource_id != resource->id ||
-		    !table->resource_valid)
-			continue;
-		pr_out_entry_start(dl);
-		pr_out_str(dl, "table_name", table->name);
-		pr_out_entry_end(dl);
-	}
-	if (array)
-		pr_out_array_end(dl);
-
-	if (list_empty(&resource->resource_list))
-		return;
-
-	if (ctx->pending_change)
-		pr_out_str(dl, "size_valid", resource->size_valid ?
-			   "true" : "false");
-	pr_out_array_start(dl, "resources");
-	list_for_each_entry(child_resource, &resource->resource_list, list) {
-		pr_out_entry_start(dl);
-		resource_show(child_resource, ctx);
-		pr_out_entry_end(dl);
-	}
-	pr_out_array_end(dl);
-}
-
-static void
-resources_show(struct resource_ctx *ctx, struct nlattr **tb)
-{
-	struct resources *resources = ctx->resources;
-	struct resource *resource;
-
-	list_for_each_entry(resource, &resources->resource_list, list) {
-		pr_out_handle_start_arr(ctx->dl, tb);
-		resource_show(resource, ctx);
-		pr_out_handle_end(ctx->dl);
-	}
-}
-
-static int resources_get(struct resource_ctx *ctx, struct nlattr **tb)
-{
-	return resource_get(ctx, NULL, NULL, tb[DEVLINK_ATTR_RESOURCE_LIST]);
-}
-
-static int cmd_resource_dump_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct resource_ctx *ctx = data;
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	int err;
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_RESOURCE_LIST])
-		return MNL_CB_ERROR;
-
-	err = resources_get(ctx, tb);
-	if (err) {
-		ctx->err = err;
-		return MNL_CB_ERROR;
-	}
-
-	if (ctx->print_resources)
-		resources_show(ctx, tb);
-
-	return MNL_CB_OK;
-}
-
-static int cmd_resource_show(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	struct dpipe_ctx dpipe_ctx = {};
-	struct resource_ctx resource_ctx = {};
-	int err;
-
-	err = dl_argv_parse(dl, DL_OPT_HANDLE, 0);
-	if (err)
-		return err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_GET,
-			       NLM_F_REQUEST);
-	dl_opts_put(nlh, dl);
-
-	err = dpipe_ctx_init(&dpipe_ctx, dl);
-	if (err)
-		return err;
-
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_show_cb,
-				  &dpipe_ctx);
-	if (err) {
-		pr_err("error get tables %s\n", strerror(dpipe_ctx.err));
-		goto out;
-	}
-
-	err = resource_ctx_init(&resource_ctx, dl);
-	if (err)
-		goto out;
-
-	resource_ctx.print_resources = true;
-	resource_ctx.tables = dpipe_ctx.tables;
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP,
-			       NLM_F_REQUEST | NLM_F_ACK);
-	dl_opts_put(nlh, dl);
-	pr_out_section_start(dl, "resources");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb,
-				  &resource_ctx);
-	pr_out_section_end(dl);
-	resource_ctx_fini(&resource_ctx);
-out:
-	dpipe_ctx_fini(&dpipe_ctx);
-	return err;
-}
-
-static void cmd_resource_help(void)
-{
-	pr_err("Usage: devlink resource show DEV\n"
-	       "       devlink resource set DEV path PATH size SIZE\n");
-}
-
-static struct resource *
-resource_find_by_name(struct list_head *list, char *name)
-{
-	struct resource *resource;
-
-	list_for_each_entry(resource, list, list) {
-		if (!strcmp(resource->name, name))
-			return resource;
-	}
-	return NULL;
-}
-
-static int
-resource_path_parse(struct resource_ctx *ctx, const char *resource_path,
-		    uint32_t *p_resource_id, bool *p_resource_valid)
-{
-	struct resource *resource;
-	uint32_t resource_id = 0;
-	char *resource_path_dup;
-	struct list_head *list;
-	const char del[] = "/";
-	char *resource_name;
-
-	resource_path_dup = strdup(resource_path);
-	list = &ctx->resources->resource_list;
-	resource_name = strtok(resource_path_dup, del);
-	while (resource_name != NULL) {
-		resource = resource_find_by_name(list, resource_name);
-		if (!resource)
-			goto err_resource_lookup;
-
-		list = &resource->resource_list;
-		resource_name = strtok(NULL, del);
-		resource_id = resource->id;
-	}
-	free(resource_path_dup);
-	*p_resource_valid = true;
-	*p_resource_id = resource_id;
-	return 0;
-
-err_resource_lookup:
-	free(resource_path_dup);
-	return -EINVAL;
-}
-
-static int cmd_resource_set(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	struct resource_ctx ctx = {};
-	int err;
-
-	err = resource_ctx_init(&ctx, dl);
-	if (err)
-		return err;
-
-	ctx.print_resources = false;
-	err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_RESOURCE_PATH |
-			    DL_OPT_RESOURCE_SIZE, 0);
-	if (err)
-		goto out;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP,
-			       NLM_F_REQUEST);
-	dl_opts_put(nlh, dl);
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb, &ctx);
-	if (err) {
-		pr_err("error getting resources %s\n", strerror(ctx.err));
-		goto out;
-	}
-
-	err = resource_path_parse(&ctx, dl->opts.resource_path,
-				  &dl->opts.resource_id,
-				  &dl->opts.resource_id_valid);
-	if (err) {
-		pr_err("error parsing resource path %s\n", strerror(-err));
-		goto out;
-	}
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_SET,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	dl_opts_put(nlh, dl);
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-out:
-	resource_ctx_fini(&ctx);
-	return err;
-}
-
-static int cmd_resource(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
-		cmd_resource_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show")) {
-		dl_arg_inc(dl);
-		return cmd_resource_show(dl);
-	} else if (dl_argv_match(dl, "set")) {
-		dl_arg_inc(dl);
-		return cmd_resource_set(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static void pr_out_region_handle_start(struct dl *dl, struct nlattr **tb)
-{
-	const char *bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
-	const char *dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
-	const char *region_name = mnl_attr_get_str(tb[DEVLINK_ATTR_REGION_NAME]);
-	char buf[256];
-
-	sprintf(buf, "%s/%s/%s", bus_name, dev_name, region_name);
-	if (dl->json_output) {
-		jsonw_name(dl->jw, buf);
-		jsonw_start_object(dl->jw);
-	} else {
-		pr_out("%s:", buf);
-	}
-}
-
-static void pr_out_region_handle_end(struct dl *dl)
-{
-	if (dl->json_output)
-		jsonw_end_object(dl->jw);
-	else
-		pr_out("\n");
-}
-
-static void pr_out_region_snapshots_start(struct dl *dl, bool array)
-{
-	__pr_out_indent_newline(dl);
-	if (dl->json_output) {
-		jsonw_name(dl->jw, "snapshot");
-		jsonw_start_array(dl->jw);
-	} else {
-		pr_out("snapshot %s", array ? "[" : "");
-	}
-}
-
-static void pr_out_region_snapshots_end(struct dl *dl, bool array)
-{
-	if (dl->json_output)
-		jsonw_end_array(dl->jw);
-	else if (array)
-		pr_out("]");
-}
-
-static void pr_out_region_snapshots_id(struct dl *dl, struct nlattr **tb, int index)
-{
-	uint32_t snapshot_id;
-
-	if (!tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID])
-		return;
-
-	snapshot_id = mnl_attr_get_u32(tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID]);
-
-	if (dl->json_output)
-		jsonw_uint(dl->jw, snapshot_id);
-	else
-		pr_out("%s%u", index ? " " : "", snapshot_id);
-}
-
-static void pr_out_snapshots(struct dl *dl, struct nlattr **tb)
-{
-	struct nlattr *tb_snapshot[DEVLINK_ATTR_MAX + 1] = {};
-	struct nlattr *nla_sanpshot;
-	int err, index = 0;
-
-	pr_out_region_snapshots_start(dl, true);
-	mnl_attr_for_each_nested(nla_sanpshot, tb[DEVLINK_ATTR_REGION_SNAPSHOTS]) {
-		err = mnl_attr_parse_nested(nla_sanpshot, attr_cb, tb_snapshot);
-		if (err != MNL_CB_OK)
-			return;
-		pr_out_region_snapshots_id(dl, tb_snapshot, index++);
-	}
-	pr_out_region_snapshots_end(dl, true);
-}
-
-static void pr_out_snapshot(struct dl *dl, struct nlattr **tb)
-{
-	pr_out_region_snapshots_start(dl, false);
-	pr_out_region_snapshots_id(dl, tb, 0);
-	pr_out_region_snapshots_end(dl, false);
-}
-
-static void pr_out_region(struct dl *dl, struct nlattr **tb)
-{
-	pr_out_region_handle_start(dl, tb);
-
-	if (tb[DEVLINK_ATTR_REGION_SIZE])
-		pr_out_u64(dl, "size",
-			   mnl_attr_get_u64(tb[DEVLINK_ATTR_REGION_SIZE]));
-
-	if (tb[DEVLINK_ATTR_REGION_SNAPSHOTS])
-		pr_out_snapshots(dl, tb);
-
-	if (tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID])
-		pr_out_snapshot(dl, tb);
-
-	pr_out_region_handle_end(dl);
-}
-
-static int cmd_region_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct dl *dl = data;
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_REGION_NAME] || !tb[DEVLINK_ATTR_REGION_SIZE])
-		return MNL_CB_ERROR;
-
-	pr_out_region(dl, tb);
-
-	return MNL_CB_OK;
-}
-
-static int cmd_region_show(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
-	int err;
-
-	if (dl_argc(dl) == 0)
-		flags |= NLM_F_DUMP;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_GET, flags);
-
-	if (dl_argc(dl) > 0) {
-		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION, 0);
-		if (err)
-			return err;
-	}
-
-	pr_out_section_start(dl, "regions");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_region_show_cb, dl);
-	pr_out_section_end(dl);
-	return err;
-}
-
-static int cmd_region_snapshot_del(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_DEL,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION |
-				DL_OPT_REGION_SNAPSHOT_ID, 0);
-	if (err)
-		return err;
-
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static int cmd_region_read_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct nlattr *nla_entry, *nla_chunk_data, *nla_chunk_addr;
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	struct nlattr *tb_field[DEVLINK_ATTR_MAX + 1] = {};
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct dl *dl = data;
-	int err;
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_REGION_CHUNKS])
-		return MNL_CB_ERROR;
-
-	mnl_attr_for_each_nested(nla_entry, tb[DEVLINK_ATTR_REGION_CHUNKS]) {
-		err = mnl_attr_parse_nested(nla_entry, attr_cb, tb_field);
-		if (err != MNL_CB_OK)
-			return MNL_CB_ERROR;
-
-		nla_chunk_data = tb_field[DEVLINK_ATTR_REGION_CHUNK_DATA];
-		if (!nla_chunk_data)
-			continue;
-
-		nla_chunk_addr = tb_field[DEVLINK_ATTR_REGION_CHUNK_ADDR];
-		if (!nla_chunk_addr)
-			continue;
-
-		pr_out_region_chunk(dl, mnl_attr_get_payload(nla_chunk_data),
-				    mnl_attr_get_payload_len(nla_chunk_data),
-				    mnl_attr_get_u64(nla_chunk_addr));
-	}
-	return MNL_CB_OK;
-}
-
-static int cmd_region_dump(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_READ,
-			       NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION |
-				DL_OPT_REGION_SNAPSHOT_ID, 0);
-	if (err)
-		return err;
-
-	pr_out_section_start(dl, "dump");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_region_read_cb, dl);
-	pr_out_section_end(dl);
-	if (!dl->json_output)
-		pr_out("\n");
-	return err;
-}
-
-static int cmd_region_read(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_READ,
-			       NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION |
-				DL_OPT_REGION_ADDRESS | DL_OPT_REGION_LENGTH |
-				DL_OPT_REGION_SNAPSHOT_ID, 0);
-	if (err)
-		return err;
-
-	pr_out_section_start(dl, "read");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_region_read_cb, dl);
-	pr_out_section_end(dl);
-	if (!dl->json_output)
-		pr_out("\n");
-	return err;
-}
-
-static void cmd_region_help(void)
-{
-	pr_err("Usage: devlink region show [ DEV/REGION ]\n");
-	pr_err("       devlink region del DEV/REGION snapshot SNAPSHOT_ID\n");
-	pr_err("       devlink region dump DEV/REGION [ snapshot SNAPSHOT_ID ]\n");
-	pr_err("       devlink region read DEV/REGION [ snapshot SNAPSHOT_ID ] address ADDRESS length LENGTH\n");
-}
-
-static int cmd_region(struct dl *dl)
-{
-	if (dl_no_arg(dl)) {
-		return cmd_region_show(dl);
-	} else if (dl_argv_match(dl, "help")) {
-		cmd_region_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show")) {
-		dl_arg_inc(dl);
-		return cmd_region_show(dl);
-	} else if (dl_argv_match(dl, "del")) {
-		dl_arg_inc(dl);
-		return cmd_region_snapshot_del(dl);
-	} else if (dl_argv_match(dl, "dump")) {
-		dl_arg_inc(dl);
-		return cmd_region_dump(dl);
-	} else if (dl_argv_match(dl, "read")) {
-		dl_arg_inc(dl);
-		return cmd_region_read(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static int cmd_health_set_params(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_SET,
-			       NLM_F_REQUEST | NLM_F_ACK);
-	err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME,
-			    DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD |
-			    DL_OPT_HEALTH_REPORTER_AUTO_RECOVER);
-	if (err)
-		return err;
-
-	dl_opts_put(nlh, dl);
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static int cmd_health_dump_clear(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_DUMP_CLEAR,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl,
-				DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME, 0);
-	if (err)
-		return err;
-
-	dl_opts_put(nlh, dl);
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static int fmsg_value_show(struct dl *dl, int type, struct nlattr *nl_data)
-{
-	uint8_t *data;
-	uint32_t len;
-
-	switch (type) {
-	case MNL_TYPE_FLAG:
-		pr_out_bool_value(dl, mnl_attr_get_u8(nl_data));
-		break;
-	case MNL_TYPE_U8:
-		pr_out_uint_value(dl, mnl_attr_get_u8(nl_data));
-		break;
-	case MNL_TYPE_U16:
-		pr_out_uint_value(dl, mnl_attr_get_u16(nl_data));
-		break;
-	case MNL_TYPE_U32:
-		pr_out_uint_value(dl, mnl_attr_get_u32(nl_data));
-		break;
-	case MNL_TYPE_U64:
-		pr_out_uint64_value(dl, mnl_attr_get_u64(nl_data));
-		break;
-	case MNL_TYPE_NUL_STRING:
-		pr_out_str_value(dl, mnl_attr_get_str(nl_data));
-		break;
-	case MNL_TYPE_BINARY:
-		len = mnl_attr_get_payload_len(nl_data);
-		data = mnl_attr_get_payload(nl_data);
-		pr_out_binary_value(dl, data, len);
-		break;
-	default:
-		return -EINVAL;
-	}
-	return MNL_CB_OK;
-}
-
-struct nest_entry {
-	int attr_type;
-	struct list_head list;
-};
-
-struct fmsg_cb_data {
-	struct dl *dl;
-	uint8_t value_type;
-	struct list_head entry_list;
-};
-
-static int cmd_fmsg_nest_queue(struct fmsg_cb_data *fmsg_data,
-			       uint8_t *attr_value, bool insert)
-{
-	struct nest_entry *entry;
-
-	if (insert) {
-		entry = malloc(sizeof(struct nest_entry));
-		if (!entry)
-			return -ENOMEM;
-
-		entry->attr_type = *attr_value;
-		list_add(&entry->list, &fmsg_data->entry_list);
-	} else {
-		if (list_empty(&fmsg_data->entry_list))
-			return MNL_CB_ERROR;
-		entry = list_first_entry(&fmsg_data->entry_list,
-					 struct nest_entry, list);
-		*attr_value = entry->attr_type;
-		list_del(&entry->list);
-		free(entry);
-	}
-	return MNL_CB_OK;
-}
-
-static int cmd_fmsg_nest(struct fmsg_cb_data *fmsg_data, uint8_t nest_value,
-			 bool start)
-{
-	struct dl *dl = fmsg_data->dl;
-	uint8_t value = nest_value;
-	int err;
-
-	err = cmd_fmsg_nest_queue(fmsg_data, &value, start);
-	if (err != MNL_CB_OK)
-		return err;
-
-	switch (value) {
-	case DEVLINK_ATTR_FMSG_OBJ_NEST_START:
-		if (start)
-			pr_out_entry_start(dl);
-		else
-			pr_out_entry_end(dl);
-		break;
-	case DEVLINK_ATTR_FMSG_PAIR_NEST_START:
-		break;
-	case DEVLINK_ATTR_FMSG_ARR_NEST_START:
-		if (dl->json_output) {
-			if (start)
-				jsonw_start_array(dl->jw);
-			else
-				jsonw_end_array(dl->jw);
-		} else {
-			if (start) {
-				__pr_out_newline();
-				__pr_out_indent_inc();
-			} else {
-				__pr_out_indent_dec();
-			}
-		}
-		break;
-	default:
-		return -EINVAL;
-	}
-	return MNL_CB_OK;
-}
-
-static int cmd_fmsg_object_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct fmsg_cb_data *fmsg_data = data;
-	struct dl *dl = fmsg_data->dl;
-	struct nlattr *nla_object;
-	int attr_type;
-	int err;
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_FMSG])
-		return MNL_CB_ERROR;
-
-	mnl_attr_for_each_nested(nla_object, tb[DEVLINK_ATTR_FMSG]) {
-		attr_type = mnl_attr_get_type(nla_object);
-		switch (attr_type) {
-		case DEVLINK_ATTR_FMSG_OBJ_NEST_START:
-		case DEVLINK_ATTR_FMSG_PAIR_NEST_START:
-		case DEVLINK_ATTR_FMSG_ARR_NEST_START:
-			err = cmd_fmsg_nest(fmsg_data, attr_type, true);
-			if (err != MNL_CB_OK)
-				return err;
-			break;
-		case DEVLINK_ATTR_FMSG_NEST_END:
-			err = cmd_fmsg_nest(fmsg_data, attr_type, false);
-			if (err != MNL_CB_OK)
-				return err;
-			break;
-		case DEVLINK_ATTR_FMSG_OBJ_NAME:
-			pr_out_name(dl, mnl_attr_get_str(nla_object));
-			break;
-		case DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE:
-			fmsg_data->value_type = mnl_attr_get_u8(nla_object);
-			break;
-		case DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA:
-			err = fmsg_value_show(dl, fmsg_data->value_type,
-					      nla_object);
-			if (err != MNL_CB_OK)
-				return err;
-			break;
-		default:
-			return -EINVAL;
-		}
-	}
-	return MNL_CB_OK;
-}
-
-static int cmd_health_object_common(struct dl *dl, uint8_t cmd, uint16_t flags)
-{
-	struct fmsg_cb_data data;
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, cmd, flags | NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl,
-				DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME, 0);
-	if (err)
-		return err;
-
-	data.dl = dl;
-	INIT_LIST_HEAD(&data.entry_list);
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_fmsg_object_cb, &data);
-	return err;
-}
-
-static int cmd_health_dump_show(struct dl *dl)
-{
-	return cmd_health_object_common(dl,
-					DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET,
-					NLM_F_DUMP);
-}
-
-static int cmd_health_diagnose(struct dl *dl)
-{
-	return cmd_health_object_common(dl,
-					DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE,
-					0);
-}
-
-static int cmd_health_recover(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_RECOVER,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl,
-				DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME, 0);
-	if (err)
-		return err;
-
-	dl_opts_put(nlh, dl);
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-enum devlink_health_reporter_state {
-	DEVLINK_HEALTH_REPORTER_STATE_HEALTHY,
-	DEVLINK_HEALTH_REPORTER_STATE_ERROR,
-};
-
-static const char *health_state_name(uint8_t state)
-{
-	switch (state) {
-	case DEVLINK_HEALTH_REPORTER_STATE_HEALTHY:
-		return HEALTH_REPORTER_STATE_HEALTHY_STR;
-	case DEVLINK_HEALTH_REPORTER_STATE_ERROR:
-		return HEALTH_REPORTER_STATE_ERROR_STR;
-	default:
-		return "<unknown state>";
-	}
-}
-
-static void format_logtime(uint64_t time_ms, char *ts_date, char *ts_time)
-{
-	struct sysinfo s_info;
-	struct tm *info;
-	time_t now, sec;
-	int err;
-
-	time(&now);
-	info = localtime(&now);
-	err = sysinfo(&s_info);
-	if (err)
-		goto out;
-	/* Subtract uptime in sec from now yields the time of system
-	 * uptime. To this, add time_ms which is the amount of
-	 * milliseconds elapsed between uptime and the dump taken.
-	 */
-	sec = now - s_info.uptime + time_ms / 1000;
-	info = localtime(&sec);
-out:
-	strftime(ts_date, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%Y-%m-%d", info);
-	strftime(ts_time, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%H:%M:%S", info);
-}
-
-static void pr_out_health(struct dl *dl, struct nlattr **tb_health)
-{
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	enum devlink_health_reporter_state state;
-	const struct nlattr *attr;
-	uint64_t time_ms;
-	int err;
-
-	err = mnl_attr_parse_nested(tb_health[DEVLINK_ATTR_HEALTH_REPORTER],
-				    attr_cb, tb);
-	if (err != MNL_CB_OK)
-		return;
-
-	if (!tb[DEVLINK_ATTR_HEALTH_REPORTER_NAME] ||
-	    !tb[DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT] ||
-	    !tb[DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT] ||
-	    !tb[DEVLINK_ATTR_HEALTH_REPORTER_STATE])
-		return;
-
-	pr_out_handle_start_arr(dl, tb_health);
-
-	pr_out_str(dl, "reporter",
-		   mnl_attr_get_str(tb[DEVLINK_ATTR_HEALTH_REPORTER_NAME]));
-	if (!dl->json_output) {
-		__pr_out_newline();
-		__pr_out_indent_inc();
-	}
-	state = mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_STATE]);
-	pr_out_str(dl, "state", health_state_name(state));
-	pr_out_u64(dl, "error",
-		   mnl_attr_get_u64(tb[DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT]));
-	pr_out_u64(dl, "recover",
-		   mnl_attr_get_u64(tb[DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT]));
-	if (tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS]) {
-		char dump_date[HEALTH_REPORTER_TIMESTAMP_FMT_LEN];
-		char dump_time[HEALTH_REPORTER_TIMESTAMP_FMT_LEN];
-
-		attr = tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS];
-		time_ms = mnl_attr_get_u64(attr);
-		format_logtime(time_ms, dump_date, dump_time);
-
-		pr_out_str(dl, "last_dump_date", dump_date);
-		pr_out_str(dl, "last_dump_time", dump_time);
-	}
-	if (tb[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])
-		pr_out_u64(dl, "grace_period",
-			   mnl_attr_get_u64(tb[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD]));
-	if (tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
-		pr_out_bool(dl, "auto_recover",
-			    mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]));
-
-	__pr_out_indent_dec();
-	pr_out_handle_end(dl);
-}
-
-static int cmd_health_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct dl *dl = data;
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_HEALTH_REPORTER])
-		return MNL_CB_ERROR;
-
-	pr_out_health(dl, tb);
-
-	return MNL_CB_OK;
-}
-
-static int cmd_health_show(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
-	int err;
-
-	if (dl_argc(dl) == 0)
-		flags |= NLM_F_DUMP;
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_GET,
-			       flags);
-
-	if (dl_argc(dl) > 0) {
-		err = dl_argv_parse_put(nlh, dl,
-					DL_OPT_HANDLE |
-					DL_OPT_HEALTH_REPORTER_NAME, 0);
-		if (err)
-			return err;
-	}
-	pr_out_section_start(dl, "health");
-
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_health_show_cb, dl);
-	pr_out_section_end(dl);
-	return err;
-}
-
-static void cmd_health_help(void)
-{
-	pr_err("Usage: devlink health show [ dev DEV reporter REPORTER_NAME ]\n");
-	pr_err("       devlink health recover DEV reporter REPORTER_NAME\n");
-	pr_err("       devlink health diagnose DEV reporter REPORTER_NAME\n");
-	pr_err("       devlink health dump show DEV reporter REPORTER_NAME\n");
-	pr_err("       devlink health dump clear DEV reporter REPORTER_NAME\n");
-	pr_err("       devlink health set DEV reporter REPORTER_NAME { grace_period | auto_recover } { msec | boolean }\n");
-}
-
-static int cmd_health(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help")) {
-		cmd_health_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show") ||
-		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
-		dl_arg_inc(dl);
-		return cmd_health_show(dl);
-	} else if (dl_argv_match(dl, "recover")) {
-		dl_arg_inc(dl);
-		return cmd_health_recover(dl);
-	} else if (dl_argv_match(dl, "diagnose")) {
-		dl_arg_inc(dl);
-		return cmd_health_diagnose(dl);
-	} else if (dl_argv_match(dl, "dump")) {
-		dl_arg_inc(dl);
-		if (dl_argv_match(dl, "show")) {
-			dl_arg_inc(dl);
-			return cmd_health_dump_show(dl);
-		} else if (dl_argv_match(dl, "clear")) {
-			dl_arg_inc(dl);
-			return cmd_health_dump_clear(dl);
-		}
-	} else if (dl_argv_match(dl, "set")) {
-		dl_arg_inc(dl);
-		return cmd_health_set_params(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static const char *trap_type_name(uint8_t type)
-{
-	switch (type) {
-	case DEVLINK_TRAP_TYPE_DROP:
-		return "drop";
-	case DEVLINK_TRAP_TYPE_EXCEPTION:
-		return "exception";
-	default:
-		return "<unknown type>";
-	}
-}
-
-static const char *trap_action_name(uint8_t action)
-{
-	switch (action) {
-	case DEVLINK_TRAP_ACTION_DROP:
-		return "drop";
-	case DEVLINK_TRAP_ACTION_TRAP:
-		return "trap";
-	default:
-		return "<unknown action>";
-	}
-}
-
-static const char *trap_metadata_name(const struct nlattr *attr)
-{
-	switch (attr->nla_type) {
-	case DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT:
-		return "input_port";
-	default:
-		return "<unknown metadata type>";
-	}
-}
-static void pr_out_trap_metadata(struct dl *dl, struct nlattr *attr)
-{
-	struct nlattr *attr_metadata;
-
-	pr_out_array_start(dl, "metadata");
-	mnl_attr_for_each_nested(attr_metadata, attr)
-		pr_out_str_value(dl, trap_metadata_name(attr_metadata));
-	pr_out_array_end(dl);
-}
-
-static void pr_out_trap(struct dl *dl, struct nlattr **tb, bool array)
-{
-	uint8_t action = mnl_attr_get_u8(tb[DEVLINK_ATTR_TRAP_ACTION]);
-	uint8_t type = mnl_attr_get_u8(tb[DEVLINK_ATTR_TRAP_TYPE]);
-
-	if (array)
-		pr_out_handle_start_arr(dl, tb);
-	else
-		__pr_out_handle_start(dl, tb, true, false);
-
-	pr_out_str(dl, "name", mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_NAME]));
-	pr_out_str(dl, "type", trap_type_name(type));
-	pr_out_bool(dl, "generic", !!tb[DEVLINK_ATTR_TRAP_GENERIC]);
-	pr_out_str(dl, "action", trap_action_name(action));
-	pr_out_str(dl, "group",
-		   mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_GROUP_NAME]));
-	if (dl->verbose)
-		pr_out_trap_metadata(dl, tb[DEVLINK_ATTR_TRAP_METADATA]);
-	pr_out_stats(dl, tb[DEVLINK_ATTR_STATS]);
-	pr_out_handle_end(dl);
-}
-
-static int cmd_trap_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct dl *dl = data;
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_TRAP_NAME] || !tb[DEVLINK_ATTR_TRAP_TYPE] ||
-	    !tb[DEVLINK_ATTR_TRAP_ACTION] ||
-	    !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] ||
-	    !tb[DEVLINK_ATTR_TRAP_METADATA] || !tb[DEVLINK_ATTR_STATS])
-		return MNL_CB_ERROR;
-
-	pr_out_trap(dl, tb, true);
-
-	return MNL_CB_OK;
-}
-
-static void cmd_trap_help(void)
-{
-	pr_err("Usage: devlink trap set DEV trap TRAP [ action { trap | drop } ]\n");
-	pr_err("       devlink trap show [ DEV trap TRAP ]\n");
-	pr_err("       devlink trap group set DEV group GROUP [ action { trap | drop } ]\n");
-	pr_err("       devlink trap group show [ DEV group GROUP ]\n");
-}
-
-static int cmd_trap_show(struct dl *dl)
-{
-	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
-	struct nlmsghdr *nlh;
-	int err;
-
-	if (dl_argc(dl) == 0)
-		flags |= NLM_F_DUMP;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GET, flags);
-
-	if (dl_argc(dl) > 0) {
-		err = dl_argv_parse_put(nlh, dl,
-					DL_OPT_HANDLE | DL_OPT_TRAP_NAME, 0);
-		if (err)
-			return err;
-	}
-
-	pr_out_section_start(dl, "trap");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_trap_show_cb, dl);
-	pr_out_section_end(dl);
-
-	return err;
-}
-
-static int cmd_trap_set(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_SET,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_TRAP_NAME,
-				DL_OPT_TRAP_ACTION);
-	if (err)
-		return err;
-
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static void pr_out_trap_group(struct dl *dl, struct nlattr **tb, bool array)
-{
-	if (array)
-		pr_out_handle_start_arr(dl, tb);
-	else
-		__pr_out_handle_start(dl, tb, true, false);
-
-	pr_out_str(dl, "name",
-		   mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_GROUP_NAME]));
-	pr_out_bool(dl, "generic", !!tb[DEVLINK_ATTR_TRAP_GENERIC]);
-	pr_out_stats(dl, tb[DEVLINK_ATTR_STATS]);
-	pr_out_handle_end(dl);
-}
-
-static int cmd_trap_group_show_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
-	struct dl *dl = data;
-
-	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
-	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
-	    !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] || !tb[DEVLINK_ATTR_STATS])
-		return MNL_CB_ERROR;
-
-	pr_out_trap_group(dl, tb, true);
-
-	return MNL_CB_OK;
-}
-
-static int cmd_trap_group_show(struct dl *dl)
-{
-	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
-	struct nlmsghdr *nlh;
-	int err;
-
-	if (dl_argc(dl) == 0)
-		flags |= NLM_F_DUMP;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GROUP_GET, flags);
-
-	if (dl_argc(dl) > 0) {
-		err = dl_argv_parse_put(nlh, dl,
-					DL_OPT_HANDLE | DL_OPT_TRAP_GROUP_NAME,
-					0);
-		if (err)
-			return err;
-	}
-
-	pr_out_section_start(dl, "trap_group");
-	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_trap_group_show_cb, dl);
-	pr_out_section_end(dl);
-
-	return err;
-}
-
-static int cmd_trap_group_set(struct dl *dl)
-{
-	struct nlmsghdr *nlh;
-	int err;
-
-	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GROUP_SET,
-			       NLM_F_REQUEST | NLM_F_ACK);
-
-	err = dl_argv_parse_put(nlh, dl,
-				DL_OPT_HANDLE | DL_OPT_TRAP_GROUP_NAME,
-				DL_OPT_TRAP_ACTION);
-	if (err)
-		return err;
-
-	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
-}
-
-static int cmd_trap_group(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help")) {
-		cmd_trap_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show") ||
-		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
-		dl_arg_inc(dl);
-		return cmd_trap_group_show(dl);
-	} else if (dl_argv_match(dl, "set")) {
-		dl_arg_inc(dl);
-		return cmd_trap_group_set(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static int cmd_trap(struct dl *dl)
-{
-	if (dl_argv_match(dl, "help")) {
-		cmd_trap_help();
-		return 0;
-	} else if (dl_argv_match(dl, "show") ||
-		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
-		dl_arg_inc(dl);
-		return cmd_trap_show(dl);
-	} else if (dl_argv_match(dl, "set")) {
-		dl_arg_inc(dl);
-		return cmd_trap_set(dl);
-	} else if (dl_argv_match(dl, "group")) {
-		dl_arg_inc(dl);
-		return cmd_trap_group(dl);
-	}
-	pr_err("Command \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static void help(void)
-{
-	pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n"
-	       "       devlink [ -f[orce] ] -b[atch] filename\n"
-	       "where  OBJECT := { dev | port | sb | monitor | dpipe | resource | region | health | trap }\n"
-	       "       OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] -s[tatistics] }\n");
-}
-
-static int dl_cmd(struct dl *dl, int argc, char **argv)
-{
-	dl->argc = argc;
-	dl->argv = argv;
-
-	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
-		help();
-		return 0;
-	} else if (dl_argv_match(dl, "dev")) {
-		dl_arg_inc(dl);
-		return cmd_dev(dl);
-	} else if (dl_argv_match(dl, "port")) {
-		dl_arg_inc(dl);
-		return cmd_port(dl);
-	} else if (dl_argv_match(dl, "sb")) {
-		dl_arg_inc(dl);
-		return cmd_sb(dl);
-	} else if (dl_argv_match(dl, "monitor")) {
-		dl_arg_inc(dl);
-		return cmd_mon(dl);
-	} else if (dl_argv_match(dl, "dpipe")) {
-		dl_arg_inc(dl);
-		return cmd_dpipe(dl);
-	} else if (dl_argv_match(dl, "resource")) {
-		dl_arg_inc(dl);
-		return cmd_resource(dl);
-	} else if (dl_argv_match(dl, "region")) {
-		dl_arg_inc(dl);
-		return cmd_region(dl);
-	} else if (dl_argv_match(dl, "health")) {
-		dl_arg_inc(dl);
-		return cmd_health(dl);
-	} else if (dl_argv_match(dl, "trap")) {
-		dl_arg_inc(dl);
-		return cmd_trap(dl);
-	}
-	pr_err("Object \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
-}
-
-static int dl_init(struct dl *dl)
-{
-	int err;
-
-	dl->nlg = mnlg_socket_open(DEVLINK_GENL_NAME, DEVLINK_GENL_VERSION);
-	if (!dl->nlg) {
-		pr_err("Failed to connect to devlink Netlink\n");
-		return -errno;
-	}
-
-	err = ifname_map_init(dl);
-	if (err) {
-		pr_err("Failed to create index map\n");
-		goto err_ifname_map_create;
-	}
-	if (dl->json_output) {
-		dl->jw = jsonw_new(stdout);
-		if (!dl->jw) {
-			pr_err("Failed to create JSON writer\n");
-			goto err_json_new;
-		}
-		jsonw_pretty(dl->jw, dl->pretty_output);
-	}
-	return 0;
-
-err_json_new:
-	ifname_map_fini(dl);
-err_ifname_map_create:
-	mnlg_socket_close(dl->nlg);
-	return err;
-}
-
-static void dl_fini(struct dl *dl)
-{
-	if (dl->json_output)
-		jsonw_destroy(&dl->jw);
-	ifname_map_fini(dl);
-	mnlg_socket_close(dl->nlg);
-}
-
-static struct dl *dl_alloc(void)
-{
-	struct dl *dl;
-
-	dl = calloc(1, sizeof(*dl));
-	if (!dl)
-		return NULL;
-	return dl;
-}
-
-static void dl_free(struct dl *dl)
-{
-	free(dl);
-}
-
-static int dl_batch(struct dl *dl, const char *name, bool force)
-{
-	char *line = NULL;
-	size_t len = 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 EXIT_FAILURE;
-		}
-	}
-
-	cmdlineno = 0;
-	while (getcmdline(&line, &len, stdin) != -1) {
-		char *largv[100];
-		int largc;
-
-		largc = makeargs(line, largv, 100);
-		if (!largc)
-			continue;	/* blank line */
-
-		if (dl_cmd(dl, largc, largv)) {
-			fprintf(stderr, "Command failed %s:%d\n",
-				name, cmdlineno);
-			ret = EXIT_FAILURE;
-			if (!force)
-				break;
-		}
-	}
-
-	if (line)
-		free(line);
-
-	return ret;
-}
-
-int main(int argc, char **argv)
-{
-	static const struct option long_options[] = {
-		{ "Version",		no_argument,		NULL, 'V' },
-		{ "force",		no_argument,		NULL, 'f' },
-		{ "batch",		required_argument,	NULL, 'b' },
-		{ "no-nice-names",	no_argument,		NULL, 'n' },
-		{ "json",		no_argument,		NULL, 'j' },
-		{ "pretty",		no_argument,		NULL, 'p' },
-		{ "verbose",		no_argument,		NULL, 'v' },
-		{ "statistics",		no_argument,		NULL, 's' },
-		{ NULL, 0, NULL, 0 }
-	};
-	const char *batch_file = NULL;
-	bool force = false;
-	struct dl *dl;
-	int opt;
-	int err;
-	int ret;
-
-	dl = dl_alloc();
-	if (!dl) {
-		pr_err("Failed to allocate memory for devlink\n");
-		return EXIT_FAILURE;
-	}
-
-	while ((opt = getopt_long(argc, argv, "Vfb:njpvs",
-				  long_options, NULL)) >= 0) {
-
-		switch (opt) {
-		case 'V':
-			printf("devlink utility, iproute2-ss%s\n", SNAPSHOT);
-			ret = EXIT_SUCCESS;
-			goto dl_free;
-		case 'f':
-			force = true;
-			break;
-		case 'b':
-			batch_file = optarg;
-			break;
-		case 'n':
-			dl->no_nice_names = true;
-			break;
-		case 'j':
-			dl->json_output = true;
-			break;
-		case 'p':
-			dl->pretty_output = true;
-			break;
-		case 'v':
-			dl->verbose = true;
-			break;
-		case 's':
-			dl->stats = true;
-			break;
-		default:
-			pr_err("Unknown option.\n");
-			help();
-			ret = EXIT_FAILURE;
-			goto dl_free;
-		}
-	}
-
-	argc -= optind;
-	argv += optind;
-
-	err = dl_init(dl);
-	if (err) {
-		ret = EXIT_FAILURE;
-		goto dl_free;
-	}
-
-	if (batch_file)
-		err = dl_batch(dl, batch_file, force);
-	else
-		err = dl_cmd(dl, argc, argv);
-
-	if (err) {
-		ret = EXIT_FAILURE;
-		goto dl_fini;
-	}
-
-	ret = EXIT_SUCCESS;
-
-dl_fini:
-	dl_fini(dl);
-dl_free:
-	dl_free(dl);
-
-	return ret;
-}
diff --git a/devlink/mnlg.c b/devlink/mnlg.c
deleted file mode 100644
index c7d25e8..0000000
--- a/devlink/mnlg.c
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- *   mnlg.c	Generic Netlink helpers for libmnl
- *
- *              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:     Jiri Pirko <jiri@mellanox.com>
- */
-
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <time.h>
-#include <libmnl/libmnl.h>
-#include <linux/genetlink.h>
-
-#include "libnetlink.h"
-#include "utils.h"
-#include "mnlg.h"
-
-struct mnlg_socket {
-	struct mnl_socket *nl;
-	char *buf;
-	uint32_t id;
-	uint8_t version;
-	unsigned int seq;
-	unsigned int portid;
-};
-
-static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
-					   uint16_t flags, uint32_t id,
-					   uint8_t version)
-{
-	struct nlmsghdr *nlh;
-	struct genlmsghdr *genl;
-
-	nlh = mnl_nlmsg_put_header(nlg->buf);
-	nlh->nlmsg_type	= id;
-	nlh->nlmsg_flags = flags;
-	nlg->seq = time(NULL);
-	nlh->nlmsg_seq = nlg->seq;
-
-	genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
-	genl->cmd = cmd;
-	genl->version = version;
-
-	return nlh;
-}
-
-struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
-				  uint16_t flags)
-{
-	return __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version);
-}
-
-int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh)
-{
-	return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
-}
-
-static int mnlg_cb_noop(const struct nlmsghdr *nlh, void *data)
-{
-	return MNL_CB_OK;
-}
-
-static int mnlg_cb_error(const struct nlmsghdr *nlh, void *data)
-{
-	const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
-
-	/* Netlink subsystems returns the errno value with different signess */
-	if (err->error < 0)
-		errno = -err->error;
-	else
-		errno = err->error;
-
-	if (nl_dump_ext_ack(nlh, NULL))
-		return MNL_CB_ERROR;
-
-	return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
-}
-
-static int mnlg_cb_stop(const struct nlmsghdr *nlh, void *data)
-{
-	int len = *(int *)NLMSG_DATA(nlh);
-
-	if (len < 0) {
-		errno = -len;
-		nl_dump_ext_ack_done(nlh, len);
-		return MNL_CB_ERROR;
-	}
-	return MNL_CB_STOP;
-}
-
-static mnl_cb_t mnlg_cb_array[NLMSG_MIN_TYPE] = {
-	[NLMSG_NOOP]	= mnlg_cb_noop,
-	[NLMSG_ERROR]	= mnlg_cb_error,
-	[NLMSG_DONE]	= mnlg_cb_stop,
-	[NLMSG_OVERRUN]	= mnlg_cb_noop,
-};
-
-int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data)
-{
-	int err;
-
-	do {
-		err = mnl_socket_recvfrom(nlg->nl, nlg->buf,
-					  MNL_SOCKET_BUFFER_SIZE);
-		if (err <= 0)
-			break;
-		err = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid,
-				  data_cb, data, mnlg_cb_array,
-				  ARRAY_SIZE(mnlg_cb_array));
-	} while (err > 0);
-
-	return err;
-}
-
-struct group_info {
-	bool found;
-	uint32_t id;
-	const char *name;
-};
-
-static int parse_mc_grps_cb(const struct nlattr *attr, void *data)
-{
-	const struct nlattr **tb = data;
-	int type = mnl_attr_get_type(attr);
-
-	if (mnl_attr_type_valid(attr, CTRL_ATTR_MCAST_GRP_MAX) < 0)
-		return MNL_CB_OK;
-
-	switch (type) {
-	case CTRL_ATTR_MCAST_GRP_ID:
-		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
-			return MNL_CB_ERROR;
-		break;
-	case CTRL_ATTR_MCAST_GRP_NAME:
-		if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
-			return MNL_CB_ERROR;
-		break;
-	}
-	tb[type] = attr;
-	return MNL_CB_OK;
-}
-
-static void parse_genl_mc_grps(struct nlattr *nested,
-			       struct group_info *group_info)
-{
-	struct nlattr *pos;
-	const char *name;
-
-	mnl_attr_for_each_nested(pos, nested) {
-		struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1] = {};
-
-		mnl_attr_parse_nested(pos, parse_mc_grps_cb, tb);
-		if (!tb[CTRL_ATTR_MCAST_GRP_NAME] ||
-		    !tb[CTRL_ATTR_MCAST_GRP_ID])
-			continue;
-
-		name = mnl_attr_get_str(tb[CTRL_ATTR_MCAST_GRP_NAME]);
-		if (strcmp(name, group_info->name) != 0)
-			continue;
-
-		group_info->id = mnl_attr_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]);
-		group_info->found = true;
-	}
-}
-
-static int get_group_id_attr_cb(const struct nlattr *attr, void *data)
-{
-	const struct nlattr **tb = data;
-	int type = mnl_attr_get_type(attr);
-
-	if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
-		return MNL_CB_ERROR;
-
-	if (type == CTRL_ATTR_MCAST_GROUPS &&
-	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
-		return MNL_CB_ERROR;
-	tb[type] = attr;
-	return MNL_CB_OK;
-}
-
-static int get_group_id_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct group_info *group_info = data;
-	struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-
-	mnl_attr_parse(nlh, sizeof(*genl), get_group_id_attr_cb, tb);
-	if (!tb[CTRL_ATTR_MCAST_GROUPS])
-		return MNL_CB_ERROR;
-	parse_genl_mc_grps(tb[CTRL_ATTR_MCAST_GROUPS], group_info);
-	return MNL_CB_OK;
-}
-
-int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name)
-{
-	struct nlmsghdr *nlh;
-	struct group_info group_info;
-	int err;
-
-	nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
-				 NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
-	mnl_attr_put_u16(nlh, CTRL_ATTR_FAMILY_ID, nlg->id);
-
-	err = mnlg_socket_send(nlg, nlh);
-	if (err < 0)
-		return err;
-
-	group_info.found = false;
-	group_info.name = group_name;
-	err = mnlg_socket_recv_run(nlg, get_group_id_cb, &group_info);
-	if (err < 0)
-		return err;
-
-	if (!group_info.found) {
-		errno = ENOENT;
-		return -1;
-	}
-
-	err = mnl_socket_setsockopt(nlg->nl, NETLINK_ADD_MEMBERSHIP,
-				    &group_info.id, sizeof(group_info.id));
-	if (err < 0)
-		return err;
-
-	return 0;
-}
-
-static int get_family_id_attr_cb(const struct nlattr *attr, void *data)
-{
-	const struct nlattr **tb = data;
-	int type = mnl_attr_get_type(attr);
-
-	if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
-		return MNL_CB_ERROR;
-
-	if (type == CTRL_ATTR_FAMILY_ID &&
-	    mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
-		return MNL_CB_ERROR;
-	tb[type] = attr;
-	return MNL_CB_OK;
-}
-
-static int get_family_id_cb(const struct nlmsghdr *nlh, void *data)
-{
-	uint32_t *p_id = data;
-	struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
-	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
-
-	mnl_attr_parse(nlh, sizeof(*genl), get_family_id_attr_cb, tb);
-	if (!tb[CTRL_ATTR_FAMILY_ID])
-		return MNL_CB_ERROR;
-	*p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
-	return MNL_CB_OK;
-}
-
-struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
-{
-	struct mnlg_socket *nlg;
-	struct nlmsghdr *nlh;
-	int one = 1;
-	int err;
-
-	nlg = malloc(sizeof(*nlg));
-	if (!nlg)
-		return NULL;
-
-	nlg->buf = malloc(MNL_SOCKET_BUFFER_SIZE);
-	if (!nlg->buf)
-		goto err_buf_alloc;
-
-	nlg->nl = mnl_socket_open(NETLINK_GENERIC);
-	if (!nlg->nl)
-		goto err_mnl_socket_open;
-
-	/* Older kernels may no support capped/extended ACK reporting */
-	mnl_socket_setsockopt(nlg->nl, NETLINK_CAP_ACK, &one, sizeof(one));
-	mnl_socket_setsockopt(nlg->nl, NETLINK_EXT_ACK, &one, sizeof(one));
-
-	err = mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID);
-	if (err < 0)
-		goto err_mnl_socket_bind;
-
-	nlg->portid = mnl_socket_get_portid(nlg->nl);
-
-	nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
-				 NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
-	mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);
-
-	err = mnlg_socket_send(nlg, nlh);
-	if (err < 0)
-		goto err_mnlg_socket_send;
-
-	err = mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id);
-	if (err < 0)
-		goto err_mnlg_socket_recv_run;
-
-	nlg->version = version;
-	return nlg;
-
-err_mnlg_socket_recv_run:
-err_mnlg_socket_send:
-err_mnl_socket_bind:
-	mnl_socket_close(nlg->nl);
-err_mnl_socket_open:
-	free(nlg->buf);
-err_buf_alloc:
-	free(nlg);
-	return NULL;
-}
-
-void mnlg_socket_close(struct mnlg_socket *nlg)
-{
-	mnl_socket_close(nlg->nl);
-	free(nlg->buf);
-	free(nlg);
-}
-
-int mnlg_socket_get_fd(struct mnlg_socket *nlg)
-{
-	return mnl_socket_get_fd(nlg->nl);
-}
diff --git a/devlink/mnlg.h b/devlink/mnlg.h
deleted file mode 100644
index 61bc5a3..0000000
--- a/devlink/mnlg.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- *   mnlg.h	Generic Netlink helpers for libmnl
- *
- *              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:     Jiri Pirko <jiri@mellanox.com>
- */
-
-#ifndef _MNLG_H_
-#define _MNLG_H_
-
-#include <libmnl/libmnl.h>
-
-struct mnlg_socket;
-
-struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
-				  uint16_t flags);
-int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh);
-int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data);
-int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name);
-struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version);
-void mnlg_socket_close(struct mnlg_socket *nlg);
-int mnlg_socket_get_fd(struct mnlg_socket *nlg);
-
-#endif /* _MNLG_H_ */
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..e9c0ff7
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,73 @@
+PSFILES=ip-cref.ps ip-tunnels.ps api-ip6-flowlabels.ps ss.ps nstat.ps arpd.ps rtstat.ps
+# tc-cref.ps
+# api-rtnl.tex api-pmtudisc.tex api-news.tex
+# iki-netdev.ps iki-neighdst.ps
+
+
+LATEX=latex
+DVIPS=dvips
+SGML2DVI=sgml2latex
+SGML2HTML=sgml2html -s 0
+LPR=lpr -Zsduplex
+SHELL=bash
+PAGESIZE=a4
+PAGESPERPAGE=2
+
+HTMLFILES=$(subst .sgml,.html,$(shell echo *.sgml))
+DVIFILES=$(subst .ps,.dvi,$(PSFILES))
+PDFFILES=$(subst .ps,.pdf,$(PSFILES))
+
+
+all: pstwocol
+
+pstwocol: $(PSFILES)
+
+html: $(HTMLFILES)
+
+dvi: $(DVIFILES)
+
+pdf: $(PDFFILES)
+
+print: $(PSFILES)
+	$(LPR) $(PSFILES)
+
+%.tex: %.sgml
+	$(SGML2DVI) --output=tex $<
+
+%.dvi: %.sgml
+	$(SGML2DVI) --output=dvi $<
+
+%.dvi: %.tex
+	@set -e; pass=2; echo "Running LaTeX $<"; \
+	while [ `$(LATEX) $< </dev/null 2>&1 | \
+		 grep -c '^\(LaTeX Warning: Label(s) may\|No file \|! Emergency stop\)'` -ge 1 ]; do \
+		if [ $$pass -gt 3 ]; then \
+			echo "Seems, something is wrong. Try by hands." ; exit 1 ; \
+		fi; \
+		echo "Re-running LaTeX $<, $${pass}d pass"; pass=$$[$$pass + 1]; \
+	done
+
+%.pdf: %.tex
+	@set -e; pass=2; echo "Running pdfLaTeX $<"; \
+	while [ `pdflatex $< </dev/null 2>&1 | \
+		 grep -c '^\(LaTeX Warning: Label(s) may\|No file \|! Emergency stop\)'` -ge 1 ]; do \
+		if [ $$pass -gt 3 ]; then \
+			echo "Seems, something is wrong. Try by hands." ; exit 1 ; \
+		fi; \
+		echo "Re-running pdfLaTeX $<, $${pass}d pass"; pass=$$[$$pass + 1]; \
+	done
+#%.pdf: %.ps
+#	ps2pdf $<
+
+%.ps: %.dvi
+	$(DVIPS) $< -o $@
+
+%.html: %.sgml
+	$(SGML2HTML) $<
+
+install:
+	install -m 0644 $(shell echo *.tex) $(DESTDIR)$(DOCDIR)
+	install -m 0644 $(shell echo *.sgml) $(DESTDIR)$(DOCDIR)
+
+clean:
+	rm -f *.aux *.log *.toc $(PSFILES) $(DVIFILES) *.html *.pdf
diff --git a/doc/Plan b/doc/Plan
new file mode 100644
index 0000000..55f478e
--- /dev/null
+++ b/doc/Plan
@@ -0,0 +1,16 @@
+Partially finished work.
+
+1.  User Reference manuals.
+1.1 IP Command reference (ip-cref.tex, published)
+1.2 TC Command reference (tc-cref.tex)
+1.3 IP tunnels (ip-tunnels.tex, published)
+
+2.  Linux-2.2 Networking API
+2.1 RTNETLINK (api-rtnl.tex)
+2.2 Path MTU Discovery (api-pmtudisc.tex)
+2.3 IPv6 Flow Labels (api-ip6-flowlabels.tex, published)
+2.4 Miscellaneous extensions (api-misc.tex)
+
+3.  Linux-2.2 Networking Intra-Kernel Interfaces
+3.1 NetDev --- Networking Devices and netdev... (iki-netdev.tex)
+3.2 Neighbour cache and destination cache. (iki-neighdst.tex)
diff --git a/doc/SNAPSHOT.tex b/doc/SNAPSHOT.tex
new file mode 100644
index 0000000..7ed0298
--- /dev/null
+++ b/doc/SNAPSHOT.tex
@@ -0,0 +1 @@
+\def\Draft{020116}
diff --git a/doc/actions/actions-general b/doc/actions/actions-general
index 407a514..70f7cd6 100644
--- a/doc/actions/actions-general
+++ b/doc/actions/actions-general
@@ -6,8 +6,8 @@
 -----------
 
 An extension to the filtering/classification architecture of Linux Traffic
-Control.
-Up to 2.6.8 the only action that could be "attached" to a filter was policing.
+Control. 
+Up to 2.6.8 the only action that could be "attached" to a filter was policing. 
 i.e you could say something like:
 
 -----
@@ -17,11 +17,11 @@
 
 which implies "if a packet is seen on the ingress of the lo device with
 a source IP address of 127.0.0.1/32 we give it a classification id  of 1:1 and
-we execute a policing action which rate limits its bandwidth utilization
+we execute a policing action which rate limits its bandwidth utilization 
 to 1.5Mbps".
 
 The new extensions allow for more than just policing actions to be added.
-They are also fully backward compatible. If you have a kernel that doesn't
+They are also fully backward compatible. If you have a kernel that doesnt
 understand them, then the effect is null i.e if you have a newer tc
 but older kernel, the actions are not installed. Likewise if you
 have a newer kernel but older tc, obviously the tc will use current
@@ -29,9 +29,9 @@
 both newer tc and kernel. If you are reading this you have the
 right tc ;->
 
-A side effect is that we can now get stateless firewalling to work with tc.
+A side effect is that we can now get stateless firewalling to work with tc. 
 Essentially this is now an alternative to iptables.
-I won't go into details of my dislike for iptables at times, but
+I wont go into details of my dislike for iptables at times, but 
 scalability is one of the main issues; however, if you need stateful
 classification - use netfilter (for now).
 
@@ -61,7 +61,7 @@
 match ip src 127.0.0.1/32 flowid 1:1 \
 action police mtu 4000 rate 1500kbit burst 90k
 
-" generic Actions" (gact) at the moment are:
+" generic Actions" (gact) at the moment are: 
 { drop, pass, reclassify, continue}
 (If you have others, no listed here give me a reason and we will add them)
 +drop says to drop the packet
@@ -77,7 +77,7 @@
 In terms of hooks:
 *ingress is mapped to pre-routing hook
 *egress is mapped to post-routing hook
-I don't see much value in the other hooks, if you see it and email me good
+I dont see much value in the other hooks, if you see it and email me good
 reasons, the addition is trivial.
 
 Example syntax for iptables targets usage becomes:
@@ -93,43 +93,43 @@
 
 3) A feature i call pipe
 The motivation is derived from Unix pipe mechanism but applied to packets.
-Essentially take a matching packet and pass it through
+Essentially take a matching packet and pass it through 
 action1 | action2 | action3 etc.
 You could do something similar to this with the tc policer and the "continue"
-operator but this rather restricts it to just the policer and requires
-multiple rules (and lookups, hence quiet inefficient);
+operator but this rather restricts it to just the policer and requires 
+multiple rules (and lookups, hence quiet inefficient); 
 
-as an example -- and please note that this is just an example _not_ The
+as an example -- and please note that this is just an example _not_ The 
 Word Youve Been Waiting For (yes i have had problems giving examples
 which ended becoming dogma in documents and people modifying them a little
-to look clever);
+to look clever); 
 
-i selected the metering rates to be small so that i can show better how
+i selected the metering rates to be small so that i can show better how 
 things work.
+ 
+The script below does the following: 
+- an incoming packet from 10.0.0.21 is first given a firewall mark of 1. 
 
-The script below does the following:
-- an incoming packet from 10.0.0.21 is first given a firewall mark of 1.
+- It is then metered to make sure it does not exceed its allocated rate of 
+1Kbps. If it doesnt exceed rate, this is where we terminate action execution.
 
-- It is then metered to make sure it does not exceed its allocated rate of
-1Kbps. If it doesn't exceed rate, this is where we terminate action execution.
-
-- If it does exceed its rate, its "color" changes to a mark of 2 and it is
+- If it does exceed its rate, its "color" changes to a mark of 2 and it is 
 then passed through a second meter.
 
--The second meter is shared across all flows on that device [i am surpised
-that this seems to be not a well know feature of the policer; Bert was telling
+-The second meter is shared across all flows on that device [i am suprised 
+that this seems to be not a well know feature of the policer; Bert was telling 
 me that someone was writing a qdisc just to do sharing across multiple devices;
 it must be the summer heat again; weve had someone doing that every year around
-summer  -- the key to sharing is to use a operator "index" in your policer
-rules (example "index 20"). All your rules have to use the same index to
+summer  -- the key to sharing is to use a operator "index" in your policer 
+rules (example "index 20"). All your rules have to use the same index to 
 share.]
-
+ 
 -If the second meter is exceeded the color of the flow changes further to 3.
 
 -We then pass the packet to another meter which is shared across all devices
 in the system. If this meter is exceeded we drop the packet.
 
-Note the mark can be used further up the system to do things like policy
+Note the mark can be used further up the system to do things like policy 
 or more interesting things on the egress.
 
 ------------------ cut here -------------------------------
@@ -145,7 +145,7 @@
 action ipt -j mark --set-mark 1 index 2 \
 #
 # then pass it through a policer which allows 1kbps; if the flow
-# doesn't exceed that rate, this is where we stop, if it exceeds we
+# doesnt exceed that rate, this is where we stop, if it exceeds we
 # pipe the packet to the next action
 action police rate 1kbit burst 9k pipe \
 #
@@ -161,31 +161,31 @@
 # and then attempt to borrow from a meter used by all devices in the
 # system. Should this be exceeded, drop the packet on the floor.
 action police index 20 mtu 5000 rate 1kbit burst 90k drop
----------------------------------
+--------------------------------- 
 
-Now lets see the actions installed with
+Now lets see the actions installed with 
 "tc filter show parent ffff: dev eth0"
 
 -------- output -----------
 jroot# tc filter show parent ffff: dev eth0
-filter protocol ip pref 1 u32
-filter protocol ip pref 1 u32 fh 800: ht divisor 1
-filter protocol ip pref 1 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:15
+filter protocol ip pref 1 u32 
+filter protocol ip pref 1 u32 fh 800: ht divisor 1 
+filter protocol ip pref 1 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:15 
 
-   action order 1: tablename: mangle  hook: NF_IP_PRE_ROUTING
+   action order 1: tablename: mangle  hook: NF_IP_PRE_ROUTING 
         target MARK set 0x1  index 2
 
-   action order 2: police 1 action pipe rate 1Kbit burst 9Kb mtu 2Kb
+   action order 2: police 1 action pipe rate 1Kbit burst 9Kb mtu 2Kb 
 
-   action order 3: tablename: mangle  hook: NF_IP_PRE_ROUTING
+   action order 3: tablename: mangle  hook: NF_IP_PRE_ROUTING 
         target MARK set 0x2  index 1
 
-   action order 4: police 30 action pipe rate 1Kbit burst 10Kb mtu 5000b
+   action order 4: police 30 action pipe rate 1Kbit burst 10Kb mtu 5000b 
 
-   action order 5: tablename: mangle  hook: NF_IP_PRE_ROUTING
+   action order 5: tablename: mangle  hook: NF_IP_PRE_ROUTING 
         target MARK set 0x3  index 3
 
-   action order 6: police 20 action drop rate 1Kbit burst 90Kb mtu 5000b
+   action order 6: police 20 action drop rate 1Kbit burst 90Kb mtu 5000b 
 
   match 0a000015/ffffffff at 12
 -------------------------------
@@ -209,31 +209,31 @@
 
 --------------
 jroot# tc -s filter show parent ffff: dev eth0
-filter protocol ip pref 1 u32
-filter protocol ip pref 1 u32 fh 800: ht divisor 1
+filter protocol ip pref 1 u32 
+filter protocol ip pref 1 u32 fh 800: ht divisor 1 
 filter protocol ip pref 1 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1
-5
+5 
 
-   action order 1: tablename: mangle  hook: NF_IP_PRE_ROUTING
+   action order 1: tablename: mangle  hook: NF_IP_PRE_ROUTING 
         target MARK set 0x1  index 2
-         Sent 188832 bytes 2248 pkts (dropped 0, overlimits 0)
+         Sent 188832 bytes 2248 pkts (dropped 0, overlimits 0) 
 
-   action order 2: police 1 action pipe rate 1Kbit burst 9Kb mtu 2Kb
-         Sent 188832 bytes 2248 pkts (dropped 0, overlimits 2122)
+   action order 2: police 1 action pipe rate 1Kbit burst 9Kb mtu 2Kb 
+         Sent 188832 bytes 2248 pkts (dropped 0, overlimits 2122) 
 
-   action order 3: tablename: mangle  hook: NF_IP_PRE_ROUTING
+   action order 3: tablename: mangle  hook: NF_IP_PRE_ROUTING 
         target MARK set 0x2  index 1
-         Sent 178248 bytes 2122 pkts (dropped 0, overlimits 0)
+         Sent 178248 bytes 2122 pkts (dropped 0, overlimits 0) 
 
-   action order 4: police 30 action pipe rate 1Kbit burst 10Kb mtu 5000b
-         Sent 178248 bytes 2122 pkts (dropped 0, overlimits 1945)
+   action order 4: police 30 action pipe rate 1Kbit burst 10Kb mtu 5000b 
+         Sent 178248 bytes 2122 pkts (dropped 0, overlimits 1945) 
 
-   action order 5: tablename: mangle  hook: NF_IP_PRE_ROUTING
+   action order 5: tablename: mangle  hook: NF_IP_PRE_ROUTING 
         target MARK set 0x3  index 3
-         Sent 163380 bytes 1945 pkts (dropped 0, overlimits 0)
+         Sent 163380 bytes 1945 pkts (dropped 0, overlimits 0) 
 
-   action order 6: police 20 action drop rate 1Kbit burst 90Kb mtu 5000b
-         Sent 163380 bytes 1945 pkts (dropped 0, overlimits 437)
+   action order 6: police 20 action drop rate 1Kbit burst 90Kb mtu 5000b 
+         Sent 163380 bytes 1945 pkts (dropped 0, overlimits 437) 
 
   match 0a000015/ffffffff at 12
 -------------------------------
@@ -241,7 +241,7 @@
 Neat, eh?
 
 
-Want to  write an action module?
+Wanna write an action module?
 ------------------------------
 Its easy. Either look at the code or send me email. I will document at
 some point; will also accept documentation.
@@ -254,3 +254,4 @@
 Expect new things in the spurious time i have to work on this
 (particularly around end of year when i have typically get time off
 from work).
+
diff --git a/doc/actions/gact-usage b/doc/actions/gact-usage
index 5fc3e62..de1308d 100644
--- a/doc/actions/gact-usage
+++ b/doc/actions/gact-usage
@@ -1,13 +1,13 @@
 
 gact <ACTION> [RAND] [INDEX]
 
-Where:
-	ACTION := reclassify | drop | continue | pass | ok
+Where: 
+	ACTION := reclassify | drop | continue | pass | ok 
 	RAND := random <RANDTYPE> <ACTION> <VAL>
 	RANDTYPE := netrand | determ
         VAL : = value not exceeding 10000
         INDEX := index value used
-
+      
 ACTION semantics
 - pass and ok are equivalent to accept
 - continue allows to restart classification lookup
@@ -42,14 +42,14 @@
          random type none pass val 0
          index 1 ref 1 bind 1 installed 59 sec used 35 sec
          Sent 1680 bytes 20 pkts (dropped 20, overlimits 0 )
-
+ 
 ----
 
 # example 2
 #allow 1 out 10 randomly using the netrand generator
 tc filter add dev eth0 parent ffff: protocol ip prio 6 u32 match ip src \
 10.0.0.9/32 flowid 1:16 action drop random netrand ok 10
-
+ 
 ping -c 20 10.0.0.9
 
 ----
@@ -59,14 +59,14 @@
          random type netrand pass val 10
          index 5 ref 1 bind 1 installed 49 sec used 25 sec
          Sent 1680 bytes 20 pkts (dropped 16, overlimits 0 )
-
+                                                                                
 --------
 #alternative: deterministically accept every second packet
 tc filter add dev eth0 parent ffff: protocol ip prio 6 u32 match ip src \
 10.0.0.9/32 flowid 1:16 action drop random determ ok 2
-
+                                                                                
 ping -c 20 10.0.0.9
-
+                                                                                
 tc -s filter show parent ffff: dev eth0
 -----
 filter protocol ip pref 6 u32 filter protocol ip pref 6 u32 fh 800: ht divisor 1filter protocol ip pref 6 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:16  (rule hit 20 success 20)
@@ -76,3 +76,4 @@
          index 4 ref 1 bind 1 installed 118 sec used 82 sec
          Sent 1680 bytes 20 pkts (dropped 10, overlimits 0 )
 -----
+
diff --git a/doc/actions/ifb-README b/doc/actions/ifb-README
index 5fe9171..3d01179 100644
--- a/doc/actions/ifb-README
+++ b/doc/actions/ifb-README
@@ -6,47 +6,47 @@
 Known IMQ/IFB USES
 ------------------
 
-As far as i know the reasons listed below is why people use IMQ.
+As far as i know the reasons listed below is why people use IMQ. 
 It would be nice to know of anything else that i missed.
 
 1) qdiscs/policies that are per device as opposed to system wide.
 IFB allows for sharing.
 
 2) Allows for queueing incoming traffic for shaping instead of
-dropping. I am not aware of any study that shows policing is
+dropping. I am not aware of any study that shows policing is 
 worse than shaping in achieving the end goal of rate control.
 I would be interested if anyone is experimenting.
 
-3) Very interesting use: if you are serving p2p you may want to give
-preference to your own locally originated traffic (when responses come back)
+3) Very interesting use: if you are serving p2p you may wanna give 
+preference to your own localy originated traffic (when responses come back)
 vs someone using your system to do bittorent. So QoSing based on state
-comes in as the solution. What people did to achieve this was stick
+comes in as the solution. What people did to achive this was stick
 the IMQ somewhere prelocal hook.
 I think this is a pretty neat feature to have in Linux in general.
 (i.e not just for IMQ).
-But i won't go back to putting netfilter hooks in the device to satisfy
-this.  I also don't think its worth it hacking ifb some more to be
+But i wont go back to putting netfilter hooks in the device to satisfy
+this.  I also dont think its worth it hacking ifb some more to be 
 aware of say L3 info and play ip rule tricks to achieve this.
---> Instead the plan is to have a conntrack related action. This action will
-selectively either query/create conntrack state on incoming packets.
-Packets could then be redirected to ifb based on what happens -> eg
-on incoming packets; if we find they are of known state we could send to
-a different queue than one which didn't have existing state. This
+--> Instead the plan is to have a contrack related action. This action will
+selectively either query/create contrack state on incoming packets. 
+Packets could then be redirected to ifb based on what happens -> eg 
+on incoming packets; if we find they are of known state we could send to 
+a different queue than one which didnt have existing state. This
 all however is dependent on whatever rules the admin enters.
 
 At the moment this 3rd function does not exist yet. I have decided that
-instead of sitting on the patch for another year, to release it and then
-if there is pressure i will add this feature.
+instead of sitting on the patch for another year, to release it and then 
+if theres pressure i will add this feature.
 
 An example, to provide functionality that most people use IMQ for below:
 
 --------
 export TC="/sbin/tc"
 
-$TC qdisc add dev ifb0 root handle 1: prio
+$TC qdisc add dev ifb0 root handle 1: prio 
 $TC qdisc add dev ifb0 parent 1:1 handle 10: sfq
 $TC qdisc add dev ifb0 parent 1:2 handle 20: tbf rate 20kbit buffer 1600 limit 3000
-$TC qdisc add dev ifb0 parent 1:3 handle 30: sfq
+$TC qdisc add dev ifb0 parent 1:3 handle 30: sfq                                
 $TC filter add dev ifb0 protocol ip pref 1 parent 1: handle 1 fw classid 1:1
 $TC filter add dev ifb0 protocol ip pref 2 parent 1: handle 2 fw classid 1:2
 
@@ -54,7 +54,7 @@
 
 $TC qdisc add dev eth0 ingress
 
-# redirect all IP packets arriving in eth0 to ifb0
+# redirect all IP packets arriving in eth0 to ifb0 
 # use mark 1 --> puts them onto class 1:1
 $TC filter add dev eth0 parent ffff: protocol ip prio 10 u32 \
 match u32 0 0 flowid 1:1 \
@@ -77,44 +77,44 @@
 --- 10.22 ping statistics ---
 3 packets transmitted, 3 packets received, 0% packet loss
 round-trip min/avg/max = 0.6/1.3/2.8 ms
-[root@jzny action-tests]#
+[root@jzny action-tests]# 
 -----
 Now look at some stats:
 
 ---
 [root@jmandrake]:~# $TC -s filter show parent ffff: dev eth0
-filter protocol ip pref 10 u32
-filter protocol ip pref 10 u32 fh 800: ht divisor 1
-filter protocol ip pref 10 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1
+filter protocol ip pref 10 u32 
+filter protocol ip pref 10 u32 fh 800: ht divisor 1 
+filter protocol ip pref 10 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 
   match 00000000/00000000 at 0
-        action order 1: tablename: mangle  hook: NF_IP_PRE_ROUTING
-        target MARK set 0x1
-        index 1 ref 1 bind 1 installed 4195sec  used 27sec
-         Sent 252 bytes 3 pkts (dropped 0, overlimits 0)
+        action order 1: tablename: mangle  hook: NF_IP_PRE_ROUTING 
+        target MARK set 0x1  
+        index 1 ref 1 bind 1 installed 4195sec  used 27sec 
+         Sent 252 bytes 3 pkts (dropped 0, overlimits 0) 
 
         action order 2: mirred (Egress Redirect to device ifb0) stolen
         index 1 ref 1 bind 1 installed 165 sec used 27 sec
-         Sent 252 bytes 3 pkts (dropped 0, overlimits 0)
+         Sent 252 bytes 3 pkts (dropped 0, overlimits 0) 
 
 [root@jmandrake]:~# $TC -s qdisc
-qdisc sfq 30: dev ifb0 limit 128p quantum 1514b
- Sent 0 bytes 0 pkts (dropped 0, overlimits 0)
-qdisc tbf 20: dev ifb0 rate 20Kbit burst 1575b lat 2147.5s
- Sent 210 bytes 3 pkts (dropped 0, overlimits 0)
-qdisc sfq 10: dev ifb0 limit 128p quantum 1514b
- Sent 294 bytes 3 pkts (dropped 0, overlimits 0)
+qdisc sfq 30: dev ifb0 limit 128p quantum 1514b 
+ Sent 0 bytes 0 pkts (dropped 0, overlimits 0) 
+qdisc tbf 20: dev ifb0 rate 20Kbit burst 1575b lat 2147.5s 
+ Sent 210 bytes 3 pkts (dropped 0, overlimits 0) 
+qdisc sfq 10: dev ifb0 limit 128p quantum 1514b 
+ Sent 294 bytes 3 pkts (dropped 0, overlimits 0) 
 qdisc prio 1: dev ifb0 bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
- Sent 504 bytes 6 pkts (dropped 0, overlimits 0)
-qdisc ingress ffff: dev eth0 ----------------
- Sent 308 bytes 5 pkts (dropped 0, overlimits 0)
+ Sent 504 bytes 6 pkts (dropped 0, overlimits 0) 
+qdisc ingress ffff: dev eth0 ---------------- 
+ Sent 308 bytes 5 pkts (dropped 0, overlimits 0) 
 
 [root@jmandrake]:~# ifconfig ifb0
-ifb0    Link encap:Ethernet  HWaddr 00:00:00:00:00:00
+ifb0    Link encap:Ethernet  HWaddr 00:00:00:00:00:00  
           inet6 addr: fe80::200:ff:fe00:0/64 Scope:Link
           UP BROADCAST RUNNING NOARP  MTU:1500  Metric:1
           RX packets:6 errors:0 dropped:3 overruns:0 frame:0
           TX packets:3 errors:0 dropped:0 overruns:0 carrier:0
-          collisions:0 txqueuelen:32
+          collisions:0 txqueuelen:32 
           RX bytes:504 (504.0 b)  TX bytes:252 (252.0 b)
 -----
 
diff --git a/doc/actions/mirred-usage b/doc/actions/mirred-usage
index 482ff66..2622c43 100644
--- a/doc/actions/mirred-usage
+++ b/doc/actions/mirred-usage
@@ -7,10 +7,10 @@
 on more than just a port (eg a 5 tuple classifier). They may also be
 capable of redirecting.
 
-Usage:
+Usage: 
 
-mirred <DIRECTION> <ACTION> [index INDEX] <dev DEVICENAME>
-where:
+mirred <DIRECTION> <ACTION> [index INDEX] <dev DEVICENAME> 
+where: 
 DIRECTION := <ingress | egress>
 ACTION := <mirror | redirect>
 INDEX is the specific policy instance id
@@ -18,7 +18,7 @@
 
 Direction:
 - Ingress is not supported at the moment. It will be in the
-future as well as mirror/redirecting to a socket.
+future as well as mirror/redirecting to a socket. 
 
 Action:
 - Mirror takes a copy of the packet and sends it to specified
@@ -26,17 +26,17 @@
 - redirect
 steals the packet and redirects to specified destination dev.
 
-What NOT to do if you don't want your machine to crash:
+What NOT to do if you dont want your machine to crash:
 ------------------------------------------------------
 
-Do not create loops!
+Do not create loops! 
 Loops are not hard to create in the egress qdiscs.
 
-Here are simple rules to follow if you don't want to get
+Here are simple rules to follow if you dont want to get
 hurt:
 A) Do not have the same packet go to same netdevice twice
 in a single graph of policies. Your machine will just hang!
-This is design intent _not a bug_ to teach you some lessons.
+This is design intent _not a bug_ to teach you some lessons. 
 
 In the future if there are easy ways to do this in the kernel
 without affecting other packets not interested in this feature
@@ -51,7 +51,7 @@
 Remember that IFB is a very specialized case of packet redirecting
 device. Instead of redirecting it puts packets at the exact spot
 on the stack it found them from.
-Redirecting from ifbX->ifbY will actually not crash your machine but your
+Redirecting from ifbX->ifbY will actually not crash your machine but your 
 packets will all be dropped (this is much simpler to detect
 and resolve and is only affecting users of ifb as opposed to the
 whole stack).
@@ -64,7 +64,7 @@
 
 1) Mirror all packets arriving on eth0 to be sent out on eth1.
 You may have a sniffer or some accounting box hooked up on eth1.
-
+ 
 ---
 tc qdisc add dev eth0 ingress
 tc filter add dev eth0 parent ffff: protocol ip prio 10 u32 \
@@ -100,7 +100,7 @@
 3) Even more funky example:
 
 #
-#allow 1 out 10 packets on ingress of lo to randomly make it to the
+#allow 1 out 10 packets on ingress of lo to randomly make it to the 
 # host A (Randomness uses the netrand generator)
 #
 ---
@@ -111,9 +111,9 @@
 ---
 
 4)
-# for packets from 10.0.0.9 going out on eth0 (could be local
-# IP or something # we are forwarding) -
-# if exceeding a 100Kbps rate, then redirect to eth1
+# for packets from 10.0.0.9 going out on eth0 (could be local 
+# IP or something # we are forwarding) - 
+# if exceeding a 100Kbps rate, then redirect to eth1 
 #
 
 ---
@@ -129,7 +129,7 @@
 This is a very useful debug feature.
 
 Lets say you are policing packets from alias 192.168.200.200/32
-you don't want those to exceed 100kbps going out.
+you dont want those to exceed 100kbps going out.
 
 ---
 tc qdisc add dev eth0 handle 1:0 root prio
@@ -158,7 +158,7 @@
 BSDs speacialized log device does without needing one).
 
 If you replace mirror with redirect, those packets will be
-blackholed and will never make it out.
+blackholed and will never make it out. 
 
 cheers,
 jamal
diff --git a/doc/api-ip6-flowlabels.tex b/doc/api-ip6-flowlabels.tex
new file mode 100644
index 0000000..aa34e94
--- /dev/null
+++ b/doc/api-ip6-flowlabels.tex
@@ -0,0 +1,429 @@
+\documentstyle[12pt,twoside]{article}
+\def\TITLE{IPv6 Flow Labels}
+\input preamble
+\begin{center}
+\Large\bf IPv6 Flow Labels in Linux-2.2.
+\end{center}
+
+
+\begin{center}
+{ \large Alexey~N.~Kuznetsov } \\
+\em Institute for Nuclear Research, Moscow \\
+\verb|kuznet@ms2.inr.ac.ru| \\
+\rm April 11, 1999
+\end{center}
+
+\vspace{5mm}
+
+\tableofcontents
+
+\section{Introduction.}
+
+Every IPv6 packet carries 28 bits of flow information. RFC2460 splits
+these bits to two fields: 8 bits of traffic class (or DS field, if you
+prefer this term) and 20 bits of flow label. Currently there exist
+no well-defined API to manage IPv6 flow information. In this document
+I describe an attempt to design the API for Linux-2.2 IPv6 stack.
+
+\vskip 1mm
+
+The API must solve the following tasks:
+
+\begin{enumerate}
+
+\item To allow user to set traffic class bits.
+
+\item To allow user to read traffic class bits of received packets.
+This feature is not so useful as the first one, however it will be
+necessary f.e.\ to implement ECN [RFC2481] for datagram oriented services
+or to implement receiver side of SRP or another end-to-end protocol
+using traffic class bits.
+
+\item To assign flow labels to packets sent by user.
+
+\item To get flow labels of received packets. I do not know
+any applications of this feature, but it is possible that receiver will
+want to use flow labels to distinguish sub-flows.
+
+\item To allocate flow labels in the way, compliant to RFC2460. Namely:
+
+\begin{itemize}
+\item
+Flow labels must be uniformly distributed (pseudo-)random numbers,
+so that any subset of 20 bits can be used as hash key.
+
+\item
+Flows with coinciding source address and flow label must have identical
+destination address and not-fragmentable extensions headers (i.e.\ 
+hop by hop options and all the headers up to and including routing header,
+if it is present.)
+
+\begin{NB}
+There is a hole in specs: some hop-by-hop options can be
+defined only on per-packet base (f.e.\  jumbo payload option).
+Essentially, it means that such options cannot present in packets
+with flow labels.
+\end{NB}
+\begin{NB}
+NB notes here and below reflect only my personal opinion,
+they should be read with smile or should not be read at all :-).
+\end{NB}
+
+
+\item
+Flow labels have finite lifetime and source is not allowed to reuse
+flow label for another flow within the maximal lifetime has expired,
+so that intermediate nodes will be able to invalidate flow state before
+the label is taken over by another flow.
+Flow state, including lifetime, is propagated along datagram path
+by some application specific methods
+(f.e.\ in RSVP PATH messages or in some hop-by-hop option).
+
+
+\end{itemize}
+
+\end{enumerate}
+
+\section{Sending/receiving flow information.}
+
+\paragraph{Discussion.}
+\addcontentsline{toc}{subsection}{Discussion}
+It was proposed (Where? I do not remember any explicit statement)
+to solve the first four tasks using
+\verb|sin6_flowinfo| field added to \verb|struct| \verb|sockaddr_in6|
+(see RFC2553).
+
+\begin{NB}
+	This method is difficult to consider as reasonable, because it
+	puts additional overhead to all the services, despite of only
+	very small subset of them (none, to be more exact) really use it.
+	It contradicts both to IETF spirit and the letter. Before RFC2553
+	one justification existed, IPv6 address alignment left 4 byte
+	hole in \verb|sockaddr_in6| in any case. Now it has no justification.
+\end{NB}
+
+We have two problems with this method. The first one is common for all OSes:
+if \verb|recvmsg()| initializes \verb|sin6_flowinfo| to flow info
+of received packet, we loose one very important property of BSD socket API,
+namely, we are not allowed to use received address for reply directly
+and have to mangle it, even if we are not interested in flowinfo subtleties.
+
+\begin{NB}
+	RFC2553 adds new requirement: to clear \verb|sin6_flowinfo|.
+	Certainly, it is not solution but rather attempt to force applications
+	to make unnecessary work. Well, as usually, one mistake in design
+	is followed by attempts	to patch the hole and more mistakes...
+\end{NB}
+
+Another problem is Linux specific. Historically Linux IPv6 did not
+initialize \verb|sin6_flowinfo| at all, so that, if kernel does not
+support flow labels, this field is not zero, but a random number.
+Some applications also did not take care about it. 
+
+\begin{NB}
+Following RFC2553 such applications can be considered as broken,
+but I still think that they are right: clearing all the address
+before filling known fields is robust but stupid solution.
+Useless wasting CPU cycles and
+memory bandwidth is not a good idea. Such patches are acceptable
+as temporary hacks, but not as standard of the future.
+\end{NB}
+
+
+\paragraph{Implementation.}
+\addcontentsline{toc}{subsection}{Implementation}
+By default Linux IPv6 does not read \verb|sin6_flowinfo| field
+assuming that common applications are not obliged to initialize it
+and are permitted to consider it as pure alignment padding.
+In order to tell kernel that application
+is aware of this field, it is necessary to set socket option
+\verb|IPV6_FLOWINFO_SEND|.
+
+\begin{verbatim}
+  int on = 1;
+  setsockopt(sock, SOL_IPV6, IPV6_FLOWINFO_SEND,
+             (void*)&on, sizeof(on));
+\end{verbatim}
+
+Linux kernel never fills \verb|sin6_flowinfo| field, when passing
+message to user space, though the kernels which support flow labels
+initialize it to zero. If user wants to get received flowinfo, he
+will set option \verb|IPV6_FLOWINFO| and after this he will receive
+flowinfo as ancillary data object of type \verb|IPV6_FLOWINFO|
+(cf.\ RFC2292).
+
+\begin{verbatim}
+  int on = 1;
+  setsockopt(sock, SOL_IPV6, IPV6_FLOWINFO, (void*)&on, sizeof(on));
+\end{verbatim}
+
+Flowinfo received and latched by a connected TCP socket also may be fetched
+with \verb|getsockopt()| \verb|IPV6_PKTOPTIONS| together with
+another optional information.
+
+Besides that, in the spirit of RFC2292 the option \verb|IPV6_FLOWINFO|
+may be used as alternative way to send flowinfo with \verb|sendmsg()| or
+to latch it with \verb|IPV6_PKTOPTIONS|.
+
+\paragraph{Note about IPv6 options and destination address.}
+\addcontentsline{toc}{subsection}{IPv6 options and destination address}
+If \verb|sin6_flowinfo| does contain not zero flow label,
+destination address in \verb|sin6_addr| and non-fragmentable
+extension headers are ignored. Instead, kernel uses the values
+cached at flow setup (see below). However, for connected sockets
+kernel prefers the values set at connection time.
+
+\paragraph{Example.}
+\addcontentsline{toc}{subsection}{Example}
+After setting socket option \verb|IPV6_FLOWINFO|
+flowlabel and DS field are received as ancillary data object
+of type \verb|IPV6_FLOWINFO| and level \verb|SOL_IPV6|.
+In the cases when it is convenient to use \verb|recvfrom(2)|,
+it is possible to replace library variant with your own one,
+sort of:
+
+\begin{verbatim}
+#include <sys/socket.h>
+#include <netinet/in6.h>
+
+size_t recvfrom(int fd, char *buf, size_t len, int flags,
+                struct sockaddr *addr, int *addrlen)
+{
+  size_t cc;
+  char cbuf[128];
+  struct cmsghdr *c;
+  struct iovec iov = { buf, len };
+  struct msghdr msg = { addr, *addrlen,
+                        &iov,  1,
+                        cbuf, sizeof(cbuf),
+                        0 };
+
+  cc = recvmsg(fd, &msg, flags);
+  if (cc < 0)
+    return cc;
+  ((struct sockaddr_in6*)addr)->sin6_flowinfo = 0;
+  *addrlen = msg.msg_namelen;
+  for (c=CMSG_FIRSTHDR(&msg); c; c = CMSG_NEXTHDR(&msg, c)) {
+    if (c->cmsg_level != SOL_IPV6 ||
+      c->cmsg_type != IPV6_FLOWINFO)
+        continue;
+    ((struct sockaddr_in6*)addr)->sin6_flowinfo = *(__u32*)CMSG_DATA(c);
+  }
+  return cc;
+}
+\end{verbatim}
+
+
+
+\section{Flow label management.}
+
+\paragraph{Discussion.}
+\addcontentsline{toc}{subsection}{Discussion}
+Requirements of RFC2460 are pretty tough. Particularly, lifetimes
+longer than boot time require to store allocated labels at stable
+storage, so that the full implementation necessarily includes user space flow
+label manager. There are at least three different approaches:
+
+\begin{enumerate}
+\item {\bf ``Cooperative''. } We could leave flow label allocation wholly
+to user space. When user needs label he requests manager directly. The approach
+is valid, but as any ``cooperative'' approach it suffers of security problems.
+
+\begin{NB}
+One idea is to disallow not privileged user to allocate flow
+labels, but instead to pass the socket to manager via \verb|SCM_RIGHTS|
+control message, so that it will allocate label and assign it to socket
+itself. Hmm... the idea is interesting.
+\end{NB}
+
+\item {\bf ``Indirect''.} Kernel redirects requests to user level daemon
+and does not install label until the daemon acknowledged the request.
+The approach is the most promising, it is especially pleasant to recognize
+parallel with IPsec API [RFC2367,Craig]. Actually, it may share API with
+IPsec.
+
+\item {\bf ``Stupid''.} To allocate labels in kernel space. It is the simplest
+method, but it suffers of two serious flaws: the first,
+we cannot lease labels with lifetimes longer than boot time, the second, 
+it is sensitive to DoS attacks. Kernel have to remember all the obsolete
+labels until their expiration and malicious user may fastly eat all the
+flow label space.
+
+\end{enumerate}
+
+Certainly, I choose the most ``stupid'' method. It is the cheapest one
+for implementor (i.e.\ me), and taking into account that flow labels
+still have no serious applications it is not useful to work on more
+advanced API, especially, taking into account that eventually we
+will get it for no fee together with IPsec.
+
+
+\paragraph{Implementation.}
+\addcontentsline{toc}{subsection}{Implementation}
+Socket option \verb|IPV6_FLOWLABEL_MGR| allows to
+request flow label manager to allocate new flow label, to reuse
+already allocated one or to delete old flow label.
+Its argument is \verb|struct| \verb|in6_flowlabel_req|:
+
+\begin{verbatim}
+struct in6_flowlabel_req
+{
+        struct in6_addr flr_dst;
+        __u32           flr_label;
+        __u8            flr_action;
+        __u8            flr_share;
+        __u16           flr_flags;
+        __u16           flr_expires;
+        __u16           flr_linger;
+        __u32         __flr_reserved;
+        /* Options in format of IPV6_PKTOPTIONS */
+};
+\end{verbatim}
+
+\begin{itemize}
+
+\item \verb|dst| is IPv6 destination address associated with the label.
+
+\item \verb|label| is flow label value in network byte order. If it is zero,
+kernel will allocate new pseudo-random number. Otherwise, kernel will try
+to lease flow label ordered by user. In this case, it is user task to provide
+necessary flow label randomness.
+
+\item \verb|action| is requested operation. Currently, only three operations
+are defined:
+
+\begin{verbatim}
+#define IPV6_FL_A_GET   0   /* Get flow label */
+#define IPV6_FL_A_PUT   1   /* Release flow label */
+#define IPV6_FL_A_RENEW 2   /* Update expire time */
+\end{verbatim}
+
+\item \verb|flags| are optional modifiers. Currently
+only \verb|IPV6_FL_A_GET| has modifiers:
+
+\begin{verbatim}
+#define IPV6_FL_F_CREATE 1   /* Allowed to create new label */
+#define IPV6_FL_F_EXCL   2   /* Do not create new label */
+\end{verbatim}
+
+
+\item \verb|share| defines who is allowed to reuse the same flow label.
+
+\begin{verbatim}
+#define IPV6_FL_S_NONE    0   /* Not defined */
+#define IPV6_FL_S_EXCL    1   /* Label is private */
+#define IPV6_FL_S_PROCESS 2   /* May be reused by this process */
+#define IPV6_FL_S_USER    3   /* May be reused by this user */
+#define IPV6_FL_S_ANY     255 /* Anyone may reuse it */
+\end{verbatim}
+
+\item \verb|linger| is time in seconds. After the last user releases flow
+label, it will not be reused with different destination and options at least
+during this time. If \verb|share| is not \verb|IPV6_FL_S_EXCL| the label
+still can be shared by another sockets. Current implementation does not allow
+unprivileged user to set linger longer than 60 sec.
+
+\item \verb|expires| is time in seconds. Flow label will be kept at least
+for this time, but it will not be destroyed before user released it explicitly
+or closed all the sockets using it. Current implementation does not allow
+unprivileged user to set timeout longer than 60 sec. Proviledged applications
+MAY set longer lifetimes, but in this case they MUST save allocated
+labels at stable storage and restore them back after reboot before the first
+application allocates new flow.
+
+\end{itemize}
+
+This structure is followed by optional extension headers associated
+with this flow label in format of \verb|IPV6_PKTOPTIONS|. Only
+\verb|IPV6_HOPOPTS|, \verb|IPV6_RTHDR| and, if \verb|IPV6_RTHDR| presents,
+\verb|IPV6_DSTOPTS| are allowed.
+
+\paragraph{Example.}
+\addcontentsline{toc}{subsection}{Example}
+ The function \verb|get_flow_label| allocates
+private flow label.
+
+\begin{verbatim}
+int get_flow_label(int fd, struct sockaddr_in6 *dst, __u32 fl)
+{
+        int on = 1;
+        struct in6_flowlabel_req freq;
+
+        memset(&freq, 0, sizeof(freq));
+        freq.flr_label = htonl(fl);
+        freq.flr_action = IPV6_FL_A_GET;
+        freq.flr_flags = IPV6_FL_F_CREATE | IPV6_FL_F_EXCL;
+        freq.flr_share = IPV6_FL_S_EXCL;
+        memcpy(&freq.flr_dst, &dst->sin6_addr, 16);
+        if (setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR,
+                       &freq, sizeof(freq)) == -1) {
+                perror ("can't lease flowlabel");
+                return -1;
+        }
+        dst->sin6_flowinfo |= freq.flr_label;
+
+        if (setsockopt(fd, SOL_IPV6, IPV6_FLOWINFO_SEND,
+                       &on, sizeof(on)) == -1) {
+                perror ("can't send flowinfo");
+
+                freq.flr_action = IPV6_FL_A_PUT;
+                setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR,
+                           &freq, sizeof(freq));
+                return -1;
+        }
+        return 0;
+}
+\end{verbatim}
+
+A bit more complicated example using routing header can be found
+in \verb|ping6| utility (\verb|iputils| package). Linux rsvpd backend
+contains an example of using operation \verb|IPV6_FL_A_RENEW|.
+
+\paragraph{Listing flow labels.} 
+\addcontentsline{toc}{subsection}{Listing flow labels}
+List of currently allocated
+flow labels may be read from \verb|/proc/net/ip6_flowlabel|.
+
+\begin{verbatim}
+Label S Owner Users Linger Expires Dst                              Opt
+A1BE5 1 0     0     6      3       3ffe2400000000010a0020fffe71fb30 0
+\end{verbatim}
+
+\begin{itemize}
+\item \verb|Label| is hexadecimal flow label value.
+\item \verb|S| is sharing style.
+\item \verb|Owner| is ID of creator, it is zero, pid or uid, depending on
+		sharing style.
+\item \verb|Users| is number of applications using the label now.
+\item \verb|Linger| is \verb|linger| of this label in seconds.
+\item \verb|Expires| is time until expiration of the label in seconds. It may
+	be negative, if the label is in use.
+\item \verb|Dst| is IPv6 destination address.
+\item \verb|Opt| is length of options, associated with the label. Option
+	data are not accessible.
+\end{itemize}
+
+
+\paragraph{Flow labels and RSVP.} 
+\addcontentsline{toc}{subsection}{Flow labels and RSVP}
+RSVP daemon supports IPv6 flow labels
+without any modifications to standard ISI RAPI. Sender must allocate
+flow label, fill corresponding sender template and submit it to local rsvp
+daemon. rsvpd will check the label and start to announce it in PATH
+messages. Rsvpd on sender node will renew the flow label, so that it will not
+be reused before path state expires and all the intermediate
+routers and receiver purge flow state.
+
+\verb|rtap| utility is modified to parse flow labels. F.e.\ if user allocated
+flow label \verb|0xA1234|, he may write:
+
+\begin{verbatim}
+RTAP> sender 3ffe:2400::1/FL0xA1234 <Tspec>
+\end{verbatim}
+
+Receiver makes reservation with command:
+\begin{verbatim}
+RTAP> reserve ff 3ffe:2400::1/FL0xA1234 <Flowspec>
+\end{verbatim}
+
+\end{document}
diff --git a/doc/arpd.sgml b/doc/arpd.sgml
new file mode 100644
index 0000000..0ab79c6
--- /dev/null
+++ b/doc/arpd.sgml
@@ -0,0 +1,130 @@
+<!doctype linuxdoc system>
+
+<article>
+
+<title>ARPD Daemon
+<author>Alexey Kuznetsov, <tt/kuznet@ms2.inr.ac.ru/
+<date>some_negative_number, 20 Sep 2001
+<abstract>
+<tt/arpd/ is daemon collecting gratuitous ARP information, saving
+it on local disk and feeding it to kernel on demand to avoid
+redundant broadcasting due to limited size of kernel ARP cache. 
+</abstract>
+
+
+<p><bf/Description/
+
+<p>The format of the command is:
+
+<tscreen><verb>
+       arpd OPTIONS [ INTERFACE [ INTERFACE ... ] ]
+</verb></tscreen>
+
+<p> <tt/OPTIONS/ are:
+
+<itemize>
+
+<item><tt/-l/ - dump <tt/arpd/ database to stdout and exit. Output consists
+of three columns: interface index, IP address and MAC address.
+Negative entries for dead hosts are also shown, in this case MAC address
+is replaced by word <tt/FAILED/ followed by colon and time when the fact
+that host is dead was proven the last time.
+
+<item><tt/-f FILE/  - read and load <tt/arpd/ database from <tt/FILE/
+in text format similar dumped by option <tt/-l/. Exit after load,
+probably listing resulting database, if option <tt/-l/ is also given.
+If <tt/FILE/ is <tt/-/, <tt/stdin/ is read to get ARP table.
+ 
+<item><tt/-b DATABASE/  - location of database file. Default location is
+<tt>/var/lib/arpd/arpd.db</tt>.
+
+<item><tt/-a NUMBER/ - <tt/arpd/ not only passively listens ARP on wire, but
+also send brodcast queries itself. <tt/NUMBER/ is number of such queries
+to make before destination is considered as dead. When <tt/arpd/ is started
+as kernel helper (i.e. with <tt/app_solicit/ enabled in <tt/sysctl/
+or even with option <tt/-k/) without this option and still did not learn enough
+information, you can observe 1 second gaps in service. Not fatal, but
+not good.
+
+<item><tt/-k/ - suppress sending broadcast queries by kernel. It takes
+sense together with option <tt/-a/.
+
+<item><tt/-n TIME/ - timeout of negative cache. When resolution fails <tt/arpd/
+suppresses further attempts to resolve for this period. It makes sense
+only together with option <tt/-k/. This timeout should not be too much
+longer than boot time of a typical host not supporting gratuitous ARP.
+Default value is 60 seconds.
+
+<item><tt/-R RATE/ - maximal steady rate of broadcasts sent by <tt/arpd/
+in packets per second. Default value is 1.
+
+<item><tt/-B NUMBER/ - number of broadcasts sent by <tt/arpd/ back to back.
+Default value is 3. Together with option <tt/-R/ this option allows
+to police broadcasting not to exceed <tt/B+R*T/ over any interval
+of time <tt/T/.
+
+</itemize>
+
+<p><tt/INTERFACE/ is name of networking inteface to watch.
+If no interfaces given, <tt/arpd/ monitors all the interfaces.
+In this case <tt/arpd/ does not adjust <tt/sysctl/ parameters,
+it is supposed user does this himself after <tt/arpd/ is started.
+
+
+<p> Signals
+
+<p> <tt/arpd/ exits gracefully syncing database and restoring adjusted
+<tt/sysctl/ parameters, when receives <tt/SIGINT/ or <tt/SIGTERM/.
+<tt/SIGHUP/ syncs database to disk. <tt/SIGUSR1/ sends some statistics
+to <tt/syslog/. Effect of another signals is undefined, they may corrupt
+database and leave <tt/sysctl/ parameters in an unpredictable state.
+
+<p> Note
+
+<p> In order to <tt/arpd/ be able to serve as ARP resolver, kernel must be
+compiled with the option <tt/CONFIG_ARPD/ and, in the case when interface list
+is not given on command line, variable <tt/app_solicit/
+on interfaces of interest should be set in <tt>/proc/sys/net/ipv4/neigh/*</tt>.
+If this is not made <tt/arpd/ still collects gratuitous ARP information
+in its database.
+
+<p> Examples
+
+<enum>
+<item> Start <tt/arpd/ to collect gratuitous ARP, but not messing
+with kernel functionality:
+
+<tscreen><verb>
+   arpd -b /var/tmp/arpd.db
+</verb></tscreen>
+
+<item> Look at result after some time:
+
+<tscreen><verb>
+   killall arpd
+   arpd -l -b /var/tmp/arpd.db
+</verb></tscreen>
+
+<item> To enable kernel helper, leaving leading role to kernel:
+
+<tscreen><verb>
+   arpd -b /var/tmp/arpd.db -a 1 eth0 eth1
+</verb></tscreen>
+
+<item> Completely replace kernel resolution on interfaces <tt/eth0/
+and <tt/eth1/. In this case kernel still does unicast probing to
+validate entries, but all the broadcast activity is suppressed
+and made under authority of <tt/arpd/: 
+
+<tscreen><verb>
+   arpd -b /var/tmp/arpd.db -a 3 -k eth0 eth1
+</verb></tscreen>
+
+This is mode which <tt/arpd/ is supposed to work normally.
+It is not default just to prevent occasional enabling of too aggressive
+mode occasionally.
+
+</enum>
+
+</article>
+
diff --git a/doc/do-psnup b/doc/do-psnup
new file mode 100644
index 0000000..2dce848
--- /dev/null
+++ b/doc/do-psnup
@@ -0,0 +1,16 @@
+#! /bin/bash
+# $1 = Temporary file . "string"
+# $2 = File to process . "string"
+# $3 = Page size . ie: a4 , letter ... "string"
+# $4 = Number of pages to fit on a single sheet . "numeric"
+
+if type psnup >&/dev/null; then
+	echo "psnup -$4 -p$3 $1 $2"
+	psnup -$4 -p$3 $1 $2
+elif type psmulti >&/dev/null; then
+	echo "psmulti $1 > $2"
+	psmulti $1 > $2
+else
+	echo "cp $1 $2"
+	cp $1 $2
+fi
diff --git a/doc/ip-cref.tex b/doc/ip-cref.tex
new file mode 100644
index 0000000..67094c9
--- /dev/null
+++ b/doc/ip-cref.tex
@@ -0,0 +1,3449 @@
+\documentstyle[12pt,twoside]{article}
+\def\TITLE{IP Command Reference}
+\input preamble
+\begin{center}
+\Large\bf IP Command Reference.
+\end{center}
+
+
+\begin{center}
+{ \large Alexey~N.~Kuznetsov } \\
+\em Institute for Nuclear Research, Moscow \\
+\verb|kuznet@ms2.inr.ac.ru| \\
+\rm April 14, 1999
+\end{center}
+
+\vspace{5mm}
+
+\tableofcontents
+
+\newpage
+
+\section{About this document}
+
+This document presents a comprehensive description of the \verb|ip| utility
+from the \verb|iproute2| package. It is not a tutorial or user's guide.
+It is a {\em dictionary\/}, not explaining terms,
+but translating them into other terms, which may also be unknown to the reader.
+However, the document is self-contained and the reader, provided they have a
+basic networking background, will find enough information
+and examples to understand and configure Linux-2.2 IP and IPv6
+networking.
+
+This document is split into sections explaining \verb|ip| commands
+and options, decrypting \verb|ip| output and containing a few examples.
+More voluminous examples and some topics, which require more elaborate
+discussion, are in the appendix.
+
+The paragraphs beginning with NB contain side notes, warnings about
+bugs and design drawbacks. They may be skipped at the first reading.
+
+\section{{\tt ip} --- command syntax}
+
+The generic form of an \verb|ip| command is:
+\begin{verbatim}
+ip [ OPTIONS ] OBJECT [ COMMAND [ ARGUMENTS ]]
+\end{verbatim}
+where \verb|OPTIONS| is a set of optional modifiers affecting the
+general behaviour of the \verb|ip| utility or changing its output. All options
+begin with the character \verb|'-'| and may be used in either long or abbreviated 
+forms. Currently, the following options are available:
+
+\begin{itemize}
+\item \verb|-V|, \verb|-Version|
+
+--- print the version of the \verb|ip| utility and exit.
+
+
+\item \verb|-s|, \verb|-stats|, \verb|-statistics|
+
+--- output more information. If the option
+appears twice or more, the amount of information increases.
+As a rule, the information is statistics or some time values.
+
+\item \verb|-d|, \verb|-details|
+
+--- output more detailed information.
+
+\item \verb|-f|, \verb|-family| followed by a protocol family
+identifier: \verb|inet|, \verb|inet6| or \verb|link|.
+
+--- enforce the protocol family to use. If the option is not present,
+the protocol family is guessed from other arguments. If the rest of the command
+line does not give enough information to guess the family, \verb|ip| falls back to the default
+one, usually \verb|inet| or \verb|any|. \verb|link| is a special family
+identifier meaning that no networking protocol is involved.
+
+\item \verb|-4|
+
+--- shortcut for \verb|-family inet|.
+
+\item \verb|-6|
+
+--- shortcut for \verb|-family inet6|.
+
+\item \verb|-0|
+
+--- shortcut for \verb|-family link|.
+
+
+\item \verb|-o|, \verb|-oneline|
+
+--- output each record on a single line, replacing line feeds
+with the \verb|'\'| character. This is convenient when you want to
+count records with \verb|wc| or to \verb|grep| the output. The trivial
+script \verb|rtpr| converts the output back into readable form.
+
+\item \verb|-r|, \verb|-resolve|
+
+--- use the system's name resolver to print DNS names instead of
+host addresses.
+
+\begin{NB}
+ Do not use this option when reporting bugs or asking for advice.
+\end{NB}
+\begin{NB}
+ \verb|ip| never uses DNS to resolve names to addresses.
+\end{NB}
+
+\item \verb|-b|, \verb|-batch FILE|
+
+--- read commands from provided file or standart input and invoke them.
+First failure will cause termination of \verb|ip|.
+In batch \verb|FILE| everything which begins with \verb|#| symbol is
+ignored and can be used for comments.
+\paragraph{Example:}
+\begin{verbatim}
+kuznet@kaiser $ cat /tmp/ip_batch.ip
+# This is a comment
+tuntap add mode tap tap1 # This is an another comment
+link set up dev tap1
+addr add 10.0.0.1/24 dev tap1
+kuznet@kaiser $ sudo ip -b /tmp/ip_batch.ip
+\end{verbatim}
+or from standart input:
+\begin{verbatim}
+kuznet@kaiser $ cat /tmp/ip_batch.ip | sudo ip -b -
+\end{verbatim}
+
+\item \verb|-force|
+
+--- don't terminate ip on errors in batch mode.
+If there were any errors during execution of the commands,
+the application return code will be non zero.
+
+\item \verb|-l|, \verb|-loops COUNT|
+
+--- 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.
+
+\end{itemize}
+
+\verb|OBJECT| is the object to manage or to get information about.
+The object types currently understood by \verb|ip| are:
+
+\begin{itemize}
+\item \verb|link| --- network device
+\item \verb|address| --- protocol (IP or IPv6) address on a device
+\item \verb|neighbour| --- ARP or NDISC cache entry
+\item \verb|route| --- routing table entry
+\item \verb|rule| --- rule in routing policy database
+\item \verb|maddress| --- multicast address
+\item \verb|mroute| --- multicast routing cache entry
+\item \verb|tunnel| --- tunnel over IP
+\end{itemize}
+
+Again, the names of all objects may be written in full or
+abbreviated form, f.e.\ \verb|address| is abbreviated as \verb|addr|
+or just \verb|a|.
+
+\verb|COMMAND| specifies the action to perform on the object.
+The set of possible actions depends on the object type.
+As a rule, it is possible to \verb|add|, \verb|delete| and
+\verb|show| (or \verb|list|) objects, but some objects
+do not allow all of these operations or have some additional commands.
+The \verb|help| command is available for all objects. It prints
+out a list of available commands and argument syntax conventions.
+
+If no command is given, some default command is assumed.
+Usually it is \verb|list| or, if the objects of this class
+cannot be listed, \verb|help|.
+
+\verb|ARGUMENTS| is a list of arguments to the command.
+The arguments depend on the command and object. There are two types of arguments:
+{\em flags\/}, consisting of a single keyword, and {\em parameters\/},
+consisting of a keyword followed by a value. For convenience,
+each command has some {\em default parameter\/}
+which may be omitted. F.e.\ parameter \verb|dev| is the default
+for the {\tt ip link} command, so {\tt ip link ls eth0} is equivalent
+to {\tt ip link ls dev eth0}.
+In the command descriptions below such parameters
+are distinguished with the marker: ``(default)''.
+
+Almost all keywords may be abbreviated with several first (or even single)
+letters. The shortcuts are convenient when \verb|ip| is used interactively,
+but they are not recommended in scripts or when reporting bugs
+or asking for advice. ``Officially'' allowed abbreviations are listed
+in the document body.
+
+
+
+\section{{\tt ip} --- error messages}
+
+\verb|ip| may fail for one of the following reasons:
+
+\begin{itemize}
+\item
+A syntax error on the command line: an unknown keyword, incorrectly formatted
+IP address {\em et al\/}. In this case \verb|ip| prints an error message
+and exits. As a rule, the error message will contain information
+about the reason for the failure. Sometimes it also prints a help page.
+
+\item
+The arguments did not pass verification for self-consistency.
+
+\item
+\verb|ip| failed to compile a kernel request from the arguments
+because the user didn't give enough information.
+
+\item
+The kernel returned an error to some syscall. In this case \verb|ip|
+prints the error message, as it is output with \verb|perror(3)|,
+prefixed with a comment and a syscall identifier.
+
+\item
+The kernel returned an error to some RTNETLINK request.
+In this case \verb|ip| prints the error message, as it is output
+with \verb|perror(3)| prefixed with ``RTNETLINK answers:''.
+
+\end{itemize}
+
+All the operations are atomic, i.e.\ 
+if the \verb|ip| utility fails, it does not change anything
+in the system. One harmful exception is \verb|ip link| command
+(Sec.\ref{IP-LINK}, p.\pageref{IP-LINK}),
+which may change only some of the device parameters given
+on command line.
+
+It is difficult to list all the error messages (especially
+syntax errors). However, as a rule, their meaning is clear
+from the context of the command.
+
+The most common mistakes are:
+
+\begin{enumerate}
+\item Netlink is not configured in the kernel. The message is:
+\begin{verbatim}
+Cannot open netlink socket: Invalid value
+\end{verbatim}
+
+\item RTNETLINK is not configured in the kernel. In this case
+one of the following messages may be printed, depending on the command:
+\begin{verbatim}
+Cannot talk to rtnetlink: Connection refused
+Cannot send dump request: Connection refused
+\end{verbatim}
+
+\item The \verb|CONFIG_IP_MULTIPLE_TABLES| option was not selected
+when configuring the kernel. In this case any attempt to use the
+\verb|ip| \verb|rule| command will fail, f.e.
+\begin{verbatim}
+kuznet@kaiser $ ip rule list
+RTNETLINK error: Invalid argument
+dump terminated
+\end{verbatim}
+
+\end{enumerate}
+
+
+\section{{\tt ip link} --- network device configuration}
+\label{IP-LINK}
+
+\paragraph{Object:} A \verb|link| is a network device and the corresponding
+commands display and change the state of devices.
+
+\paragraph{Commands:} \verb|set| and \verb|show| (or \verb|list|).
+
+\subsection{{\tt ip link set} --- change device attributes}
+
+\paragraph{Abbreviations:} \verb|set|, \verb|s|.
+
+\paragraph{Arguments:}
+
+\begin{itemize}
+\item \verb|dev NAME| (default)
+
+--- \verb|NAME| specifies the network device on which to operate.
+
+\item \verb|up| and \verb|down|
+
+--- change the state of the device to \verb|UP| or \verb|DOWN|.
+
+\item \verb|arp on| or \verb|arp off|
+
+--- change the \verb|NOARP| flag on the device.
+
+\begin{NB}
+This operation is {\em not allowed\/} if the device is in state \verb|UP|.
+Though neither the \verb|ip| utility nor the kernel check for this condition.
+You can get unpredictable results changing this flag while the
+device is running.
+\end{NB}
+
+\item \verb|multicast on| or \verb|multicast off|
+
+--- change the \verb|MULTICAST| flag on the device.
+
+\item \verb|dynamic on| or \verb|dynamic off|
+
+--- change the \verb|DYNAMIC| flag on the device.
+
+\item \verb|name NAME|
+
+--- change the name of the device. This operation is not
+recommended if the device is running or has some addresses
+already configured.
+
+\item \verb|txqueuelen NUMBER| or \verb|txqlen NUMBER|
+
+--- change the transmit queue length of the device.
+
+\item \verb|mtu NUMBER|
+
+--- change the MTU of the device.
+
+\item \verb|address LLADDRESS|
+
+--- change the station address of the interface.
+
+\item \verb|broadcast LLADDRESS|, \verb|brd LLADDRESS| or \verb|peer LLADDRESS|
+
+--- change the link layer broadcast address or the peer address when
+the interface is \verb|POINTOPOINT|.
+
+\vskip 1mm
+\begin{NB}
+For most devices (f.e.\ for Ethernet) changing the link layer
+broadcast address will break networking.
+Do not use it, if you do not understand what this operation really does.
+\end{NB}
+
+\item \verb|netns PID|
+
+--- move the device to the network namespace associated with the process PID.
+
+\end{itemize}
+
+\vskip 1mm
+\begin{NB}
+The \verb|PROMISC| and \verb|ALLMULTI| flags are considered
+obsolete and should not be changed administratively, though
+the {\tt ip} utility will allow that.
+\end{NB}
+
+\paragraph{Warning:} If multiple parameter changes are requested,
+\verb|ip| aborts immediately after any of the changes have failed.
+This is the only case when \verb|ip| can move the system to
+an unpredictable state. The solution is to avoid changing
+several parameters with one {\tt ip link set} call.
+
+\paragraph{Examples:}
+\begin{itemize}
+\item \verb|ip link set dummy address 00:00:00:00:00:01|
+
+--- change the station address of the interface \verb|dummy|.
+
+\item \verb|ip link set dummy up|
+
+--- start the interface \verb|dummy|.
+
+\end{itemize}
+
+
+\subsection{{\tt ip link show} --- display device attributes}
+\label{IP-LINK-SHOW}
+
+\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|lst|, \verb|sh|, \verb|ls|,
+\verb|l|.
+
+\paragraph{Arguments:}
+\begin{itemize}
+\item \verb|dev NAME| (default)
+
+--- \verb|NAME| specifies the network device to show.
+If this argument is omitted all devices are listed.
+
+\item \verb|up|
+
+--- only display running interfaces.
+
+\end{itemize}
+
+
+\paragraph{Output format:}
+
+\begin{verbatim}
+kuznet@alisa:~ $ ip link ls eth0
+3: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc cbq qlen 100
+    link/ether 00:a0:cc:66:18:78 brd ff:ff:ff:ff:ff:ff
+kuznet@alisa:~ $ ip link ls sit0
+5: sit0@NONE: <NOARP,UP> mtu 1480 qdisc noqueue
+    link/sit 0.0.0.0 brd 0.0.0.0
+kuznet@alisa:~ $ ip link ls dummy
+2: dummy: <BROADCAST,NOARP> mtu 1500 qdisc noop
+    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
+kuznet@alisa:~ $ 
+\end{verbatim}
+
+
+The number before each colon is an {\em interface index\/} or {\em ifindex\/}.
+This number uniquely identifies the interface. This is followed by the {\em interface name\/}
+(\verb|eth0|, \verb|sit0| etc.). The interface name is also
+unique at every given moment. However, the interface may disappear from the
+list (f.e.\ when the corresponding driver module is unloaded) and another
+one with the same name may be created later. Besides that,
+the administrator may change the name of any device with
+\verb|ip| \verb|link| \verb|set| \verb|name|
+to make it more intelligible.
+
+The interface name may have another name or \verb|NONE| appended 
+after the \verb|@| sign. This means that this device is bound to some other
+device,
+i.e.\ packets send through it are encapsulated and sent via the ``master''
+device. If the name is \verb|NONE|, the master is unknown.
+
+Then we see the interface {\em mtu\/} (``maximal transfer unit''). This determines
+the maximal size of data which can be sent as a single packet over this interface.
+
+{\em qdisc\/} (``queuing discipline'') shows the queuing algorithm used
+on the interface. Particularly, \verb|noqueue| means that this interface
+does not queue anything and \verb|noop| means that the interface is in blackhole
+mode i.e.\ all packets sent to it are immediately discarded.
+{\em qlen\/} is the default transmit queue length of the device measured
+in packets.
+
+The interface flags are summarized in the angle brackets.
+
+\begin{itemize}
+\item \verb|UP| --- the device is turned on. It is ready to accept
+packets for transmission and it may inject into the kernel packets received
+from other nodes on the network.
+
+\item \verb|LOOPBACK| --- the interface does not communicate with other
+hosts. All packets sent through it will be returned
+and nothing but bounced packets can be received.
+
+\item \verb|BROADCAST| --- the device has the facility to send packets
+to all hosts sharing the same link. A typical example is an Ethernet link.
+
+\item \verb|POINTOPOINT| --- the link has only two ends with one node
+attached to each end. All packets sent to this link will reach the peer
+and all packets received by us came from this single peer.
+
+If neither \verb|LOOPBACK| nor \verb|BROADCAST| nor \verb|POINTOPOINT|
+are set, the interface is assumed to be NMBA (Non-Broadcast Multi-Access).
+This is the most generic type of device and the most complicated one, because
+the host attached to a NBMA link has no means to send to anyone
+without additionally configured information.
+
+\item \verb|MULTICAST| --- is an advisory flag indicating that the interface
+is aware of multicasting i.e.\ sending packets to some subset of neighbouring
+nodes. Broadcasting is a particular case of multicasting, where the multicast
+group consists of all nodes on the link. It is important to emphasize
+that software {\em must not\/} interpret the absence of this flag as the inability
+to use multicasting on this interface. Any \verb|POINTOPOINT| and
+\verb|BROADCAST| link is multicasting by definition, because we have
+direct access to all the neighbours and, hence, to any part of them.
+Certainly, the use of high bandwidth multicast transfers is not recommended
+on broadcast-only links because of high expense, but it is not strictly
+prohibited.
+
+\item \verb|PROMISC| --- the device listens to and feeds to the kernel all
+traffic on the link even if it is not destined for us, not broadcasted
+and not destined for a multicast group of which we are member. Usually
+this mode exists only on broadcast links and is used by bridges and for network
+monitoring.
+
+\item \verb|ALLMULTI| --- the device receives all multicast packets
+wandering on the link. This mode is used by multicast routers.
+
+\item \verb|NOARP| --- this flag is different from the other ones. It has
+no invariant value and its interpretation depends on the network protocols
+involved. As a rule, it indicates that the device needs no address
+resolution and that the software or hardware knows how to deliver packets
+without any help from the protocol stacks.
+
+\item \verb|DYNAMIC| --- is an advisory flag indicating that the interface is
+dynamically created and destroyed.
+
+\item \verb|SLAVE| --- this interface is bonded to some other interfaces
+to share link capacities.
+
+\end{itemize}
+
+\vskip 1mm
+\begin{NB}
+There are other flags but they are either obsolete (\verb|NOTRAILERS|)
+or not implemented (\verb|DEBUG|) or specific to some devices
+(\verb|MASTER|, \verb|AUTOMEDIA| and \verb|PORTSEL|). We do not discuss
+them here.
+\end{NB}
+
+
+The second line contains information on the link layer addresses
+associated with the device. The first word (\verb|ether|, \verb|sit|)
+defines the interface hardware type. This type determines the format and semantics
+of the addresses and is logically part of the address.
+The default format of the station address and the broadcast address
+(or the peer address for pointopoint links) is a
+sequence of hexadecimal bytes separated by colons, but some link
+types may have their natural address format, f.e.\ addresses
+of tunnels over IP are printed as dotted-quad IP addresses.
+
+\vskip 1mm
+\begin{NB}
+  NBMA links have no well-defined broadcast or peer address,
+  however this field may contain useful information, f.e.\
+  about the address of broadcast relay or about the address of the ARP server.
+\end{NB}
+\begin{NB}
+Multicast addresses are not shown by this command, see
+\verb|ip maddr ls| in~Sec.\ref{IP-MADDR} (p.\pageref{IP-MADDR} of this
+document).
+\end{NB}
+
+
+\paragraph{Statistics:} With the \verb|-statistics| option, \verb|ip| also
+prints interface statistics:
+
+\begin{verbatim}
+kuznet@alisa:~ $ ip -s link ls eth0
+3: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc cbq qlen 100
+    link/ether 00:a0:cc:66:18:78 brd ff:ff:ff:ff:ff:ff
+    RX: bytes  packets  errors  dropped overrun mcast   
+    2449949362 2786187  0       0       0       0      
+    TX: bytes  packets  errors  dropped carrier collsns 
+    178558497  1783945  332     0       332     35172  
+kuznet@alisa:~ $
+\end{verbatim}
+\verb|RX:| and \verb|TX:| lines summarize receiver and transmitter
+statistics. They contain:
+\begin{itemize}
+\item \verb|bytes| --- the total number of bytes received or transmitted
+on the interface. This number wraps when the maximal length of the data type
+natural for the architecture is exceeded, so continuous monitoring requires
+a user level daemon snapping it periodically.
+\item \verb|packets| --- the total number of packets received or transmitted
+on the interface.
+\item \verb|errors| --- the total number of receiver or transmitter errors.
+\item \verb|dropped| --- the total number of packets dropped due to lack
+of resources.
+\item \verb|overrun| --- the total number of receiver overruns resulting
+in dropped packets. As a rule, if the interface is overrun, it means
+serious problems in the kernel or that your machine is too slow
+for this interface.
+\item \verb|mcast| --- the total number of received multicast packets. This option
+is only supported by a few devices.
+\item \verb|carrier| --- total number of link media failures f.e.\ because
+of lost carrier.
+\item \verb|collsns| --- the total number of collision events
+on Ethernet-like media. This number may have a different sense on other
+link types.
+\item \verb|compressed| --- the total number of compressed packets. This is
+available only for links using VJ header compression.
+\end{itemize}
+
+
+If the \verb|-s| option is entered twice or more,
+\verb|ip| prints more detailed statistics on receiver
+and transmitter errors.
+
+\begin{verbatim}
+kuznet@alisa:~ $ ip -s -s link ls eth0
+3: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc cbq qlen 100
+    link/ether 00:a0:cc:66:18:78 brd ff:ff:ff:ff:ff:ff
+    RX: bytes  packets  errors  dropped overrun mcast   
+    2449949362 2786187  0       0       0       0      
+    RX errors: length   crc     frame   fifo    missed
+               0        0       0       0       0      
+    TX: bytes  packets  errors  dropped carrier collsns 
+    178558497  1783945  332     0       332     35172  
+    TX errors: aborted  fifo    window  heartbeat
+               0        0       0       332    
+kuznet@alisa:~ $
+\end{verbatim}
+These error names are pure Ethernetisms. Other devices
+may have non zero values in these fields but they may be
+interpreted differently.
+
+
+\section{{\tt ip address} --- protocol address management}
+
+\paragraph{Abbreviations:} \verb|address|, \verb|addr|, \verb|a|.
+
+\paragraph{Object:} The \verb|address| is a protocol (IP or IPv6) address attached
+to a network device. Each device must have at least one address
+to use the corresponding protocol. It is possible to have several
+different addresses attached to one device. These addresses are not
+discriminated, so that the term {\em alias\/} is not quite appropriate
+for them and we do not use it in this document.
+
+The \verb|ip addr| command displays addresses and their properties,
+adds new addresses and deletes old ones.
+
+\paragraph{Commands:} \verb|add|, \verb|delete|, \verb|flush| and \verb|show|
+(or \verb|list|).
+
+
+\subsection{{\tt ip address add} --- add a new protocol address}
+\label{IP-ADDR-ADD}
+
+\paragraph{Abbreviations:} \verb|add|, \verb|a|.
+
+\paragraph{Arguments:}
+
+\begin{itemize}
+\item \verb|dev NAME|
+
+\noindent--- the name of the device to add the address to.
+
+\item \verb|local ADDRESS| (default)
+
+--- the address of the interface. The format of the address depends
+on the protocol. It is a dotted quad for IP and a sequence of hexadecimal halfwords
+separated by colons for IPv6. The \verb|ADDRESS| may be followed by
+a slash and a decimal number which encodes the network prefix length.
+
+
+\item \verb|peer ADDRESS|
+
+--- the address of the remote endpoint for pointopoint interfaces.
+Again, the \verb|ADDRESS| may be followed by a slash and a decimal number,
+encoding the network prefix length. If a peer address is specified,
+the local address {\em cannot\/} have a prefix length. The network prefix is associated
+with the peer rather than with the local address.
+
+
+\item \verb|broadcast ADDRESS|
+
+--- the broadcast address on the interface.
+
+It is possible to use the special symbols \verb|'+'| and \verb|'-'|
+instead of the broadcast address. In this case, the broadcast address
+is derived by setting/resetting the host bits of the interface prefix.
+
+\vskip 1mm
+\begin{NB}
+Unlike \verb|ifconfig|, the \verb|ip| utility {\em does not\/} set any broadcast
+address unless explicitly requested.
+\end{NB}
+
+
+\item \verb|label NAME|
+
+--- Each address may be tagged with a label string.
+In order to preserve compatibility with Linux-2.0 net aliases,
+this string must coincide with the name of the device or must be prefixed
+with the device name followed by colon.
+
+
+\item \verb|scope SCOPE_VALUE|
+
+--- the scope of the area where this address is valid.
+The available scopes are listed in file \verb|/etc/iproute2/rt_scopes|.
+Predefined scope values are:
+
+ \begin{itemize}
+	\item \verb|global| --- the address is globally valid.
+	\item \verb|site| --- (IPv6 only) the address is site local,
+	i.e.\ it is valid inside this site.
+	\item \verb|link| --- the address is link local, i.e.\ 
+	it is valid only on this device.
+	\item \verb|host| --- the address is valid only inside this host.
+ \end{itemize}
+
+Appendix~\ref{ADDR-SEL} (p.\pageref{ADDR-SEL} of this document)
+contains more details on address scopes.
+
+\end{itemize}
+
+\paragraph{Examples:}
+\begin{itemize}
+\item \verb|ip addr add 127.0.0.1/8 dev lo brd + scope host|
+
+--- add the usual loopback address to the loopback device.
+
+\item \verb|ip addr add 10.0.0.1/24 brd + dev eth0 label eth0:Alias|
+
+--- add the address 10.0.0.1 with prefix length 24 (i.e.\ netmask
+\verb|255.255.255.0|), standard broadcast and label \verb|eth0:Alias|
+to the interface \verb|eth0|.
+\end{itemize}
+
+
+\subsection{{\tt ip address delete} --- delete a protocol address}
+
+\paragraph{Abbreviations:} \verb|delete|, \verb|del|, \verb|d|.
+
+\paragraph{Arguments:} coincide with the arguments of \verb|ip addr add|.
+The device name is a required argument. The rest are optional.
+If no arguments are given, the first address is deleted.
+
+\paragraph{Examples:}
+\begin{itemize}
+\item \verb|ip addr del 127.0.0.1/8 dev lo|
+
+--- deletes the loopback address from the loopback device.
+It would be best not to repeat this experiment.
+
+\item Disable IP on the interface \verb|eth0|:
+\begin{verbatim}
+  while ip -f inet addr del dev eth0; do
+    : nothing
+  done
+\end{verbatim}
+Another method to disable IP on an interface using {\tt ip addr flush}
+may be found in sec.\ref{IP-ADDR-FLUSH}, p.\pageref{IP-ADDR-FLUSH}.
+
+\end{itemize}
+
+
+\subsection{{\tt ip address show} --- display protocol addresses}
+
+\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|lst|, \verb|sh|, \verb|ls|,
+\verb|l|.
+
+\paragraph{Arguments:}
+
+\begin{itemize}
+\item \verb|dev NAME| (default)
+
+--- the name of the device.
+
+\item \verb|scope SCOPE_VAL|
+
+--- only list addresses with this scope.
+
+\item \verb|to PREFIX|
+
+--- only list addresses matching this prefix.
+
+\item \verb|label PATTERN|
+
+--- only list addresses with labels matching the \verb|PATTERN|.
+\verb|PATTERN| is a usual shell style pattern.
+
+
+\item \verb|dynamic| and \verb|permanent|
+
+--- (IPv6 only) only list addresses installed due to stateless
+address configuration or only list permanent (not dynamic) addresses.
+
+\item \verb|tentative|
+
+--- (IPv6 only) only list addresses which did not pass duplicate
+address detection.
+
+\item \verb|deprecated|
+
+--- (IPv6 only) only list deprecated addresses.
+
+
+\item  \verb|primary| and \verb|secondary|
+
+--- only list primary (or secondary) addresses.
+
+\end{itemize}
+
+
+\paragraph{Output format:}
+
+\begin{verbatim}
+kuznet@alisa:~ $ ip addr ls eth0
+3: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc cbq qlen 100
+    link/ether 00:a0:cc:66:18:78 brd ff:ff:ff:ff:ff:ff
+    inet 193.233.7.90/24 brd 193.233.7.255 scope global eth0
+    inet6 3ffe:2400:0:1:2a0:ccff:fe66:1878/64 scope global dynamic 
+       valid_lft forever preferred_lft 604746sec
+    inet6 fe80::2a0:ccff:fe66:1878/10 scope link 
+kuznet@alisa:~ $ 
+\end{verbatim}
+
+The first two lines coincide with the output of \verb|ip link ls|.
+It is natural to interpret link layer addresses
+as addresses of the protocol family \verb|AF_PACKET|.
+
+Then the list of IP and IPv6 addresses follows, accompanied by
+additional address attributes: scope value (see Sec.\ref{IP-ADDR-ADD},
+p.\pageref{IP-ADDR-ADD} above), flags and the address label.
+
+Address flags are set by the kernel and cannot be changed
+administratively. Currently, the following flags are defined:
+
+\begin{enumerate}
+\item \verb|secondary|
+
+--- the address is not used when selecting the default source address
+of outgoing packets (Cf.\ Appendix~\ref{ADDR-SEL}, p.\pageref{ADDR-SEL}.).
+An IP address becomes secondary if another address with the same
+prefix bits already exists. The first address is primary.
+It is the leader of the group of all secondary addresses. When the leader
+is deleted, all secondaries are purged too.
+There is a tweak in \verb|/proc/sys/net/ipv4/conf/<dev>/promote_secondaries|
+which activate secondaries promotion when a primary is deleted.
+To permanently enable this feature on all devices add
+\verb|net.ipv4.conf.all.promote_secondaries=1| to \verb|/etc/sysctl.conf|.
+This tweak is available in linux 2.6.15 and later.
+
+
+\item \verb|dynamic|
+
+--- the address was created due to stateless autoconfiguration~\cite{RFC-ADDRCONF}.
+In this case the output also contains information on times, when
+the address is still valid. After \verb|preferred_lft| expires the address is
+moved to the deprecated state. After \verb|valid_lft| expires the address
+is finally invalidated.
+
+\item \verb|deprecated|
+
+--- the address is deprecated, i.e.\ it is still valid, but cannot
+be used by newly created connections.
+
+\item \verb|tentative|
+
+--- the address is not used because duplicate address detection~\cite{RFC-ADDRCONF}
+is still not complete or failed.
+
+\end{enumerate}
+
+
+\subsection{{\tt ip address flush} --- flush protocol addresses}
+\label{IP-ADDR-FLUSH}
+
+\paragraph{Abbreviations:} \verb|flush|, \verb|f|.
+
+\paragraph{Description:}This command flushes the protocol addresses
+selected by some criteria.
+
+\paragraph{Arguments:} This command has the same arguments as \verb|show|.
+The difference is that it does not run when no arguments are given.
+
+\paragraph{Warning:} This command (and other \verb|flush| commands
+described below) is pretty dangerous. If you make a mistake, it will
+not forgive it, but will cruelly purge all the addresses.
+
+\paragraph{Statistics:} With the \verb|-statistics| option, the command
+becomes verbose. It prints out the number of deleted addresses and the number
+of rounds made to flush the address list. If this option is given
+twice, \verb|ip addr flush| also dumps all the deleted addresses
+in the format described in the previous subsection.
+
+\paragraph{Example:} Delete all the addresses from the private network
+10.0.0.0/8:
+\begin{verbatim}
+netadm@amber:~ # ip -s -s a f to 10/8
+2: dummy    inet 10.7.7.7/16 brd 10.7.255.255 scope global dummy
+3: eth0    inet 10.10.7.7/16 brd 10.10.255.255 scope global eth0
+4: eth1    inet 10.8.7.7/16 brd 10.8.255.255 scope global eth1
+
+*** Round 1, deleting 3 addresses ***
+*** Flush is complete after 1 round ***
+netadm@amber:~ # 
+\end{verbatim}
+Another instructive example is disabling IP on all the Ethernets:
+\begin{verbatim}
+netadm@amber:~ # ip -4 addr flush label "eth*"
+\end{verbatim}
+And the last example shows how to flush all the IPv6 addresses
+acquired by the host from stateless address autoconfiguration
+after you enabled forwarding or disabled autoconfiguration.
+\begin{verbatim}
+netadm@amber:~ # ip -6 addr flush dynamic
+\end{verbatim}
+
+
+
+\section{{\tt ip neighbour} --- neighbour/arp tables management}
+
+\paragraph{Abbreviations:} \verb|neighbour|, \verb|neighbor|, \verb|neigh|,
+\verb|n|.
+
+\paragraph{Object:} \verb|neighbour| objects establish bindings between protocol
+addresses and link layer addresses for hosts sharing the same link.
+Neighbour entries are organized into tables. The IPv4 neighbour table
+is known by another name --- the ARP table.
+
+The corresponding commands display neighbour bindings
+and their properties, add new neighbour entries and delete old ones.
+
+\paragraph{Commands:} \verb|add|, \verb|change|, \verb|replace|,
+\verb|delete|, \verb|flush| and \verb|show| (or \verb|list|).
+
+\paragraph{See also:} Appendix~\ref{PROXY-NEIGH}, p.\pageref{PROXY-NEIGH}
+describes how to manage proxy ARP/NDISC with the \verb|ip| utility.
+
+
+\subsection{{\tt ip neighbour add} --- add a new neighbour entry\\
+	{\tt ip neighbour change} --- change an existing entry\\
+	{\tt ip neighbour replace} --- add a new entry or change an existing one}
+
+\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|change|, \verb|chg|;
+\verb|replace|,	\verb|repl|.
+
+\paragraph{Description:} These commands create new neighbour records
+or update existing ones.
+
+\paragraph{Arguments:}
+
+\begin{itemize}
+\item \verb|to ADDRESS| (default)
+
+--- the protocol address of the neighbour. It is either an IPv4 or IPv6 address.
+
+\item \verb|dev NAME|
+
+--- the interface to which this neighbour is attached.
+
+
+\item \verb|lladdr LLADDRESS|
+
+--- the link layer address of the neighbour. \verb|LLADDRESS| can also be
+\verb|null|. 
+
+\item \verb|nud NUD_STATE|
+
+--- the state of the neighbour entry. \verb|nud| is an abbreviation for ``Neighbour
+Unreachability Detection''. The state can take one of the following values:
+
+\begin{enumerate}
+\item \verb|permanent| --- the neighbour entry is valid forever and can be only be removed
+administratively.
+\item \verb|noarp| --- the neighbour entry is valid. No attempts to validate
+this entry will be made but it can be removed when its lifetime expires.
+\item \verb|reachable| --- the neighbour entry is valid until the reachability
+timeout expires.
+\item \verb|stale| --- the neighbour entry is valid but suspicious.
+This option to \verb|ip neigh| does not change the neighbour state if
+it was valid and the address is not changed by this command.
+\end{enumerate}
+
+\end{itemize}
+
+\paragraph{Examples:}
+\begin{itemize}
+\item \verb|ip neigh add 10.0.0.3 lladdr 0:0:0:0:0:1 dev eth0 nud perm|
+
+--- add a permanent ARP entry for the neighbour 10.0.0.3 on the device \verb|eth0|.
+
+\item \verb|ip neigh chg 10.0.0.3 dev eth0 nud reachable|
+
+--- change its state to \verb|reachable|.
+\end{itemize}
+
+
+\subsection{{\tt ip neighbour delete} --- delete a neighbour entry}
+
+\paragraph{Abbreviations:} \verb|delete|, \verb|del|, \verb|d|.
+
+\paragraph{Description:} This command invalidates a neighbour entry.
+
+\paragraph{Arguments:} The arguments are the same as with \verb|ip neigh add|,
+except that \verb|lladdr| and \verb|nud| are ignored.
+
+
+\paragraph{Example:}
+\begin{itemize}
+\item \verb|ip neigh del 10.0.0.3 dev eth0|
+
+--- invalidate an ARP entry for the neighbour 10.0.0.3 on the device \verb|eth0|.
+
+\end{itemize}
+
+\begin{NB}
+ The deleted neighbour entry will not disappear from the tables
+ immediately. If it is in use it cannot be deleted until the last
+ client releases it. Otherwise it will be destroyed during
+ the next garbage collection.
+\end{NB}
+
+
+\paragraph{Warning:} Attempts to delete or manually change
+a \verb|noarp| entry created by the kernel may result in unpredictable behaviour.
+Particularly, the kernel may try to resolve this address even
+on a \verb|NOARP| interface or if the address is multicast or broadcast.
+
+
+\subsection{{\tt ip neighbour show} --- list neighbour entries}
+
+\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|.
+
+\paragraph{Description:}This commands displays neighbour tables.
+
+\paragraph{Arguments:}
+
+\begin{itemize}
+
+\item \verb|to ADDRESS| (default)
+
+--- the prefix selecting the neighbours to list.
+
+\item \verb|dev NAME|
+
+--- only list the neighbours attached to this device.
+
+\item \verb|unused|
+
+--- only list neighbours which are not currently in use.
+
+\item \verb|nud NUD_STATE|
+
+--- only list neighbour entries in this state. \verb|NUD_STATE| takes
+values listed below or the special value \verb|all| which means all states.
+This option may occur more than once. If this option is absent, \verb|ip|
+lists all entries except for \verb|none| and \verb|noarp|.
+
+\end{itemize}
+
+
+\paragraph{Output format:}
+
+\begin{verbatim}
+kuznet@alisa:~ $ ip neigh ls
+:: dev lo lladdr 00:00:00:00:00:00 nud noarp
+fe80::200:cff:fe76:3f85 dev eth0 lladdr 00:00:0c:76:3f:85 router \
+    nud stale
+0.0.0.0 dev lo lladdr 00:00:00:00:00:00 nud noarp
+193.233.7.254 dev eth0 lladdr 00:00:0c:76:3f:85 nud reachable
+193.233.7.85 dev eth0 lladdr 00:e0:1e:63:39:00 nud stale
+kuznet@alisa:~ $ 
+\end{verbatim}
+
+The first word of each line is the protocol address of the neighbour.
+Then the device name follows. The rest of the line describes the contents of
+the neighbour entry identified by the pair (device, address).
+
+\verb|lladdr| is the link layer address of the neighbour.
+
+\verb|nud| is the state of the ``neighbour unreachability detection'' machine
+for this entry. The detailed description of the neighbour
+state machine can be found in~\cite{RFC-NDISC}. Here is the full list
+of the states with short descriptions:
+
+\begin{enumerate}
+\item\verb|none| --- the state of the neighbour is void.
+\item\verb|incomplete| --- the neighbour is in the process of resolution.
+\item\verb|reachable| --- the neighbour is valid and apparently reachable.
+\item\verb|stale| --- the neighbour is valid, but is probably already
+unreachable, so the kernel will try to check it at the first transmission.
+\item\verb|delay| --- a packet has been sent to the stale neighbour and the kernel is waiting
+for confirmation.
+\item\verb|probe| --- the delay timer expired but no confirmation was received.
+The kernel has started to probe the neighbour with ARP/NDISC messages.
+\item\verb|failed| --- resolution has failed.
+\item\verb|noarp| --- the neighbour is valid. No attempts to check the entry
+will be made.
+\item\verb|permanent| --- it is a \verb|noarp| entry, but only the administrator
+may remove the entry from the neighbour table.
+\end{enumerate}
+
+The link layer address is valid in all states except for \verb|none|,
+\verb|failed| and \verb|incomplete|.
+
+IPv6 neighbours can be marked with the additional flag \verb|router|
+which means that the neighbour introduced itself as an IPv6 router~\cite{RFC-NDISC}.
+
+\paragraph{Statistics:} The \verb|-statistics| option displays some usage
+statistics, f.e.\
+
+\begin{verbatim}
+kuznet@alisa:~ $ ip -s n ls 193.233.7.254
+193.233.7.254 dev eth0 lladdr 00:00:0c:76:3f:85 ref 5 used 12/13/20 \
+    nud reachable
+kuznet@alisa:~ $ 
+\end{verbatim}
+
+Here \verb|ref| is the number of users of this entry
+and \verb|used| is a triplet of time intervals in seconds
+separated by slashes. In this case they show that:
+
+\begin{enumerate}
+\item the entry was used 12 seconds ago.
+\item the entry was confirmed 13 seconds ago.
+\item the entry was updated 20 seconds ago.
+\end{enumerate}
+
+\subsection{{\tt ip neighbour flush} --- flush neighbour entries}
+
+\paragraph{Abbreviations:} \verb|flush|, \verb|f|.
+
+\paragraph{Description:}This command flushes neighbour tables, selecting
+entries to flush by some criteria.
+
+\paragraph{Arguments:} This command has the same arguments as \verb|show|.
+The differences are that it does not run when no arguments are given,
+and that the default neighbour states to be flushed do not include
+\verb|permanent| and \verb|noarp|.
+
+
+\paragraph{Statistics:} With the \verb|-statistics| option, the command
+becomes verbose. It prints out the number of deleted neighbours and the number
+of rounds made to flush the neighbour table. If the option is given
+twice, \verb|ip neigh flush| also dumps all the deleted neighbours
+in the format described in the previous subsection.
+
+\paragraph{Example:}
+\begin{verbatim}
+netadm@alisa:~ # ip -s -s n f 193.233.7.254
+193.233.7.254 dev eth0 lladdr 00:00:0c:76:3f:85 ref 5 used 12/13/20 \
+    nud reachable
+
+*** Round 1, deleting 1 entries ***
+*** Flush is complete after 1 round ***
+netadm@alisa:~ # 
+\end{verbatim}
+
+
+\section{{\tt ip route} --- routing table management}
+\label{IP-ROUTE}
+
+\paragraph{Abbreviations:} \verb|route|, \verb|ro|, \verb|r|.
+
+\paragraph{Object:} \verb|route| entries in the kernel routing tables keep
+information about paths to other networked nodes.
+
+Each route entry has a {\em key\/} consisting of a {\em prefix\/}
+(i.e.\ a pair containing a network address and the length of its mask) and,
+optionally, the TOS value. An IP packet matches the route if the highest
+bits of its destination address are equal to the route prefix at least
+up to the prefix length and if the TOS of the route is zero or equal to
+the TOS of the packet.
+ 
+If several routes match the packet, the following pruning rules
+are used to select the best one (see~\cite{RFC1812}):
+\begin{enumerate}
+\item The longest matching prefix is selected. All shorter ones
+are dropped.
+
+\item If the TOS of some route with the longest prefix is equal to the TOS
+of the packet, the routes with different TOS are dropped.
+
+If no exact TOS match was found and routes with TOS=0 exist,
+the rest of routes are pruned.
+
+Otherwise, the route lookup fails.
+
+\item If several routes remain after the previous steps, then
+the routes with the best preference values are selected.
+
+\item If we still have several routes, then the {\em first\/} of them
+is selected.
+
+\begin{NB}
+ Note the ambiguity of the last step. Unfortunately, Linux
+ historically allows such a bizarre situation. The sense of the
+word ``first'' depends on the order of route additions and it is practically
+impossible to maintain a bundle of such routes in this order.
+\end{NB}
+
+For simplicity we will limit ourselves to the case where such a situation
+is impossible and routes are uniquely identified by the triplet
+\{prefix, tos, preference\}. Actually, it is impossible to create
+non-unique routes with \verb|ip| commands described in this section.
+
+One useful exception to this rule is the default route on non-forwarding
+hosts. It is ``officially'' allowed to have several fallback routes
+when several routers are present on directly connected networks.
+In this case, Linux-2.2 makes ``dead gateway detection''~\cite{RFC1122}
+controlled by neighbour unreachability detection and by advice
+from transport protocols to select a working router, so the order
+of the routes is not essential. However, in this case,
+fiddling with default routes manually is not recommended. Use the Router Discovery
+protocol (see Appendix~\ref{EXAMPLE-SETUP}, p.\pageref{EXAMPLE-SETUP})
+instead. Actually, Linux-2.2 IPv6 does not give user level applications
+any access to default routes.
+\end{enumerate}
+
+Certainly, the steps above are not performed exactly
+in this sequence. Instead, the routing table in the kernel is kept
+in some data structure to achieve the final result
+with minimal cost. However, not depending on a particular
+routing algorithm implemented in the kernel, we can summarize
+the statements above as: a route is identified by the triplet
+\{prefix, tos, preference\}. This {\em key\/} lets us locate
+the route in the routing table.
+
+\paragraph{Route attributes:} Each route key refers to a routing
+information record containing
+the data required to deliver IP packets (f.e.\ output device and
+next hop router) and some optional attributes (f.e. the path MTU or
+the preferred source address when communicating with this destination).
+These attributes are described in the following subsection.
+
+\paragraph{Route types:} \label{IP-ROUTE-TYPES}
+It is important that the set
+of required and optional attributes depend on the route {\em type\/}.
+The most important route type
+is \verb|unicast|. It describes real paths to other hosts.
+As a rule, common routing tables contain only such routes. However,
+there are other types of routes with different semantics. The
+full list of types understood by Linux-2.2 is:
+\begin{itemize}
+\item \verb|unicast| --- the route entry describes real paths to the
+destinations covered by the route prefix.
+\item \verb|unreachable| --- these destinations are unreachable. Packets
+are discarded and the ICMP message {\em host unreachable\/} is generated.
+The local senders get an \verb|EHOSTUNREACH| error.
+\item \verb|blackhole| --- these destinations are unreachable. Packets
+are discarded silently. The local senders get an \verb|EINVAL| error.
+\item \verb|prohibit| --- these destinations are unreachable. Packets
+are discarded and the ICMP message {\em communication administratively
+prohibited\/} is generated. The local senders get an \verb|EACCES| error.
+\item \verb|local| --- the destinations are assigned to this
+host. The packets are looped back and delivered locally.
+\item \verb|broadcast| --- the destinations are broadcast addresses.
+The packets are sent as link broadcasts.
+\item \verb|throw| --- a special control route used together with policy
+rules (see sec.\ref{IP-RULE}, p.\pageref{IP-RULE}). If such a route is selected, lookup
+in this table is terminated pretending that no route was found.
+Without policy routing it is equivalent to the absence of the route in the routing
+table. The packets are dropped and the ICMP message {\em net unreachable\/}
+is generated. The local senders get an \verb|ENETUNREACH| error.
+\item \verb|nat| --- a special NAT route. Destinations covered by the prefix
+are considered to be dummy (or external) addresses which require translation
+to real (or internal) ones before forwarding. The addresses to translate to
+are selected with the attribute \verb|via|. More about NAT is
+in Appendix~\ref{ROUTE-NAT}, p.\pageref{ROUTE-NAT}.
+\item \verb|anycast| --- ({\em not implemented\/}) the destinations are
+{\em anycast\/} addresses assigned to this host. They are mainly equivalent
+to \verb|local| with one difference: such addresses are invalid when used
+as the source address of any packet.
+\item \verb|multicast| --- a special type used for multicast routing.
+It is not present in normal routing tables.
+\end{itemize}
+
+\paragraph{Route tables:} Linux-2.2 can pack routes into several routing
+tables identified by a number in the range from 1 to 255 or by
+name from the file \verb|/etc/iproute2/rt_tables|. By default all normal
+routes are inserted into the \verb|main| table (ID 254) and the kernel only uses
+this table when calculating routes.
+
+Actually, one other table always exists, which is invisible but
+even more important. It is the \verb|local| table (ID 255). This table
+consists of routes for local and broadcast addresses. The kernel maintains
+this table automatically and the administrator usually need not modify it
+or even look at it.
+
+The multiple routing tables enter the game when {\em policy routing\/}
+is used. See sec.\ref{IP-RULE}, p.\pageref{IP-RULE}.
+In this case, the table identifier effectively becomes
+one more parameter, which should be added to the triplet
+\{prefix, tos, preference\} to uniquely identify the route.
+
+
+\subsection{{\tt ip route add} --- add a new route\\
+	{\tt ip route change} --- change a route\\
+	{\tt ip route replace} --- change a route or add a new one}
+\label{IP-ROUTE-ADD}
+
+\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|change|, \verb|chg|;
+	\verb|replace|, \verb|repl|.
+
+
+\paragraph{Arguments:}
+\begin{itemize}
+\item \verb|to PREFIX| or \verb|to TYPE PREFIX| (default)
+
+--- the destination prefix of the route. If \verb|TYPE| is omitted,
+\verb|ip| assumes type \verb|unicast|. Other values of \verb|TYPE|
+are listed above. \verb|PREFIX| is an IP or IPv6 address optionally followed
+by a slash and the prefix length. If the length of the prefix is missing,
+\verb|ip| assumes a full-length host route. There is also a special
+\verb|PREFIX| --- \verb|default| --- which is equivalent to IP \verb|0/0| or
+to IPv6 \verb|::/0|.
+
+\item \verb|tos TOS| or \verb|dsfield TOS|
+
+--- the Type Of Service (TOS) key. This key has no associated mask and
+the longest match is understood as: First, compare the TOS
+of the route and of the packet. If they are not equal, then the packet
+may still match a route with a zero TOS. \verb|TOS| is either an 8 bit hexadecimal
+number or an identifier from {\tt /etc/iproute2/rt\_dsfield}.
+
+
+\item \verb|metric NUMBER| or \verb|preference NUMBER|
+
+--- the preference value of the route. \verb|NUMBER| is an arbitrary 32bit number.
+
+\item \verb|table TABLEID|
+
+--- the table to add this route to.
+\verb|TABLEID| may be a number or a string from the file
+\verb|/etc/iproute2/rt_tables|. If this parameter is omitted,
+\verb|ip| assumes the \verb|main| table, with the exception of
+\verb|local|, \verb|broadcast| and \verb|nat| routes, which are
+put into the \verb|local| table by default.
+
+\item \verb|dev NAME|
+
+--- the output device name.
+
+\item \verb|via ADDRESS|
+
+--- the address of the nexthop router. Actually, the sense of this field depends
+on the route type. For normal \verb|unicast| routes it is either the true nexthop
+router or, if it is a direct route installed in BSD compatibility mode,
+it can be a local address of the interface.
+For NAT routes it is the first address of the block of translated IP destinations.
+
+\item \verb|src ADDRESS|
+
+--- the source address to prefer when sending to the destinations
+covered by the route prefix.
+
+\item \verb|realm REALMID|
+
+--- the realm to which this route is assigned.
+\verb|REALMID| may be a number or a string from the file
+\verb|/etc/iproute2/rt_realms|. Sec.\ref{RT-REALMS} (p.\pageref{RT-REALMS})
+contains more information on realms.
+
+\item \verb|mtu MTU| or \verb|mtu lock MTU|
+
+--- the MTU along the path to the destination. If the modifier \verb|lock| is
+not used, the MTU may be updated by the kernel due to Path MTU Discovery.
+If the modifier \verb|lock| is used, no path MTU discovery will be tried,
+all packets will be sent without the DF bit in IPv4 case
+or fragmented to MTU for IPv6.
+
+\item \verb|window NUMBER|
+
+--- the maximal window for TCP to advertise to these destinations,
+measured in bytes. It limits maximal data bursts that our TCP
+peers are allowed to send to us.
+
+\item \verb|rtt NUMBER|
+
+--- the initial RTT (``Round Trip Time'') estimate.
+
+
+\item \verb|rttvar NUMBER|
+
+--- \threeonly the initial RTT variance estimate.
+
+
+\item \verb|ssthresh NUMBER|
+
+--- \threeonly an estimate for the initial slow start threshold.
+
+
+\item \verb|cwnd NUMBER|
+
+--- \threeonly the clamp for congestion window. It is ignored if the \verb|lock|
+    flag is not used.
+
+
+\item \verb|advmss NUMBER|
+
+--- \threeonly the MSS (``Maximal Segment Size'') to advertise to these
+    destinations when establishing TCP connections. If it is not given,
+    Linux uses a default value calculated from the first hop device MTU.
+
+\begin{NB}
+  If the path to these destination is asymmetric, this guess may be wrong.
+\end{NB}
+
+\item \verb|reordering NUMBER|
+
+--- \threeonly Maximal reordering on the path to this destination.
+    If it is not given, Linux uses the value selected with \verb|sysctl|
+    variable \verb|net/ipv4/tcp_reordering|.
+
+\item \verb|hoplimit NUMBER|
+
+--- [2.5.74+ only] Maximum number of hops on the path to this destination.
+    The default is the value selected with the \verb|sysctl| variable
+    \verb|net/ipv4/ip_default_ttl|.
+
+\item \verb|initcwnd NUMBER|
+--- [2.5.70+ only] 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~\cite{RFC2414}.
+
++\item \verb|initrwnd NUMBER|
+ 
++--- [2.6.33+ only] Initial receive window size for connections to 
++    this destination. The actual window size is this value multiplied
++    by the MSS (''Maximal Segment Size'') of the connection. The default
++    value is zero, meaning to use Slow Start value.
+ 
+\item \verb|nexthop NEXTHOP|
+
+--- the nexthop of a multipath route. \verb|NEXTHOP| is a complex value
+with its own syntax similar to the top level argument lists:
+\begin{itemize}
+\item \verb|via ADDRESS| is the nexthop router.
+\item \verb|dev NAME| is the output device.
+\item \verb|weight NUMBER| is a weight for this element of a multipath
+route reflecting its relative bandwidth or quality.
+\end{itemize}
+
+\item \verb|scope SCOPE_VAL|
+
+--- the scope of the destinations covered by the route prefix.
+\verb|SCOPE_VAL| may be a number or a string from the file
+\verb|/etc/iproute2/rt_scopes|.
+If this parameter is omitted,
+\verb|ip| assumes scope \verb|global| for all gatewayed \verb|unicast|
+routes, scope \verb|link| for direct \verb|unicast| and \verb|broadcast| routes
+and scope \verb|host| for \verb|local| routes.
+
+\item \verb|protocol RTPROTO|
+
+--- the routing protocol identifier of this route.
+\verb|RTPROTO| may be a number or a string from the file
+\verb|/etc/iproute2/rt_protos|. If the routing protocol ID is
+not given, \verb|ip| assumes protocol \verb|boot| (i.e.\
+it assumes the route was added by someone who doesn't
+understand what they are doing). Several protocol values have a fixed interpretation.
+Namely:
+\begin{itemize}
+\item \verb|redirect| --- the route was installed due to an ICMP redirect.
+\item \verb|kernel| --- the route was installed by the kernel during
+autoconfiguration.
+\item \verb|boot| --- the route was installed during the bootup sequence.
+If a routing daemon starts, it will purge all of them.
+\item \verb|static| --- the route was installed by the administrator
+to override dynamic routing. Routing daemon will respect them
+and, probably, even advertise them to its peers.
+\item \verb|ra| --- the route was installed by Router Discovery protocol.
+\end{itemize}
+The rest of the values are not reserved and the administrator is free
+to assign (or not to assign) protocol tags. At least, routing
+daemons should take care of setting some unique protocol values,
+f.e.\ as they are assigned in \verb|rtnetlink.h| or in \verb|rt_protos|
+database.
+
+
+\item \verb|onlink|
+
+--- pretend that the nexthop is directly attached to this link,
+even if it does not match any interface prefix. One application of this
+option may be found in~\cite{IP-TUNNELS}.
+
+\item \verb|pref PREF|
+
+--- the IPv6 route preference.
+\verb|PREF| PREF is a string specifying the route preference as defined in
+RFC4191 for Router Discovery messages. Namely:
+\begin{itemize}
+\item \verb|low| --- the route has a lowest priority.
+\item \verb|medium| --- the route has a default priority.
+\item \verb|high| --- the route has a highest priority.
+\end{itemize}
+
+\end{itemize}
+
+
+\begin{NB}
+  Actually there are more commands: \verb|prepend| does the same
+  thing as classic \verb|route add|, i.e.\ adds a route, even if another
+  route to the same destination exists. Its opposite case is \verb|append|,
+  which adds the route to the end of the list. Avoid these
+  features.
+\end{NB}
+\begin{NB}
+  More sad news, IPv6 only understands the \verb|append| command correctly.
+  All the others are translated into \verb|append| commands. Certainly,
+  this will change in the future.
+\end{NB}
+
+\paragraph{Examples:}
+\begin{itemize}
+\item add a plain route to network 10.0.0/24 via gateway 193.233.7.65
+\begin{verbatim}
+  ip route add 10.0.0/24 via 193.233.7.65
+\end{verbatim}
+\item change it to a direct route via the \verb|dummy| device
+\begin{verbatim}
+  ip ro chg 10.0.0/24 dev dummy
+\end{verbatim}
+\item add a default multipath route splitting the load between \verb|ppp0|
+and \verb|ppp1|
+\begin{verbatim}
+  ip route add default scope global nexthop dev ppp0 \
+                                    nexthop dev ppp1
+\end{verbatim}
+Note the scope value. It is not necessary but it informs the kernel
+that this route is gatewayed rather than direct. Actually, if you
+know the addresses of remote endpoints it would be better to use the
+\verb|via| parameter.
+\item announce that the address 192.203.80.144 is not a real one, but
+should be translated to 193.233.7.83 before forwarding
+\begin{verbatim}
+  ip route add nat 192.203.80.144 via 193.233.7.83
+\end{verbatim}
+Backward translation is setup with policy rules described
+in the following section (sec.\ref{IP-RULE}, p.\pageref{IP-RULE}).
+\end{itemize}
+
+\subsection{{\tt ip route delete} --- delete a route}
+
+\paragraph{Abbreviations:} \verb|delete|, \verb|del|, \verb|d|.
+
+\paragraph{Arguments:} \verb|ip route del| has the same arguments as
+\verb|ip route add|, but their semantics are a bit different.
+
+Key values (\verb|to|, \verb|tos|, \verb|preference| and \verb|table|)
+select the route to delete. If optional attributes are present, \verb|ip|
+verifies that they coincide with the attributes of the route to delete.
+If no route with the given key and attributes was found, \verb|ip route del|
+fails.
+\begin{NB}
+Linux-2.0 had the option to delete a route selected only by prefix address,
+ignoring its length (i.e.\ netmask). This option no longer exists
+because it was ambiguous. However, look at {\tt ip route flush}
+(sec.\ref{IP-ROUTE-FLUSH}, p.\pageref{IP-ROUTE-FLUSH}) which
+provides similar and even richer functionality.
+\end{NB}
+
+\paragraph{Example:}
+\begin{itemize}
+\item delete the multipath route created by the command in previous subsection
+\begin{verbatim}
+  ip route del default scope global nexthop dev ppp0 \
+                                    nexthop dev ppp1
+\end{verbatim}
+\end{itemize}
+
+
+
+\subsection{{\tt ip route show} --- list routes}
+
+\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|.
+
+\paragraph{Description:} the command displays the contents of the routing tables
+or the route(s) selected by some criteria.
+
+
+\paragraph{Arguments:}
+\begin{itemize}
+\item \verb|to SELECTOR| (default)
+
+--- only select routes from the given range of destinations. \verb|SELECTOR|
+consists of an optional modifier (\verb|root|, \verb|match| or \verb|exact|)
+and a prefix. \verb|root PREFIX| selects routes with prefixes not shorter
+than \verb|PREFIX|. F.e.\ \verb|root 0/0| selects the entire routing table.
+\verb|match PREFIX| selects routes with prefixes not longer than
+\verb|PREFIX|. F.e.\ \verb|match 10.0/16| selects \verb|10.0/16|,
+\verb|10/8| and \verb|0/0|, but it does not select \verb|10.1/16| and
+\verb|10.0.0/24|. And \verb|exact PREFIX| (or just \verb|PREFIX|)
+selects routes with this exact prefix. If neither of these options
+are present, \verb|ip| assumes \verb|root 0/0| i.e.\ it lists the entire table.
+
+
+\item \verb|tos TOS| or \verb|dsfield TOS|
+
+ --- only select routes with the given TOS.
+
+
+\item \verb|table TABLEID|
+
+ --- show the routes from this table(s). The default setting is to show
+\verb|table| \verb|main|. \verb|TABLEID| may either be the ID of a real table
+or one of the special values:
+  \begin{itemize}
+  \item \verb|all| --- list all of the tables.
+  \item \verb|cache| --- dump the routing cache.
+  \end{itemize}
+\begin{NB}
+  IPv6 has a single table. However, splitting it into \verb|main|, \verb|local|
+  and \verb|cache| is emulated by the \verb|ip| utility.
+\end{NB}
+
+\item \verb|cloned| or \verb|cached|
+
+--- list cloned routes i.e.\ routes which were dynamically forked from
+other routes because some route attribute (f.e.\ MTU) was updated.
+Actually, it is equivalent to \verb|table cache|.
+
+\item \verb|from SELECTOR|
+
+--- the same syntax as for \verb|to|, but it binds the source address range
+rather than destinations. Note that the \verb|from| option only works with
+cloned routes.
+
+\item \verb|protocol RTPROTO|
+
+--- only list routes of this protocol.
+
+
+\item \verb|scope SCOPE_VAL|
+
+--- only list routes with this scope.
+
+\item \verb|type TYPE|
+
+--- only list routes of this type.
+
+\item \verb|dev NAME|
+
+--- only list routes going via this device.
+
+\item \verb|via PREFIX|
+
+--- only list routes going via the nexthop routers selected by \verb|PREFIX|.
+
+\item \verb|src PREFIX|
+
+--- only list routes with preferred source addresses selected
+by \verb|PREFIX|.
+
+\item \verb|realm REALMID| or \verb|realms FROMREALM/TOREALM|
+
+--- only list routes with these realms.
+
+\end{itemize}
+
+\paragraph{Examples:} Let us count routes of protocol \verb|gated/bgp|
+on a router:
+\begin{verbatim}
+kuznet@amber:~ $ ip ro ls proto gated/bgp | wc
+   1413    9891    79010
+kuznet@amber:~ $
+\end{verbatim}
+To count the size of the routing cache, we have to use the \verb|-o| option
+because cached attributes can take more than one line of output:
+\begin{verbatim}
+kuznet@amber:~ $ ip -o ro ls cloned | wc
+   159    2543    18707
+kuznet@amber:~ $
+\end{verbatim}
+
+
+\paragraph{Output format:} The output of this command consists
+of per route records separated by line feeds.
+However, some records may consist
+of more than one line: particularly, this is the case when the route
+is cloned or you requested additional statistics. If the
+\verb|-o| option was given, then line feeds separating lines inside
+records are replaced with the backslash sign.
+
+The output has the same syntax as arguments given to {\tt ip route add},
+so that it can be understood easily. F.e.\
+\begin{verbatim}
+kuznet@amber:~ $ ip ro ls 193.233.7/24
+193.233.7.0/24 dev eth0  proto gated/conn  scope link \
+    src 193.233.7.65 realms inr.ac 
+kuznet@amber:~ $
+\end{verbatim}
+
+If you list cloned entries, the output contains other attributes which
+are evaluated during route calculation and updated during route
+lifetime. An example of the output is:
+\begin{verbatim}
+kuznet@amber:~ $ ip ro ls 193.233.7.82 tab cache
+193.233.7.82 from 193.233.7.82 dev eth0  src 193.233.7.65 \
+  realms inr.ac/inr.ac 
+    cache <src-direct,redirect>  mtu 1500 rtt 300 iif eth0
+193.233.7.82 dev eth0  src 193.233.7.65 realms inr.ac 
+    cache  mtu 1500 rtt 300
+kuznet@amber:~ $
+\end{verbatim}
+\begin{NB}
+  \label{NB-strange-route}
+  The route looks a bit strange, doesn't it? Did you notice that
+  it is a path from 193.233.7.82 back to 193.233.82? Well, you will
+  see in the section on \verb|ip route get| (p.\pageref{NB-nature-of-strangeness})
+  how it appeared.
+\end{NB}
+The second line, starting with the word \verb|cache|, shows
+additional attributes which normal routes do not possess.
+Cached flags are summarized in angle brackets:
+\begin{itemize}
+\item \verb|local| --- packets are delivered locally.
+It stands for loopback unicast routes, for broadcast routes
+and for multicast routes, if this host is a member of the corresponding
+group.
+
+\item \verb|reject| --- the path is bad. Any attempt to use it results
+in an error. See attribute \verb|error| below (p.\pageref{IP-ROUTE-GET-error}).
+
+\item \verb|mc| --- the destination is multicast.
+
+\item \verb|brd| --- the destination is broadcast.
+
+\item \verb|src-direct| --- the source is on a directly connected
+interface.
+
+\item \verb|redirected| --- the route was created by an ICMP Redirect.
+
+\item \verb|redirect| --- packets going via this route will 
+trigger an ICMP redirect.
+
+\item \verb|fastroute| --- the route is eligible to be used for fastroute.
+
+\item \verb|equalize| --- make packet by packet randomization
+along this path.
+
+\item \verb|dst-nat| --- the destination address requires translation.
+
+\item \verb|src-nat| --- the source address requires translation.
+
+\item \verb|masq| --- the source address requires masquerading.
+This feature disappeared in linux-2.4.
+
+\item \verb|notify| --- ({\em not implemented}) change/deletion
+of this route will trigger RTNETLINK notification.
+\end{itemize}
+
+Then some optional attributes follow:
+\begin{itemize}
+\item \verb|error| --- on \verb|reject| routes it is error code
+returned to local senders when they try to use this route.
+These error codes are translated into ICMP error codes, sent to remote
+senders, according to the rules described above in the subsection
+devoted to route types (p.\pageref{IP-ROUTE-TYPES}).
+\label{IP-ROUTE-GET-error}
+
+\item \verb|expires| --- this entry will expire after this timeout.
+
+\item \verb|iif| --- the packets for this path are expected to arrive
+on this interface.
+\end{itemize}
+
+\paragraph{Statistics:} With the \verb|-statistics| option, more
+information about this route is shown:
+\begin{itemize}
+\item \verb|users| --- the number of users of this entry.
+\item \verb|age| --- shows when this route was last used.
+\item \verb|used| --- the number of lookups of this route since its creation.
+\end{itemize}
+
+\subsection{{\tt ip route 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}
+
+\paragraph{Abbreviations:} \verb|flush|, \verb|f|.
+
+\paragraph{Description:} this command flushes routes selected
+by some criteria.
+
+\paragraph{Arguments:} the arguments have the same syntax and semantics
+as the arguments of \verb|ip route show|, but routing tables are not
+listed but purged. The only difference is the default action: \verb|show|
+dumps all the IP main routing table but \verb|flush| prints the helper page.
+The reason for this difference does not require any explanation, does it?
+
+
+\paragraph{Statistics:} With the \verb|-statistics| option, the command
+becomes verbose. It prints out the number of deleted routes and the number
+of rounds made to flush the routing table. If the option is given
+twice, \verb|ip route flush| also dumps all the deleted routes
+in the format described in the previous subsection.
+
+\paragraph{Examples:} The first example flushes all the
+gatewayed routes from the main table (f.e.\ after a routing daemon crash).
+\begin{verbatim}
+netadm@amber:~ # ip -4 ro flush scope global type unicast
+\end{verbatim}
+This option deserves to be put into a scriptlet \verb|routef|.
+\begin{NB}
+This option was described in the \verb|route(8)| man page borrowed
+from BSD, but was never implemented in Linux.
+\end{NB}
+
+The second example flushes all IPv6 cloned routes:
+\begin{verbatim}
+netadm@amber:~ # ip -6 -s -s ro flush cache
+3ffe:2400::220:afff:fef4:c5d1 via 3ffe:2400::220:afff:fef4:c5d1 \
+  dev eth0  metric 0 
+    cache  used 2 age 12sec mtu 1500 rtt 300
+3ffe:2400::280:adff:feb7:8034 via 3ffe:2400::280:adff:feb7:8034 \
+  dev eth0  metric 0 
+    cache  used 2 age 15sec mtu 1500 rtt 300
+3ffe:2400::280:c8ff:fe59:5bcc via 3ffe:2400::280:c8ff:fe59:5bcc \
+  dev eth0  metric 0 
+    cache  users 1 used 1 age 23sec mtu 1500 rtt 300
+3ffe:2400:0:1:2a0:ccff:fe66:1878 via 3ffe:2400:0:1:2a0:ccff:fe66:1878 \
+  dev eth1  metric 0 
+    cache  used 2 age 20sec mtu 1500 rtt 300
+3ffe:2400:0:1:a00:20ff:fe71:fb30 via 3ffe:2400:0:1:a00:20ff:fe71:fb30 \
+  dev eth1  metric 0 
+    cache  used 2 age 33sec mtu 1500 rtt 300
+ff02::1 via ff02::1 dev eth1  metric 0 
+    cache  users 1 used 1 age 45sec mtu 1500 rtt 300
+
+*** Round 1, deleting 6 entries ***
+*** Flush is complete after 1 round ***
+netadm@amber:~ # ip -6 -s -s ro flush cache
+Nothing to flush.
+netadm@amber:~ #
+\end{verbatim}
+
+The third example flushes BGP routing tables after a \verb|gated|
+death.
+\begin{verbatim}
+netadm@amber:~ # ip ro ls proto gated/bgp | wc
+   1408    9856    78730
+netadm@amber:~ # ip -s ro f proto gated/bgp
+
+*** Round 1, deleting 1408 entries ***
+*** Flush is complete after 1 round ***
+netadm@amber:~ # ip ro f proto gated/bgp
+Nothing to flush.
+netadm@amber:~ # ip ro ls proto gated/bgp
+netadm@amber:~ #
+\end{verbatim}
+
+
+\subsection{{\tt ip route get} --- get a single route}
+\label{IP-ROUTE-GET}
+
+\paragraph{Abbreviations:} \verb|get|, \verb|g|.
+
+\paragraph{Description:} this command gets a single route to a destination
+and prints its contents exactly as the kernel sees it.
+
+\paragraph{Arguments:} 
+\begin{itemize}
+\item \verb|to ADDRESS| (default)
+
+--- the destination address.
+
+\item \verb|from ADDRESS|
+
+--- the source address.
+
+\item \verb|tos TOS| or \verb|dsfield TOS|
+
+--- the Type Of Service.
+
+\item \verb|iif NAME|
+
+--- the device from which this packet is expected to arrive.
+
+\item \verb|oif NAME|
+
+--- force the output device on which this packet will be routed.
+
+\item \verb|connected|
+
+--- if no source address (option \verb|from|) was given, relookup
+the route with the source set to the preferred address received from the first lookup.
+If policy routing is used, it may be a different route.
+
+\end{itemize}
+
+Note that this operation is not equivalent to \verb|ip route show|.
+\verb|show| shows existing routes. \verb|get| resolves them and
+creates new clones if necessary. Essentially, \verb|get|
+is equivalent to sending a packet along this path.
+If the \verb|iif| argument is not given, the kernel creates a route
+to output packets towards the requested destination.
+This is equivalent to pinging the destination
+with a subsequent {\tt ip route ls cache}, however, no packets are
+actually sent. With the \verb|iif| argument, the kernel pretends
+that a packet arrived from this interface and searches for
+a path to forward the packet.
+
+\paragraph{Output format:} This command outputs routes in the same
+format as \verb|ip route ls|.
+
+\paragraph{Examples:} 
+\begin{itemize}
+\item Find a route to output packets to 193.233.7.82:
+\begin{verbatim}
+kuznet@amber:~ $ ip route get 193.233.7.82
+193.233.7.82 dev eth0  src 193.233.7.65 realms inr.ac
+    cache  mtu 1500 rtt 300
+kuznet@amber:~ $
+\end{verbatim}
+
+\item Find a route to forward packets arriving on \verb|eth0|
+from 193.233.7.82 and destined for 193.233.7.82:
+\begin{verbatim}
+kuznet@amber:~ $ ip r g 193.233.7.82 from 193.233.7.82 iif eth0
+193.233.7.82 from 193.233.7.82 dev eth0  src 193.233.7.65 \
+  realms inr.ac/inr.ac 
+    cache <src-direct,redirect>  mtu 1500 rtt 300 iif eth0
+kuznet@amber:~ $
+\end{verbatim}
+\begin{NB}
+  \label{NB-nature-of-strangeness}
+  This is the command that created the funny route from 193.233.7.82
+  looped back to 193.233.7.82 (cf.\ NB on~p.\pageref{NB-strange-route}).
+  Note the \verb|redirect| flag on it.
+\end{NB}
+
+\item Find a multicast route for packets arriving on \verb|eth0|
+from host 193.233.7.82 and destined for multicast group 224.2.127.254
+(it is assumed that a multicast routing daemon is running.
+In this case, it is \verb|pimd|)
+\begin{verbatim}
+kuznet@amber:~ $ ip r g 224.2.127.254 from 193.233.7.82 iif eth0
+multicast 224.2.127.254 from 193.233.7.82 dev lo  \
+  src 193.233.7.65 realms inr.ac/cosmos 
+    cache <mc> iif eth0 Oifs: eth1 pimreg
+kuznet@amber:~ $
+\end{verbatim}
+This route differs from the ones seen before. It contains a ``normal'' part
+and a ``multicast'' part. The normal part is used to deliver (or not to
+deliver) the packet to local IP listeners. In this case the router
+is not a member
+of this group, so that route has no \verb|local| flag and only
+forwards packets. The output device for such entries is always loopback.
+The multicast part consists of an additional \verb|Oifs:| list showing
+the output interfaces.
+\end{itemize}
+
+
+It is time for a more complicated example. Let us add an invalid
+gatewayed route for a destination which is really directly connected:
+\begin{verbatim}
+netadm@alisa:~ # ip route add 193.233.7.98 via 193.233.7.254
+netadm@alisa:~ # ip route get 193.233.7.98
+193.233.7.98 via 193.233.7.254 dev eth0  src 193.233.7.90
+    cache  mtu 1500 rtt 3072
+netadm@alisa:~ #
+\end{verbatim}
+and probe it with ping:
+\begin{verbatim}
+netadm@alisa:~ # ping -n 193.233.7.98
+PING 193.233.7.98 (193.233.7.98) from 193.233.7.90 : 56 data bytes
+From 193.233.7.254: Redirect Host(New nexthop: 193.233.7.98)
+64 bytes from 193.233.7.98: icmp_seq=0 ttl=255 time=3.5 ms
+From 193.233.7.254: Redirect Host(New nexthop: 193.233.7.98)
+64 bytes from 193.233.7.98: icmp_seq=1 ttl=255 time=2.2 ms
+64 bytes from 193.233.7.98: icmp_seq=2 ttl=255 time=0.4 ms
+64 bytes from 193.233.7.98: icmp_seq=3 ttl=255 time=0.4 ms
+64 bytes from 193.233.7.98: icmp_seq=4 ttl=255 time=0.4 ms
+^C
+--- 193.233.7.98 ping statistics ---
+5 packets transmitted, 5 packets received, 0% packet loss
+round-trip min/avg/max = 0.4/1.3/3.5 ms
+netadm@alisa:~ #
+\end{verbatim}
+What happened? Router 193.233.7.254 understood that we have a much
+better path to the destination and sent us an ICMP redirect message.
+We may retry \verb|ip route get| to see what we have in the routing
+tables now:
+\begin{verbatim}
+netadm@alisa:~ # ip route get 193.233.7.98
+193.233.7.98 dev eth0  src 193.233.7.90 
+    cache <redirected>  mtu 1500 rtt 3072
+netadm@alisa:~ #
+\end{verbatim}
+
+
+
+\section{{\tt ip rule} --- routing policy database management}
+\label{IP-RULE}
+
+\paragraph{Abbreviations:} \verb|rule|, \verb|ru|.
+
+\paragraph{Object:} \verb|rule|s in the routing policy database control
+the route selection algorithm.
+
+Classic routing algorithms used in the Internet make routing decisions
+based only on the destination address of packets (and in theory,
+but not in practice, on the TOS field). The seminal review of classic
+routing algorithms and their modifications can be found in~\cite{RFC1812}.
+
+In some circumstances we want to route packets differently depending not only
+on destination addresses, but also on other packet fields: source address,
+IP protocol, transport protocol ports or even packet payload.
+This task is called ``policy routing''.
+
+\begin{NB}
+  ``policy routing'' $\neq$ ``routing policy''.
+
+\noindent	``policy routing'' $=$ ``cunning routing''.
+
+\noindent	``routing policy'' $=$ ``routing tactics'' or ``routing plan''.
+\end{NB}
+
+To solve this task, the conventional destination based routing table, ordered
+according to the longest match rule, is replaced with a ``routing policy
+database'' (or RPDB), which selects routes
+by executing some set of rules. The rules may have lots of keys of different
+natures and therefore they have no natural ordering, but one imposed
+by the administrator. Linux-2.2 RPDB is a linear list of rules
+ordered by numeric priority value.
+RPDB explicitly allows matching a few packet fields:
+
+\begin{itemize}
+\item packet source address.
+\item packet destination address.
+\item TOS.
+\item incoming interface (which is packet metadata, rather than a packet field).
+\end{itemize}
+
+Matching IP protocols and transport ports is also possible,
+indirectly, via \verb|ipchains|, by exploiting their ability
+to mark some classes of packets with \verb|fwmark|. Therefore,
+\verb|fwmark| is also included in the set of keys checked by rules.
+
+Each policy routing rule consists of a {\em selector\/} and an {\em action\/}
+predicate. The RPDB is scanned in the order of increasing priority. The selector
+of each rule is applied to \{source address, destination address, incoming
+interface, tos, fwmark\} and, if the selector matches the packet,
+the action is performed.  The action predicate may return with success.
+In this case, it will either give a route or failure indication
+and the RPDB lookup is terminated. Otherwise, the RPDB program
+continues on the next rule.
+
+What is the action, semantically? The natural action is to select the
+nexthop and the output device. This is what
+Cisco IOS~\cite{IOS} does. Let us call it ``match \& set''.
+The Linux-2.2 approach is more flexible. The action includes
+lookups in destination-based routing tables and selecting
+a route from these tables according to the classic longest match algorithm.
+The ``match \& set'' approach is the simplest case of the Linux one. It is realized
+when a second level routing table contains a single default route.
+Recall that Linux-2.2 supports multiple tables
+managed with the \verb|ip route| command, described in the previous section.
+
+At startup time the kernel configures the default RPDB consisting of three
+rules:
+
+\begin{enumerate}
+\item Priority: 0, Selector: match anything, Action: lookup routing
+table \verb|local| (ID 255).
+The \verb|local| table is a special routing table containing
+high priority control routes for local and broadcast addresses.
+
+Rule 0 is special. It cannot be deleted or overridden.
+
+
+\item Priority: 32766, Selector: match anything, Action: lookup routing
+table \verb|main| (ID 254).
+The \verb|main| table is the normal routing table containing all non-policy
+routes. This rule may be deleted and/or overridden with other
+ones by the administrator.
+
+\item Priority: 32767, Selector: match anything, Action: lookup routing
+table \verb|default| (ID 253).
+The \verb|default| table is empty. It is reserved for some
+post-processing if no previous default rules selected the packet.
+This rule may also be deleted.
+
+\end{enumerate}
+
+Do not confuse routing tables with rules: rules point to routing tables,
+several rules may refer to one routing table and some routing tables
+may have no rules pointing to them. If the administrator deletes all the rules
+referring to a table, the table is not used, but it still exists
+and will disappear only after all the routes contained in it are deleted.
+
+
+\paragraph{Rule attributes:} Each RPDB entry has additional
+attributes. F.e.\ each rule has a pointer to some routing
+table. NAT and masquerading rules have an attribute to select new IP
+address to translate/masquerade. Besides that, rules have some
+optional attributes, which routes have, namely \verb|realms|.
+These values do not override those contained in the routing tables. They
+are only used if the route did not select any attributes.
+
+
+\paragraph{Rule types:} The RPDB may contain rules of the following
+types:
+\begin{itemize}
+\item \verb|unicast| --- the rule prescribes to return the route found
+in the routing table referenced by the rule.
+\item \verb|blackhole| --- the rule prescribes to silently drop the packet.
+\item \verb|unreachable| --- the rule prescribes to generate a ``Network
+is unreachable'' error.
+\item \verb|prohibit| --- the rule prescribes to generate
+``Communication is administratively prohibited'' error.
+\item \verb|nat| --- the rule prescribes to translate the source address
+of the IP packet into some other value. More about NAT is
+in Appendix~\ref{ROUTE-NAT}, p.\pageref{ROUTE-NAT}.
+\end{itemize}
+
+
+\paragraph{Commands:} \verb|add|, \verb|delete| and \verb|show|
+(or \verb|list|).
+
+\subsection{{\tt ip rule add} --- insert a new rule\\
+	{\tt ip rule delete} --- delete a rule}
+\label{IP-RULE-ADD}
+
+\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|delete|, \verb|del|,
+	\verb|d|.
+
+\paragraph{Arguments:}
+
+\begin{itemize}
+\item \verb|type TYPE| (default)
+
+--- the type of this rule. The list of valid types was given in the previous
+subsection.
+
+\item \verb|from PREFIX|
+
+--- select the source prefix to match.
+
+\item \verb|to PREFIX|
+
+--- select the destination prefix to match.
+
+\item \verb|iif NAME|
+
+--- select the incoming device to match. If the interface is loopback,
+the rule only matches packets originating from this host. This means that you
+may create separate routing tables for forwarded and local packets and,
+hence, completely segregate them.
+
+\item \verb|tos TOS| or \verb|dsfield TOS|
+
+--- select the TOS value to match.
+
+\item \verb|fwmark MARK|
+
+--- select the \verb|fwmark| value to match.
+
+\item \verb|priority PREFERENCE|
+
+--- the priority of this rule. Each rule should have an explicitly
+set {\em unique\/} priority value.
+\begin{NB}
+  Really, for historical reasons \verb|ip rule add| does not require a
+  priority value and allows them to be non-unique.
+  If the user does not supplied a priority, it is selected by the kernel.
+  If the user creates a rule with a priority value that
+  already exists, the kernel does not reject the request. It adds
+  the new rule before all old rules of the same priority.
+
+  It is mistake in design, no more. And it will be fixed one day,
+  so do not rely on this feature. Use explicit priorities.
+\end{NB}
+
+
+\item \verb|table TABLEID|
+
+--- the routing table identifier to lookup if the rule selector matches.
+
+\item \verb|realms FROM/TO|
+
+--- Realms to select if the rule matched and the routing table lookup
+succeeded. Realm \verb|TO| is only used if the route did not select
+any realm.
+
+\item \verb|nat ADDRESS|
+
+--- The base of the IP address block to translate (for source addresses).
+The \verb|ADDRESS| may be either the start of the block of NAT addresses
+(selected by NAT routes) or in linux-2.2 a local host address (or even zero).
+In the last case the router does not translate the packets,
+but masquerades them to this address; this feature disappered in 2.4.
+More about NAT is in Appendix~\ref{ROUTE-NAT},
+p.\pageref{ROUTE-NAT}.
+
+\end{itemize}
+
+\paragraph{Warning:} Changes to the RPDB made with these commands
+do not become active immediately. It is assumed that after
+a script finishes a batch of updates, it flushes the routing cache
+with \verb|ip route flush cache|.
+
+\paragraph{Examples:}
+\begin{itemize}
+\item Route packets with source addresses from 192.203.80/24
+according to routing table \verb|inr.ruhep|:
+\begin{verbatim}
+ip ru add from 192.203.80.0/24 table inr.ruhep prio 220
+\end{verbatim}
+
+\item Translate packet source address 193.233.7.83 into 192.203.80.144
+and route it according to table \#1 (actually, it is \verb|inr.ruhep|):
+\begin{verbatim}
+ip ru add from 193.233.7.83 nat 192.203.80.144 table 1 prio 320
+\end{verbatim}
+
+\item Delete the unused default rule:
+\begin{verbatim}
+ip ru del prio 32767
+\end{verbatim}
+
+\end{itemize}
+
+
+
+\subsection{{\tt ip rule show} --- list rules}
+\label{IP-RULE-SHOW}
+
+\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|.
+
+
+\paragraph{Arguments:} Good news, this is one command that has no arguments.
+
+\paragraph{Output format:}
+
+\begin{verbatim}
+kuznet@amber:~ $ ip ru ls
+0:	from all lookup local 
+200:	from 192.203.80.0/24 to 193.233.7.0/24 lookup main
+210:	from 192.203.80.0/24 to 192.203.80.0/24 lookup main
+220:	from 192.203.80.0/24 lookup inr.ruhep realms inr.ruhep/radio-msu
+300:	from 193.233.7.83 to 193.233.7.0/24 lookup main
+310:	from 193.233.7.83 to 192.203.80.0/24 lookup main
+320:	from 193.233.7.83 lookup inr.ruhep map-to 192.203.80.144
+32766:	from all lookup main 
+kuznet@amber:~ $
+\end{verbatim}
+
+In the first column is the rule priority value followed
+by a colon. Then the selectors follow. Each key is prefixed
+with the same keyword that was used to create the rule.
+
+The keyword \verb|lookup| is followed by a routing table identifier,
+as it is recorded in the file \verb|/etc/iproute2/rt_tables|.
+
+If the rule does NAT (f.e.\ rule \#320), it is shown by the keyword
+\verb|map-to| followed by the start of the block of addresses to map.
+
+The sense of this example is pretty simple. The prefixes
+192.203.80.0/24 and 193.233.7.0/24 form the internal network, but
+they are routed differently when the packets leave it.
+Besides that, the host 193.233.7.83 is translated into
+another prefix to look like 192.203.80.144 when talking
+to the outer world.
+
+\subsection{{\tt ip rule save} -- save rules tables}
+\label{IP-RULE-SAVE}
+
+\paragraph{Description:} this command saves the contents of the rules
+tables or the rule(s) selected by some criteria to standard output.
+
+\paragraph{Arguments:} \verb|ip rule save| has the same arguments as
+\verb|ip rule show|.
+
+\paragraph{Example:} This saves all the rules to the {\tt saved\_rules}
+file:
+\begin{verbatim}
+dan@caffeine:~ # ip rule save > saved_rules
+\end{verbatim}
+
+\paragraph{Output format:} The format of the data stream provided by
+\verb|ip rule save| is that of \verb|rtnetlink|.  See
+\verb|rtnetlink(7)| for more information.
+
+\subsection{{\tt ip rule restore} -- restore rules tables}
+\label{IP-RULE-RESTORE}
+
+\paragraph{Description:} this command restores the contents of the rules
+tables according to a data stream as provided by \verb|ip rule save| via
+standard input.  Note that any rules already in the table are left unchanged,
+and duplicates are not ignored.
+
+\paragraph{Arguments:} This command takes no arguments.
+
+\paragraph{Example:} This restores all rules that were saved to the
+{\tt saved\_rules} file:
+
+\begin{verbatim}
+dan@caffeine:~ # ip rule restore < saved_rules
+\end{verbatim}
+
+
+
+\section{{\tt ip maddress} --- multicast addresses management}
+\label{IP-MADDR}
+
+\paragraph{Object:} \verb|maddress| objects are multicast addresses.
+
+\paragraph{Commands:} \verb|add|, \verb|delete|, \verb|show| (or \verb|list|).
+
+\subsection{{\tt ip maddress show} --- list multicast addresses}
+
+\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|.
+
+\paragraph{Arguments:}
+
+\begin{itemize}
+
+\item \verb|dev NAME| (default)
+
+--- the device name.
+
+\end{itemize}
+
+\paragraph{Output format:}
+
+\begin{verbatim}
+kuznet@alisa:~ $ ip maddr ls dummy
+2:  dummy
+    link  33:33:00:00:00:01
+    link  01:00:5e:00:00:01
+    inet  224.0.0.1 users 2
+    inet6 ff02::1
+kuznet@alisa:~ $ 
+\end{verbatim}
+
+The first line of the output shows the interface index and its name.
+Then the multicast address list follows. Each line starts with the
+protocol identifier. The word \verb|link| denotes a link layer
+multicast addresses.
+
+If a multicast address has more than one user, the number
+of users is shown after the \verb|users| keyword.
+
+One additional feature not present in the example above
+is the \verb|static| flag, which indicates that the address was joined
+with \verb|ip maddr add|. See the following subsection.
+
+
+
+\subsection{{\tt ip maddress add} --- add a multicast address\\
+	    {\tt ip maddress delete} --- delete a multicast address}
+
+\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|delete|, \verb|del|, \verb|d|.
+
+\paragraph{Description:} these commands attach/detach
+a static link layer multicast address to listen on the interface.
+Note that it is impossible to join protocol multicast groups
+statically. This command only manages link layer addresses.
+
+
+\paragraph{Arguments:}
+
+\begin{itemize}
+\item \verb|address LLADDRESS| (default)
+
+--- the link layer multicast address.
+
+\item \verb|dev NAME|
+
+--- the device to join/leave this multicast address.
+
+\end{itemize}
+
+
+\paragraph{Example:} Let us continue with the example from the previous subsection.
+
+\begin{verbatim}
+netadm@alisa:~ # ip maddr add 33:33:00:00:00:01 dev dummy
+netadm@alisa:~ # ip -0 maddr ls dummy
+2:  dummy
+    link  33:33:00:00:00:01 users 2 static
+    link  01:00:5e:00:00:01
+netadm@alisa:~ # ip maddr del 33:33:00:00:00:01 dev dummy
+\end{verbatim}
+
+\begin{NB}
+ Neither \verb|ip| nor the kernel check for multicast address validity.
+ Particularly, this means that you can try to load a unicast address
+ instead of a multicast address. Most drivers will ignore such addresses,
+ but several (f.e.\ Tulip) will intern it to their on-board filter.
+ The effects may be strange. Namely, the addresses become additional
+ local link addresses and, if you loaded the address of another host
+ to the router, wait for duplicated packets on the wire.
+ It is not a bug, but rather a hole in the API and intra-kernel interfaces.
+ This feature is really more useful for traffic monitoring, but using it
+ with Linux-2.2 you {\em have to\/} be sure that the host is not
+ a router and, especially, that it is not a transparent proxy or masquerading
+ agent.
+\end{NB}
+
+
+
+\section{{\tt ip mroute} --- multicast routing cache management}
+\label{IP-MROUTE}
+
+\paragraph{Abbreviations:} \verb|mroute|, \verb|mr|.
+
+\paragraph{Object:} \verb|mroute| objects are multicast routing cache
+entries created by a user level mrouting daemon
+(f.e.\ \verb|pimd| or \verb|mrouted|).
+
+Due to the limitations of the current interface to the multicast routing
+engine, it is impossible to change \verb|mroute| objects administratively,
+so we may only display them. This limitation will be removed
+in the future.
+
+\paragraph{Commands:} \verb|show| (or \verb|list|).
+
+
+\subsection{{\tt ip mroute show} --- list mroute cache entries}
+
+\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|.
+
+\paragraph{Arguments:}
+
+\begin{itemize}
+\item \verb|to PREFIX| (default)
+
+--- the prefix selecting the destination multicast addresses to list.
+
+
+\item \verb|iif NAME|
+
+--- the interface on which multicast packets are received.
+
+
+\item \verb|from PREFIX|
+
+--- the prefix selecting the IP source addresses of the multicast route.
+
+
+\end{itemize}
+
+\paragraph{Output format:}
+
+\begin{verbatim}
+kuznet@amber:~ $ ip mroute ls
+(193.232.127.6, 224.0.1.39)      Iif: unresolved 
+(193.232.244.34, 224.0.1.40)     Iif: unresolved 
+(193.233.7.65, 224.66.66.66)     Iif: eth0       Oifs: pimreg 
+kuznet@amber:~ $ 
+\end{verbatim}
+
+Each line shows one (S,G) entry in the multicast routing cache,
+where S is the source address and G is the multicast group. \verb|Iif| is
+the interface on which multicast packets are expected to arrive.
+If the word \verb|unresolved| is there instead of the interface name,
+it means that the routing daemon still hasn't resolved this entry.
+The keyword \verb|oifs| is followed by a list of output interfaces, separated
+by spaces. If a multicast routing entry is created with non-trivial
+TTL scope, administrative distances are appended to the device names
+in the \verb|oifs| list.
+
+\paragraph{Statistics:} The \verb|-statistics| option also prints the
+number of packets and bytes forwarded along this route and
+the number of packets that arrived on the wrong interface, if this number is not zero.
+
+\begin{verbatim}
+kuznet@amber:~ $ ip -s mr ls 224.66/16
+(193.233.7.65, 224.66.66.66)     Iif: eth0       Oifs: pimreg 
+  9383 packets, 300256 bytes
+kuznet@amber:~ $
+\end{verbatim}
+
+
+\section{{\tt ip tunnel} --- tunnel configuration}
+\label{IP-TUNNEL}
+
+\paragraph{Abbreviations:} \verb|tunnel|, \verb|tunl|.
+
+\paragraph{Object:} \verb|tunnel| objects are tunnels, encapsulating
+packets in IPv4 packets and then sending them over the IP infrastructure.
+
+\paragraph{Commands:} \verb|add|, \verb|delete|, \verb|change|, \verb|show|
+(or \verb|list|).
+
+\paragraph{See also:} A more informal discussion of tunneling
+over IP and the \verb|ip tunnel| command can be found in~\cite{IP-TUNNELS}.
+
+\subsection{{\tt ip tunnel add} --- add a new tunnel\\
+	{\tt ip tunnel change} --- change an existing tunnel\\
+	{\tt ip tunnel delete} --- destroy a tunnel}
+
+\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|change|, \verb|chg|;
+\verb|delete|, \verb|del|, \verb|d|.
+
+
+\paragraph{Arguments:}
+
+\begin{itemize}
+
+\item \verb|name NAME| (default)
+
+--- select the tunnel device name.
+
+\item \verb|mode MODE|
+
+--- set the tunnel mode. Three modes are currently available:
+	\verb|ipip|, \verb|sit| and \verb|gre|.
+
+\item \verb|remote ADDRESS|
+
+--- set the remote endpoint of the tunnel.
+
+\item \verb|local ADDRESS|
+
+--- set the fixed local address for tunneled packets.
+It must be an address on another interface of this host.
+
+\item \verb|ttl N|
+
+--- set a fixed TTL \verb|N| on tunneled packets.
+	\verb|N| is a number in the range 1--255. 0 is a special value
+	meaning that packets inherit the TTL value. 
+		The default value is: \verb|inherit|.
+
+\item \verb|tos T| or \verb|dsfield T|
+
+--- set a fixed TOS \verb|T| on tunneled packets.
+		The default value is: \verb|inherit|.
+
+
+
+\item \verb|dev NAME| 
+
+--- bind the tunnel to the device \verb|NAME| so that
+	tunneled packets will only be routed via this device and will
+	not be able to escape to another device when the route to endpoint changes.
+
+\item \verb|nopmtudisc|
+
+--- disable Path MTU Discovery on this tunnel.
+	It is enabled by default. Note that a fixed ttl is incompatible
+	with this option: tunnelling with a fixed ttl always makes pmtu discovery.
+
+\item \verb|key K|, \verb|ikey K|, \verb|okey K|
+
+--- (only GRE tunnels) use keyed GRE with key \verb|K|. \verb|K| is
+	either a number or an IP address-like dotted quad.
+   The \verb|key| parameter sets the key to use in both directions.
+   The \verb|ikey| and \verb|okey| parameters set different keys for input and output.
+   
+
+\item \verb|csum|, \verb|icsum|, \verb|ocsum|
+
+--- (only GRE tunnels) generate/require checksums for tunneled packets.
+   The \verb|ocsum| flag calculates checksums for outgoing packets.
+   The \verb|icsum| flag requires that all input packets have the correct
+   checksum. The \verb|csum| flag is equivalent to the combination
+  ``\verb|icsum| \verb|ocsum|''.
+
+\item \verb|seq|, \verb|iseq|, \verb|oseq|
+
+--- (only GRE tunnels) serialize packets.
+   The \verb|oseq| flag enables sequencing of outgoing packets.
+   The \verb|iseq| flag requires that all input packets are serialized.
+   The \verb|seq| flag is equivalent to the combination ``\verb|iseq| \verb|oseq|''.
+
+\begin{NB}
+ I think this option does not
+	work. At least, I did not test it, did not debug it and
+	do not even understand how it is supposed to work or for what
+	purpose Cisco planned to use it. Do not use it.
+\end{NB}
+
+
+\end{itemize}
+
+\paragraph{Example:} Create a pointopoint IPv6 tunnel with maximal TTL of 32.
+\begin{verbatim}
+netadm@amber:~ # ip tunl add Cisco mode sit remote 192.31.7.104 \
+    local 192.203.80.142 ttl 32 
+\end{verbatim}
+
+\subsection{{\tt ip tunnel show} --- list tunnels}
+
+\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|.
+
+
+\paragraph{Arguments:} None.
+
+\paragraph{Output format:}
+\begin{verbatim}
+kuznet@amber:~ $ ip tunl ls Cisco
+Cisco: ipv6/ip  remote 192.31.7.104  local 192.203.80.142  ttl 32 
+kuznet@amber:~ $ 
+\end{verbatim}
+The line starts with the tunnel device name followed by a colon.
+Then the tunnel mode follows. The parameters of the tunnel are listed
+with the same keywords that were used when creating the tunnel.
+
+\paragraph{Statistics:}
+
+\begin{verbatim}
+kuznet@amber:~ $ ip -s tunl ls Cisco
+Cisco: ipv6/ip  remote 192.31.7.104  local 192.203.80.142  ttl 32 
+RX: Packets    Bytes        Errors CsumErrs OutOfSeq Mcasts
+    12566      1707516      0      0        0        0       
+TX: Packets    Bytes        Errors DeadLoop NoRoute  NoBufs
+    13445      1879677      0      0        0        0     
+kuznet@amber:~ $ 
+\end{verbatim}
+Essentially, these numbers are the same as the numbers
+printed with {\tt ip -s link show}
+(sec.\ref{IP-LINK-SHOW}, p.\pageref{IP-LINK-SHOW}) but the tags are different
+to reflect that they are tunnel specific.
+\begin{itemize}
+\item \verb|CsumErrs| --- the total number of packets dropped
+because of checksum failures for a GRE tunnel with checksumming enabled.
+\item \verb|OutOfSeq| --- the total number of packets dropped
+because they arrived out of sequence for a GRE tunnel with
+serialization enabled.
+\item \verb|Mcasts| --- the total number of multicast packets
+received on a broadcast GRE tunnel.
+\item \verb|DeadLoop| --- the total number of packets which were not
+transmitted because the tunnel is looped back to itself.
+\item \verb|NoRoute| --- the total number of packets which were not
+transmitted because there is no IP route to the remote endpoint.
+\item \verb|NoBufs| --- the total number of packets which were not
+transmitted because the kernel failed to allocate a buffer.
+\end{itemize}
+
+
+\section{{\tt ip monitor} and {\tt rtmon} --- state monitoring}
+\label{IP-MONITOR}
+
+The \verb|ip| utility can monitor the state of devices, addresses
+and routes continuously. This option has a slightly different format.
+Namely,
+the \verb|monitor| command is the first in the command line and then
+the object list follows:
+\begin{verbatim}
+  ip monitor [ file FILE ] [ all | OBJECT-LIST ] [ label ]
+\end{verbatim}
+\verb|OBJECT-LIST| is the list of object types that we want to
+monitor.  It may contain \verb|link|, \verb|address| and \verb|route|.
+Specifying \verb|label| indicates that output lines should be labelled
+with the type of object being printed --- this happens by default if
+\verb|all| is specified.  If no \verb|file| argument is given,
+\verb|ip| opens RTNETLINK, listens on it and dumps state changes in
+the format described in previous sections.
+
+If a file name is given, it does not listen on RTNETLINK,
+but opens the file containing RTNETLINK messages saved in binary format
+and dumps them. Such a history file can be generated with the
+\verb|rtmon| utility. This utility has a command line syntax similar to
+\verb|ip monitor|.
+Ideally, \verb|rtmon| should be started before
+the first network configuration command is issued. F.e.\ if
+you insert:
+\begin{verbatim}
+  rtmon file /var/log/rtmon.log
+\end{verbatim}
+in a startup script, you will be able to view the full history
+later.
+
+Certainly, it is possible to start \verb|rtmon| at any time.
+It prepends the history with the state snapshot dumped at the moment
+of starting.
+
+
+\section{Route realms and policy propagation, {\tt rtacct}}
+\label{RT-REALMS}
+
+On routers using OSPF ASE or, especially, the BGP protocol, routing
+tables may be huge. If we want to classify or to account for the packets
+per route, we will have to keep lots of information. Even worse, if we
+want to distinguish the packets not only by their destination, but
+also by their source, the task gets quadratic complexity and its solution
+is physically impossible.
+
+One approach to propagating the policy from routing protocols
+to the forwarding engine has been proposed in~\cite{IOS-BGP-PP}.
+Essentially, Cisco Policy Propagation via BGP is based on the fact
+that dedicated routers all have the RIB (Routing Information Base)
+close to the forwarding engine, so policy routing rules can
+check all the route attributes, including ASPATH information
+and community strings.
+
+The Linux architecture, splitting the RIB (maintained by a user level
+daemon) and the kernel based FIB (Forwarding Information Base),
+does not allow such a simple approach.
+
+It is to our fortune because there is another solution
+which allows even more flexible policy and richer semantics.
+
+Namely, routes can be clustered together in user space, based on their
+attributes.  F.e.\ a BGP router knows route ASPATH, its community;
+an OSPF router knows the route tag or its area. The administrator, when adding
+routes manually, also knows their nature. Providing that the number of such
+aggregates (we call them {\em realms\/}) is low, the task of full
+classification both by source and destination becomes quite manageable.
+
+So each route may be assigned to a realm. It is assumed that
+this identification is made by a routing daemon, but static routes
+can also be handled manually with \verb|ip route| (see sec.\ref{IP-ROUTE},
+p.\pageref{IP-ROUTE}).
+\begin{NB}
+  There is a patch to \verb|gated|, allowing classification of routes
+  to realms with all the set of policy rules implemented in \verb|gated|:
+  by prefix, by ASPATH, by origin, by tag etc.
+\end{NB}
+
+To facilitate the construction (f.e.\ in case the routing
+daemon is not aware of realms), missing realms may be completed
+with routing policy rules, see sec.~\ref{IP-RULE}, p.\pageref{IP-RULE}.
+
+For each packet the kernel calculates a tuple of realms: source realm
+and destination realm, using the following algorithm:
+
+\begin{enumerate}
+\item If the route has a realm, the destination realm of the packet is set to it.
+\item If the rule has a source realm, the source realm of the packet is set to it.
+If the destination realm was not inherited from the route and the rule has a destination realm,
+it is also set.
+\item If at least one of the realms is still unknown, the kernel finds
+the reversed route to the source of the packet.
+\item If the source realm is still unknown, get it from the reversed route.
+\item If one of the realms is still unknown, swap the realms of reversed
+routes and apply step 2 again.
+\end{enumerate}
+
+After this procedure is completed we know what realm the packet
+arrived from and the realm where it is going to propagate to.
+If some of the realms are unknown, they are initialized to zero
+(or realm \verb|unknown|).
+
+The main application of realms is the TC \verb|route| classifier~\cite{TC-CREF},
+where they are used to help assign packets to traffic classes,
+to account, police and schedule them according to this
+classification.
+
+A much simpler but still very useful application is incoming packet
+accounting by realms. The kernel gathers a packet statistics summary
+which can be viewed with the \verb|rtacct| utility.
+\begin{verbatim}
+kuznet@amber:~ $ rtacct russia
+Realm      BytesTo    PktsTo     BytesFrom  PktsFrom   
+russia     20576778   169176     47080168   153805     
+kuznet@amber:~ $
+\end{verbatim}
+This shows that this router received 153805 packets from
+the realm \verb|russia| and forwarded 169176 packets to \verb|russia|.
+The realm \verb|russia| consists of routes with ASPATHs not leaving
+Russia.
+
+Note that locally originating packets are not accounted here,
+\verb|rtacct| shows incoming packets only. Using the \verb|route|
+classifier (see~\cite{TC-CREF}) you can get even more detailed
+accounting information about outgoing packets, optionally
+summarizing traffic not only by source or destination, but
+by any pair of source and destination realms.
+
+
+\begin{thebibliography}{99}
+\addcontentsline{toc}{section}{References}
+\bibitem{RFC-NDISC} T.~Narten, E.~Nordmark, W.~Simpson.
+``Neighbor Discovery for IP Version 6 (IPv6)'', RFC-2461.
+
+\bibitem{RFC-ADDRCONF} S.~Thomson, T.~Narten.
+``IPv6 Stateless Address Autoconfiguration'', RFC-2462.
+
+\bibitem{RFC1812} F.~Baker.
+``Requirements for IP Version 4 Routers'', RFC-1812.
+
+\bibitem{RFC1122} R.~T.~Braden.
+``Requirements for Internet hosts --- communication layers'', RFC-1122.
+
+\bibitem{IOS} ``Cisco IOS Release 12.0 Network Protocols
+Command Reference, Part 1'' and
+``Cisco IOS Release 12.0 Quality of Service Solutions
+Configuration Guide: Configuring Policy-Based Routing'',\\
+http://www.cisco.com/univercd/cc/td/doc/product/software/ios120.
+
+\bibitem{IP-TUNNELS} A.~N.~Kuznetsov.
+``Tunnels over IP in Linux-2.2'', \\
+In: {\tt ftp://ftp.inr.ac.ru/ip-routing/iproute2-current.tar.gz}.
+
+\bibitem{TC-CREF} A.~N.~Kuznetsov. ``TC Command Reference'',\\
+In: {\tt ftp://ftp.inr.ac.ru/ip-routing/iproute2-current.tar.gz}.
+
+\bibitem{IOS-BGP-PP} ``Cisco IOS Release 12.0 Quality of Service Solutions
+Configuration Guide: Configuring QoS Policy Propagation via
+Border Gateway Protocol'',\\
+http://www.cisco.com/univercd/cc/td/doc/product/software/ios120.
+
+\bibitem{RFC-DHCP} R.~Droms.
+``Dynamic Host Configuration Protocol.'', RFC-2131
+
+\bibitem{RFC2414}  M.~Allman, S.~Floyd, C.~Partridge.
+``Increasing TCP's Initial Window'', RFC-2414.
+
+\end{thebibliography}
+
+
+
+
+\appendix
+\addcontentsline{toc}{section}{Appendix}
+
+\section{Source address selection}
+\label{ADDR-SEL}
+
+When a host creates an IP packet, it must select some source
+address. Correct source address selection is a critical procedure,
+because it gives the receiver the information needed to deliver a
+reply. If the source is selected incorrectly, in the best case,
+the backward path may appear different to the forward one which
+is harmful for performance. In the worst case, when the addresses
+are administratively scoped, the reply may be lost entirely.
+
+Linux-2.2 selects source addresses using the following algorithm:
+
+\begin{itemize}
+\item
+The application may select a source address explicitly with \verb|bind(2)|
+syscall or supplying it to \verb|sendmsg(2)| via the ancillary data object
+\verb|IP_PKTINFO|. In this case the kernel only checks the validity
+of the address and never tries to ``improve'' an incorrect user choice,
+generating an error instead.
+\begin{NB}
+ Never say ``Never''. The sysctl option \verb|ip_dynaddr| breaks
+ this axiom. It has been made deliberately with the purpose
+ of automatically reselecting the address on hosts with dynamic dial-out interfaces.
+ However, this hack {\em must not\/} be used on multihomed hosts
+ and especially on routers: it would break them.
+\end{NB}
+
+
+\item Otherwise, IP routing tables can contain an explicit source
+address hint for this destination. The hint is set with the \verb|src| parameter
+to the \verb|ip route| command, sec.\ref{IP-ROUTE}, p.\pageref{IP-ROUTE}.
+
+
+\item Otherwise, the kernel searches through the list of addresses
+attached to the interface through which the packets will be routed.
+The search strategies are different for IP and IPv6. Namely:
+
+\begin{itemize}
+\item IPv6 searches for the first valid, not deprecated address
+with the same scope as the destination.
+
+\item IP searches for the first valid address with a scope wider
+than the scope of the destination but it prefers addresses
+which fall to the same subnet as the nexthop of the route
+to the destination. Unlike IPv6, the scopes of IPv4 destinations
+are not encoded in their addresses but are supplied
+in routing tables instead (the \verb|scope| parameter to the \verb|ip route| command,
+sec.\ref{IP-ROUTE}, p.\pageref{IP-ROUTE}).
+
+\end{itemize}
+
+
+\item Otherwise, if the scope of the destination is \verb|link| or \verb|host|,
+the algorithm fails and returns a zero source address.
+
+\item Otherwise, all interfaces are scanned to search for an address
+with an appropriate scope. The loopback device \verb|lo| is always the first
+in the search list, so that if an address with global scope (not 127.0.0.1!)
+is configured on loopback, it is always preferred.
+
+\end{itemize}
+
+
+\section{Proxy ARP/NDISC}
+\label{PROXY-NEIGH}
+
+Routers may answer ARP/NDISC solicitations on behalf of other hosts.
+In Linux-2.2 proxy ARP on an interface may be enabled
+by setting the kernel \verb|sysctl| variable 
+\verb|/proc/sys/net/ipv4/conf/<dev>/proxy_arp| to 1. After this, the router
+starts to answer ARP requests on the interface \verb|<dev>|, provided
+the route to the requested destination does {\em not\/} go back via the same
+device.
+
+The variable \verb|/proc/sys/net/ipv4/conf/all/proxy_arp| enables proxy
+ARP on all the IP devices.
+
+However, this approach fails in the case of IPv6 because the router
+must join the solicited node multicast address to listen for the corresponding
+NDISC queries. It means that proxy NDISC is possible only on a per destination
+basis.
+
+Logically, proxy ARP/NDISC is not a kernel task. It can easily be implemented
+in user space. However, similar functionality was present in BSD kernels
+and in Linux-2.0, so we have to preserve it at least to the extent that
+is standardized in BSD.
+\begin{NB}
+  Linux-2.0 ARP had a feature called {\em subnet\/} proxy ARP.
+  It is replaced with the sysctl flag in Linux-2.2.
+\end{NB}
+
+
+The \verb|ip| utility provides a way to manage proxy ARP/NDISC
+with the \verb|ip neigh| command, namely:
+\begin{verbatim}
+  ip neigh add proxy ADDRESS [ dev NAME ]
+\end{verbatim}
+adds a new proxy ARP/NDISC record and
+\begin{verbatim}
+  ip neigh del proxy ADDRESS [ dev NAME ]
+\end{verbatim}
+deletes it.
+
+If the name of the device is not given, the router will answer solicitations
+for address \verb|ADDRESS| on all devices, otherwise it will only serve
+the device \verb|NAME|. Even if the proxy entry is created with
+\verb|ip neigh|, the router {\em will not\/} answer a query if the route
+to the destination goes back via the interface from which the solicitation
+was received.
+
+It is important to emphasize that proxy entries have {\em no\/}
+parameters other than these (IP/IPv6 address and optional device).
+Particularly, the entry does not store any link layer address.
+It always advertises the station address of the interface
+on which it sends advertisements (i.e. it's own station address).
+
+\section{Route NAT status}
+\label{ROUTE-NAT}
+
+NAT (or ``Network Address Translation'') remaps some parts
+of the IP address space into other ones. Linux-2.2 route NAT is supposed
+to be used to facilitate policy routing by rewriting addresses
+to other routing domains or to help while renumbering sites
+to another prefix.
+
+\paragraph{What it is not:}
+It is necessary to emphasize that {\em it is not supposed\/}
+to be used to compress address space or to split load.
+This is not missing functionality but a design principle.
+Route NAT is {\em stateless\/}. It does not hold any state
+about translated sessions. This means that it handles any number
+of sessions flawlessly. But it also means that it is {\em static\/}.
+It cannot detect the moment when the last TCP client stops
+using an address. For the same reason, it will not help to split
+load between several servers.
+\begin{NB}
+It is a pretty commonly held belief that it is useful to split load between
+several servers with NAT. This is a mistake. All you get from this
+is the requirement that the router keep the state of all the TCP connections
+going via it. Well, if the router is so powerful, run apache on it. 8)
+\end{NB}
+
+The second feature: it does not touch packet payload,
+does not try to ``improve'' broken protocols by looking
+through its data and mangling it. It mangles IP addresses,
+only IP addresses and nothing but IP addresses.
+This also, is not missing any functionality.
+
+To resume: if you need to compress address space or keep
+active FTP clients happy, your choice is not route NAT but masquerading,
+port forwarding, NAPT etc. 
+\begin{NB}
+By the way, you may also want to look at
+http://www.suse.com/\~mha/HyperNews/get/linux-ip-nat.html
+\end{NB}
+
+
+\paragraph{How it works.}
+Some part of the address space is reserved for dummy addresses
+which will look for all the world like some host addresses
+inside your network. No other hosts may use these addresses,
+however other routers may also be configured to translate them.
+\begin{NB}
+A great advantage of route NAT is that it may be used not
+only in stub networks but in environments with arbitrarily complicated
+structure. It does not firewall, it {\em forwards.}
+\end{NB}
+These addresses are selected by the \verb|ip route| command
+(sec.\ref{IP-ROUTE-ADD}, p.\pageref{IP-ROUTE-ADD}). F.e.\
+\begin{verbatim}
+  ip route add nat 192.203.80.144 via 193.233.7.83
+\end{verbatim}
+states that the single address 192.203.80.144 is a dummy NAT address.
+For all the world it looks like a host address inside our network.
+For neighbouring hosts and routers it looks like the local address
+of the translating router. The router answers ARP for it, advertises
+this address as routed via it, {\em et al\/}. When the router
+receives a packet destined for 192.203.80.144, it replaces 
+this address with 193.233.7.83 which is the address of some real
+host and forwards the packet. If you need to remap
+blocks of addresses, you may use a command like:
+\begin{verbatim}
+  ip route add nat 192.203.80.192/26 via 193.233.7.64
+\end{verbatim}
+This command will map a block of 63 addresses 192.203.80.192-255 to
+193.233.7.64-127.
+
+When an internal host (193.233.7.83 in the example above)
+sends something to the outer world and these packets are forwarded
+by our router, it should translate the source address 193.233.7.83
+into 192.203.80.144. This task is solved by setting a special
+policy rule (sec.\ref{IP-RULE-ADD}, p.\pageref{IP-RULE-ADD}):
+\begin{verbatim}
+  ip rule add prio 320 from 193.233.7.83 nat 192.203.80.144
+\end{verbatim}
+This rule says that the source address 193.233.7.83
+should be translated into 192.203.80.144 before forwarding.
+It is important that the address after the \verb|nat| keyword
+is some NAT address, declared by {\tt ip route add nat}.
+If it is just a random address the router will not map to it.
+\begin{NB}
+The exception is when the address is a local address of this
+router (or 0.0.0.0) and masquerading is configured in the linux-2.2
+kernel. In this case the router will masquerade the packets as this address.
+If 0.0.0.0 is selected, the result is equivalent to one
+obtained with firewalling rules. Otherwise, you have the way
+to order Linux to masquerade to this fixed address.
+NAT mechanism used in linux-2.4 is more flexible than
+masquerading, so that this feature has lost meaning and disabled.
+\end{NB}
+
+If the network has non-trivial internal structure, it is
+useful and even necessary to add rules disabling translation
+when a packet does not leave this network. Let us return to the
+example from sec.\ref{IP-RULE-SHOW} (p.\pageref{IP-RULE-SHOW}).
+\begin{verbatim}
+300:	from 193.233.7.83 to 193.233.7.0/24 lookup main
+310:	from 193.233.7.83 to 192.203.80.0/24 lookup main
+320:	from 193.233.7.83 lookup inr.ruhep map-to 192.203.80.144
+\end{verbatim}
+This block of rules causes normal forwarding when
+packets from 193.233.7.83 do not leave networks 193.233.7/24
+and 192.203.80/24. Also, if the \verb|inr.ruhep| table does not
+contain a route to the destination (which means that the routing
+domain owning addresses from 192.203.80/24 is dead), no translation
+will occur. Otherwise, the packets are translated.
+
+\paragraph{How to only translate selected ports:}
+If you only want to translate selected ports (f.e.\ http)
+and leave the rest intact, you may use \verb|ipchains|
+to \verb|fwmark| a class of packets.
+Suppose you did and all the packets from 193.233.7.83
+destined for port 80 are marked with marker 0x1234 in input fwchain.
+In this case you may replace rule \#320 with:
+\begin{verbatim}
+320:	from 193.233.7.83 fwmark 1234 lookup main map-to 192.203.80.144
+\end{verbatim}
+and translation will only be enabled for outgoing http requests.
+
+\section{Example: minimal host setup}
+\label{EXAMPLE-SETUP}
+
+The following script gives an example of a fault safe
+setup of IP (and IPv6, if it is compiled into the kernel)
+in the common case of a node attached to a single broadcast
+network. A more advanced script, which may be used both on multihomed
+hosts and on routers, is described in the following
+section.
+
+The utilities used in the script may be found in the
+directory ftp://ftp.inr.ac.ru/ip-routing/:
+\begin{enumerate}
+\item \verb|ip| --- package \verb|iproute2|.
+\item \verb|arping| --- package \verb|iputils|.
+\item \verb|rdisc| --- package \verb|iputils|.
+\end{enumerate}
+\begin{NB}
+It also refers to a DHCP client, \verb|dhcpcd|. I should refrain from
+recommending a good DHCP client to use. All that I can
+say is that ISC \verb|dhcp-2.0b1pl6| patched with the patch that
+can be found in the \verb|dhcp.bootp.rarp| subdirectory of
+the same ftp site {\em does\/} work,
+at least on Ethernet and Token Ring.
+\end{NB}
+
+\begin{verbatim}
+#! /bin/bash
+\end{verbatim}
+\begin{flushleft}
+\# {\bf Usage: \verb|ifone ADDRESS[/PREFIX-LENGTH] [DEVICE]|}\\
+\# {\bf Parameters:}\\
+\# \$1 --- Static IP address, optionally followed by prefix length.\\
+\# \$2 --- Device name. If it is missing, \verb|eth0| is asssumed.\\
+\# F.e. \verb|ifone 193.233.7.90|
+\end{flushleft}
+\begin{verbatim}
+dev=$2
+: ${dev:=eth0}
+ipaddr=
+\end{verbatim}
+\# Parse IP address, splitting prefix length.
+\begin{verbatim}
+if [ "$1" != "" ]; then
+  ipaddr=${1%/*}
+  if [ "$1" != "$ipaddr" ]; then
+    pfxlen=${1#*/}
+  fi
+  : ${pfxlen:=24}
+fi
+pfx="${ipaddr}/${pfxlen}"
+\end{verbatim}
+
+\begin{flushleft}
+\# {\bf Step 0} --- enable loopback.\\
+\#\\
+\# This step is necessary on any networked box before attempt\\
+\# to configure any other device.\\
+\end{flushleft}
+\begin{verbatim}
+ip link set up dev lo
+ip addr add 127.0.0.1/8 dev lo brd + scope host
+\end{verbatim}
+\begin{flushleft}
+\# IPv6 autoconfigure themself on loopback.\\
+\#\\
+\# If user gave loopback as device, we add the address as alias and exit.
+\end{flushleft}
+\begin{verbatim}
+if [ "$dev" = "lo" ]; then
+  if [ "$ipaddr" != "" -a  "$ipaddr" != "127.0.0.1" ]; then
+    ip address add $ipaddr dev $dev
+    exit $?
+  fi
+  exit 0
+fi
+\end{verbatim}
+
+\noindent\# {\bf Step 1} --- enable device \verb|$dev|
+
+\begin{verbatim}
+if ! ip link set up dev $dev ; then
+  echo "Cannot enable interface $dev. Aborting." 1>&2
+  exit 1
+fi
+\end{verbatim}
+\begin{flushleft}
+\# The interface is \verb|UP|. IPv6 started stateless autoconfiguration itself,\\
+\# and its configuration finishes here. However,\\
+\# IP still needs some static preconfigured address.
+\end{flushleft}
+\begin{verbatim}
+if [ "$ipaddr" = "" ]; then
+  echo "No address for $dev is configured, trying DHCP..." 1>&2
+  dhcpcd
+  exit $?
+fi
+\end{verbatim}
+
+\begin{flushleft}
+\# {\bf Step 2} --- IP Duplicate Address Detection~\cite{RFC-DHCP}.\\
+\# Send two probes and wait for result for 3 seconds.\\
+\# If the interface opens slower f.e.\ due to long media detection,\\
+\# you want to increase the timeout.\\
+\end{flushleft}
+\begin{verbatim}
+if ! arping -q -c 2 -w 3 -D -I $dev $ipaddr ; then
+  echo "Address $ipaddr is busy, trying DHCP..." 1>&2
+  dhcpcd
+  exit $?
+fi
+\end{verbatim}
+\begin{flushleft}
+\# OK, the address is unique, we may add it on the interface.\\
+\#\\
+\# {\bf Step 3} --- Configure the address on the interface.
+\end{flushleft}
+
+\begin{verbatim}
+if ! ip address add $pfx brd + dev $dev; then
+  echo "Failed to add $pfx on $dev, trying DHCP..." 1>&2
+  dhcpcd
+  exit $?
+fi
+\end{verbatim}
+
+\noindent\# {\bf Step 4} --- Announce our presence on the link.
+\begin{verbatim}
+arping -A -c 1 -I $dev $ipaddr
+noarp=$?
+( sleep 2;
+  arping -U -c 1 -I $dev $ipaddr ) >& /dev/null </dev/null &
+\end{verbatim}
+
+\begin{flushleft}
+\# {\bf Step 5} (optional) --- Add some control routes.\\
+\#\\
+\# 1. Prohibit link local multicast addresses.\\
+\# 2. Prohibit link local (alias, limited) broadcast.\\
+\# 3. Add default multicast route.
+\end{flushleft}
+\begin{verbatim}
+ip route add unreachable 224.0.0.0/24 
+ip route add unreachable 255.255.255.255
+if [ `ip link ls $dev | grep -c MULTICAST` -ge 1 ]; then
+  ip route add 224.0.0.0/4 dev $dev scope global
+fi
+\end{verbatim}
+
+\begin{flushleft}
+\# {\bf Step 6} --- Add fallback default route with huge metric.\\
+\# If a proxy ARP server is present on the interface, we will be\\
+\# able to talk to all the Internet without further configuration.\\
+\# It is not so cheap though and we still hope that this route\\
+\# will be overridden by more correct one by rdisc.\\
+\# Do not make this step if the device is not ARPable,\\
+\# because dead nexthop detection does not work on them.
+\end{flushleft}
+\begin{verbatim}
+if [ "$noarp" = "0" ]; then
+  ip ro add default dev $dev metric 30000 scope global
+fi
+\end{verbatim}
+
+\begin{flushleft}
+\# {\bf Step 7} --- Restart router discovery and exit.
+\end{flushleft}
+\begin{verbatim}
+killall -HUP rdisc || rdisc -fs
+exit 0
+\end{verbatim}
+
+
+\section{Example: {\protect\tt ifcfg} --- interface address management}
+\label{EXAMPLE-IFCFG}
+
+This is a simplistic script replacing one option of \verb|ifconfig|,
+namely, IP address management. It not only adds
+addresses, but also carries out Duplicate Address Detection~\cite{RFC-DHCP},
+sends unsolicited ARP to update the caches of other hosts sharing
+the interface, adds some control routes and restarts Router Discovery
+when it is necessary.
+
+I strongly recommend using it {\em instead\/} of \verb|ifconfig| both
+on hosts and on routers.
+
+\begin{verbatim}
+#! /bin/bash
+\end{verbatim}
+\begin{flushleft}
+\# {\bf Usage: \verb?ifcfg DEVICE[:ALIAS] [add|del] ADDRESS[/LENGTH] [PEER]?}\\
+\# {\bf Parameters:}\\
+\# ---Device name. It may have alias suffix, separated by colon.\\
+\# ---Command: add, delete or stop.\\
+\# ---IP address, optionally followed by prefix length.\\
+\# ---Optional peer address for pointopoint interfaces.\\
+\# F.e. \verb|ifcfg eth0 193.233.7.90/24|
+
+\noindent\# This function determines, whether it is router or host.\\
+\# It returns 0, if the host is apparently not router.
+\end{flushleft}
+\begin{verbatim}
+CheckForwarding () {
+  local sbase fwd
+  sbase=/proc/sys/net/ipv4/conf
+  fwd=0
+  if [ -d $sbase ]; then
+    for dir in $sbase/*/forwarding; do
+      fwd=$[$fwd + `cat $dir`]
+    done
+  else
+    fwd=2
+  fi
+  return $fwd
+}
+\end{verbatim}
+\begin{flushleft}
+\# This function restarts Router Discovery.\\
+\end{flushleft}
+\begin{verbatim}
+RestartRDISC () {
+  killall -HUP rdisc || rdisc -fs
+}
+\end{verbatim}
+\begin{flushleft}
+\# Calculate ABC "natural" mask length\\
+\# Arg: \$1 = dotquad address
+\end{flushleft}
+\begin{verbatim}
+ABCMaskLen () {
+  local class;
+  class=${1%%.*}
+  if [ $class -eq 0 -o $class -ge 224 ]; then return 0
+  elif [ $class -ge 192 ]; then return 24
+  elif [ $class -ge 128 ]; then return 16
+  else  return 8 ; fi
+}
+\end{verbatim}
+
+
+\begin{flushleft}
+\# {\bf MAIN()}\\
+\#\\
+\# Strip alias suffix separated by colon.
+\end{flushleft}
+\begin{verbatim}
+label="label $1"
+ldev=$1
+dev=${1%:*}
+if [ "$dev" = "" -o "$1" = "help" ]; then
+  echo "Usage: ifcfg DEV [[add|del [ADDR[/LEN]] [PEER] | stop]" 1>&2
+  echo "       add - add new address" 1>&2
+  echo "       del - delete address" 1>&2
+  echo "       stop - completely disable IP" 1>&2
+  exit 1
+fi
+shift
+
+CheckForwarding
+fwd=$?
+\end{verbatim}
+\begin{flushleft}
+\# Parse command. If it is ``stop'', flush and exit.
+\end{flushleft}
+\begin{verbatim}
+deleting=0
+case "$1" in
+add) shift ;;
+stop)
+  if [ "$ldev" != "$dev" ]; then
+    echo "Cannot stop alias $ldev" 1>&2
+    exit 1;
+  fi
+  ip -4 addr flush dev $dev $label || exit 1
+  if [ $fwd -eq 0 ]; then RestartRDISC; fi
+  exit 0 ;;
+del*)
+  deleting=1; shift ;;
+*)
+esac
+\end{verbatim}
+\begin{flushleft}
+\# Parse prefix, split prefix length, separated by slash.
+\end{flushleft}
+\begin{verbatim}
+ipaddr=
+pfxlen=
+if [ "$1" != "" ]; then
+  ipaddr=${1%/*}
+  if [ "$1" != "$ipaddr" ]; then
+    pfxlen=${1#*/}
+  fi
+  if [ "$ipaddr" = "" ]; then
+    echo "$1 is bad IP address." 1>&2
+    exit 1
+  fi
+fi
+shift
+\end{verbatim}
+\begin{flushleft}
+\# If peer address is present, prefix length is 32.\\
+\# Otherwise, if prefix length was not given, guess it.
+\end{flushleft}
+\begin{verbatim}
+peer=$1
+if [ "$peer" != "" ]; then
+  if [ "$pfxlen" != "" -a "$pfxlen" != "32" ]; then
+    echo "Peer address with non-trivial netmask." 1>&2
+    exit 1
+  fi
+  pfx="$ipaddr peer $peer"
+else
+  if [ "$pfxlen" = "" ]; then
+    ABCMaskLen $ipaddr
+    pfxlen=$?
+  fi
+  pfx="$ipaddr/$pfxlen"
+fi
+if [ "$ldev" = "$dev" -a "$ipaddr" != "" ]; then
+  label=
+fi
+\end{verbatim}
+\begin{flushleft}
+\# If deletion was requested, delete the address and restart RDISC
+\end{flushleft}
+\begin{verbatim}
+if [ $deleting -ne 0 ]; then
+  ip addr del $pfx dev $dev $label || exit 1
+  if [ $fwd -eq 0 ]; then RestartRDISC; fi
+  exit 0
+fi
+\end{verbatim}
+\begin{flushleft}
+\# Start interface initialization.\\
+\#\\
+\# {\bf Step 0} --- enable device \verb|$dev|
+\end{flushleft}
+\begin{verbatim}
+if ! ip link set up dev $dev ; then
+  echo "Error: cannot enable interface $dev." 1>&2
+  exit 1
+fi
+if [ "$ipaddr" = "" ]; then exit 0; fi
+\end{verbatim}
+\begin{flushleft}
+\# {\bf Step 1} --- IP Duplicate Address Detection~\cite{RFC-DHCP}.\\
+\# Send two probes and wait for result for 3 seconds.\\
+\# If the interface opens slower f.e.\ due to long media detection,\\
+\# you want to increase the timeout.\\
+\end{flushleft}
+\begin{verbatim}
+if ! arping -q -c 2 -w 3 -D -I $dev $ipaddr ; then
+  echo "Error: some host already uses address $ipaddr on $dev." 1>&2
+  exit 1
+fi
+\end{verbatim}
+\begin{flushleft}
+\# OK, the address is unique. We may add it to the interface.\\
+\#\\
+\# {\bf Step 2} --- Configure the address on the interface.
+\end{flushleft}
+\begin{verbatim}
+if ! ip address add $pfx brd + dev $dev $label; then
+  echo "Error: failed to add $pfx on $dev." 1>&2
+  exit 1
+fi
+\end{verbatim}
+\noindent\# {\bf Step 3} --- Announce our presence on the link
+\begin{verbatim}
+arping -q -A -c 1 -I $dev $ipaddr
+noarp=$?
+( sleep 2 ;
+  arping -q -U -c 1 -I $dev $ipaddr ) >& /dev/null </dev/null &
+\end{verbatim}
+\begin{flushleft}
+\# {\bf Step 4} (optional) --- Add some control routes.\\
+\#\\
+\# 1. Prohibit link local multicast addresses.\\
+\# 2. Prohibit link local (alias, limited) broadcast.\\
+\# 3. Add default multicast route.
+\end{flushleft}
+\begin{verbatim}
+ip route add unreachable 224.0.0.0/24 >& /dev/null 
+ip route add unreachable 255.255.255.255 >& /dev/null
+if [ `ip link ls $dev | grep -c MULTICAST` -ge 1 ]; then
+  ip route add 224.0.0.0/4 dev $dev scope global >& /dev/null
+fi
+\end{verbatim}
+\begin{flushleft}
+\# {\bf Step 5} --- Add fallback default route with huge metric.\\
+\# If a proxy ARP server is present on the interface, we will be\\
+\# able to talk to all the Internet without further configuration.\\
+\# Do not make this step on router or if the device is not ARPable.\\
+\# because dead nexthop detection does not work on them.
+\end{flushleft}
+\begin{verbatim}
+if [ $fwd -eq 0 ]; then
+  if [ $noarp -eq 0 ]; then
+    ip ro append default dev $dev metric 30000 scope global
+  elif [ "$peer" != "" ]; then
+    if ping -q -c 2 -w 4 $peer ; then
+      ip ro append default via $peer dev $dev metric 30001
+    fi
+  fi
+  RestartRDISC
+fi
+
+exit 0
+\end{verbatim}
+\begin{flushleft}
+\# End of {\bf MAIN()}
+\end{flushleft}
+
+
+\end{document}
diff --git a/doc/ip-tunnels.tex b/doc/ip-tunnels.tex
new file mode 100644
index 0000000..0a8c930
--- /dev/null
+++ b/doc/ip-tunnels.tex
@@ -0,0 +1,469 @@
+\documentstyle[12pt,twoside]{article}
+\def\TITLE{Tunnels over IP}
+\input preamble
+\begin{center}
+\Large\bf Tunnels over IP in Linux-2.2
+\end{center}
+
+
+\begin{center}
+{ \large Alexey~N.~Kuznetsov } \\
+\em Institute for Nuclear Research, Moscow \\
+\verb|kuznet@ms2.inr.ac.ru| \\
+\rm March 17, 1999
+\end{center}
+
+\vspace{5mm}
+
+\tableofcontents
+
+
+\section{Instead of introduction: micro-FAQ.}
+
+\begin{itemize}
+
+\item
+Q: In linux-2.0.36 I used:
+\begin{verbatim} 
+    ifconfig tunl1 10.0.0.1 pointopoint 193.233.7.65
+\end{verbatim} 
+to create tunnel. It does not work in 2.2.0!
+
+A: You are right, it does not work. The command written above is split to two commands.
+\begin{verbatim}
+    ip tunnel add MY-TUNNEL mode ipip remote 193.233.7.65
+\end{verbatim} 
+will create tunnel device with name \verb|MY-TUNNEL|. Now you may configure
+it with:
+\begin{verbatim} 
+    ifconfig MY-TUNNEL 10.0.0.1
+\end{verbatim} 
+Certainly, if you prefer name \verb|tunl1| to \verb|MY-TUNNEL|,
+you still may use it.
+
+\item
+Q: In linux-2.0.36 I used:
+\begin{verbatim} 
+    ifconfig tunl0 10.0.0.1
+    route add -net 10.0.0.0 gw 193.233.7.65 dev tunl0
+\end{verbatim} 
+to tunnel net 10.0.0.0 via router 193.233.7.65. It does not
+work in 2.2.0! Moreover, \verb|route| prints a funny error sort of
+``network unreachable'' and after this I found a strange direct route
+to 10.0.0.0 via \verb|tunl0| in routing table.
+
+A: Yes, in 2.2 the rule that {\em normal} gateway must reside on directly
+connected network has not any exceptions. You may tell kernel, that
+this particular route is {\em abnormal}:
+\begin{verbatim} 
+  ifconfig tunl0 10.0.0.1 netmask 255.255.255.255
+  ip route add 10.0.0.0/8 via 193.233.7.65 dev tunl0 onlink
+\end{verbatim}
+Note keyword \verb|onlink|, it is the magic key that orders kernel
+not to check for consistency of gateway address.
+Probably, after this explanation you have already guessed another method
+to cheat kernel:
+\begin{verbatim} 
+  ifconfig tunl0 10.0.0.1 netmask 255.255.255.255
+  route add -host 193.233.7.65 dev tunl0
+  route add -net 10.0.0.0 netmask 255.0.0.0 gw 193.233.7.65
+  route del -host 193.233.7.65 dev tunl0
+\end{verbatim}
+Well, if you like such tricks, nobody may prohibit you to use them.
+Only do not forget
+that between \verb|route add| and \verb|route del| host 193.233.7.65 is
+unreachable.
+
+\item
+Q: In 2.0.36 I used to load \verb|tunnel| device module and \verb|ipip| module.
+I cannot find any \verb|tunnel| in 2.2!
+
+A: Linux-2.2 has single module \verb|ipip| for both directions of tunneling
+and for all IPIP tunnel devices.
+
+\item
+Q: \verb|traceroute| does not work over tunnel! Well, stop... It works,
+     only skips some number of hops.
+
+A: Yes. By default tunnel driver copies \verb|ttl| value from
+inner packet to outer one. It means that path traversed by tunneled
+packets to another endpoint is not hidden. If you dislike this, or if you
+are going to use some routing protocol expecting that packets
+with ttl 1 will reach peering host (f.e.\ RIP, OSPF or EBGP)
+and you are not afraid of
+tunnel loops, you may append option \verb|ttl 64|, when creating tunnel
+with \verb|ip tunnel add|.
+
+\item
+Q: ... Well, list of things, which 2.0 was able to do finishes.
+
+\end{itemize}
+
+\paragraph{Summary of differences between 2.2 and 2.0.}
+
+\begin{itemize}
+
+\item {\bf In 2.0} you could compile tunnel device into kernel
+	and got set of 4 devices \verb|tunl0| ... \verb|tunl3| or,
+	alternatively, compile it as module and load new module
+	for each new tunnel. Also, module \verb|ipip| was necessary
+	to receive tunneled packets.
+
+      {\bf 2.2} has {\em one\/} module \verb|ipip|. Loading it you get base
+	tunnel device \verb|tunl0| and another tunnels may be created with command
+	\verb|ip tunnel add|. These new devices may have arbitrary names.
+
+
+\item {\bf In 2.0} you set remote tunnel endpoint address with
+	the command \verb|ifconfig| ... \verb|pointopoint A|.
+
+	{\bf In 2.2} this command has the same semantics on all
+	the interfaces, namely it sets not tunnel endpoint,
+	but address of peering host, which is directly reachable
+	via this tunnel,
+	rather than via Internet. Actual tunnel endpoint address \verb|A|
+	should be set with \verb|ip tunnel add ... remote A|.
+
+\item {\bf In 2.0} you create tunnel routes with the command:
+\begin{verbatim}
+    route add -net 10.0.0.0 gw A dev tunl0
+\end{verbatim}
+
+	{\bf 2.2} interprets this command equally for all device
+	kinds and gateway is required to be directly reachable via this tunnel,
+	rather than via Internet. You still may use \verb|ip route add ... onlink|
+	to override this behaviour.
+
+\end{itemize}
+
+
+\section{Tunnel setup: basics}
+
+Standard Linux-2.2 kernel supports three flavor of tunnels,
+listed in the following table:
+\vspace{2mm}
+
+\begin{tabular}{lll}
+\vrule depth 0.8ex width 0pt\relax
+Mode & Description  & Base device \\
+ipip & IP over IP & tunl0 \\
+sit & IPv6 over IP & sit0 \\
+gre & ANY over GRE over IP & gre0
+\end{tabular}
+
+\vspace{2mm}
+
+\noindent All the kinds of tunnels are created with one command:
+\begin{verbatim}
+  ip tunnel add <NAME> mode <MODE> [ local <S> ] [ remote <D> ]
+\end{verbatim}
+
+This command creates new tunnel device with name \verb|<NAME>|.
+The \verb|<NAME>| is an arbitrary string. Particularly,
+it may be even \verb|eth0|. The rest of parameters set
+different tunnel characteristics.
+
+\begin{itemize}
+
+\item
+\verb|mode <MODE>| sets tunnel mode. Three modes are available now
+	\verb|ipip|, \verb|sit| and \verb|gre|.
+
+\item
+\verb|remote <D>| sets remote endpoint of the tunnel to IP
+	address \verb|<D>|.
+\item
+\verb|local <S>| sets fixed local address for tunneled
+	packets. It must be an address on another interface of this host.
+
+\end{itemize}
+
+\let\thefootnote\oldthefootnote
+
+Both \verb|remote| and \verb|local| may be omitted. In this case we
+say that they are zero or wildcard. Two tunnels of one mode cannot
+have the same \verb|remote| and \verb|local|. Particularly it means
+that base device or fallback tunnel cannot be replicated.\footnote{
+This restriction is relaxed for keyed GRE tunnels.}
+
+Tunnels are divided to two classes: {\bf pointopoint} tunnels, which
+have some not wildcard \verb|remote| address and deliver all the packets
+to this destination, and {\bf NBMA} (i.e. Non-Broadcast Multi-Access) tunnels,
+which have no \verb|remote|. Particularly, base devices (f.e.\ \verb|tunl0|)
+are NBMA, because they have neither \verb|remote| nor
+\verb|local| addresses.
+
+
+After tunnel device is created you should configure it as you did
+it with another devices. Certainly, the configuration of tunnels has
+some features related to the fact that they work over existing Internet
+routing infrastructure and simultaneously create new virtual links,
+which changes this infrastructure. The danger that not enough careful
+tunnel setup will result in formation of tunnel loops,
+collapse of routing or flooding network with exponentially
+growing number of tunneled fragments is very real.
+
+
+Protocol setup on pointopoint tunnels does not differ of configuration
+of another devices. You should set a protocol address with \verb|ifconfig|
+and add routes with \verb|route| utility.
+
+NBMA tunnels are different. To route something via NBMA tunnel
+you have to explain to driver, where it should deliver packets to.
+The only way to make it is to create special routes with gateway
+address pointing to desired endpoint. F.e.\ 
+\begin{verbatim}
+    ip route add 10.0.0.0/24 via <A> dev tunl0 onlink
+\end{verbatim}
+It is important to use option \verb|onlink|, otherwise
+kernel will refuse request to create route via gateway not directly
+reachable over device \verb|tunl0|. With IPv6 the situation is much simpler:
+when you start device \verb|sit0|, it automatically configures itself
+with all IPv4 addresses mapped to IPv6 space, so that all IPv4
+Internet is {\em really reachable} via \verb|sit0|! Excellent, the command
+\begin{verbatim}
+    ip route add 3FFE::/16 via ::193.233.7.65 dev sit0
+\end{verbatim}
+will route \verb|3FFE::/16| via \verb|sit0|, sending all the packets
+destined to this prefix to 193.233.7.65.
+
+\section{Tunnel setup: options}
+
+Command \verb|ip tunnel add| has several additional options.
+\begin{itemize}
+
+\item \verb|ttl N| --- set fixed TTL \verb|N| on tunneled packets.
+	\verb|N| is number in the range 1--255. 0 is special value,
+	meaning that packets inherit TTL value. 
+		Default value is: \verb|inherit|.
+
+\item \verb|tos T| --- set fixed tos \verb|T| on tunneled packets.
+		Default value is: \verb|inherit|.
+
+\item \verb|dev DEV| --- bind tunnel to device \verb|DEV|, so that
+	tunneled packets will be routed only via this device and will
+	not be able to escape to another device, when route to endpoint changes.
+
+\item \verb|nopmtudisc| --- disable Path MTU Discovery on this tunnel.
+	It is enabled by default. Note that fixed ttl is incompatible
+	with this option: tunnels with fixed ttl always make pmtu discovery.
+
+\end{itemize}
+
+\verb|ipip| and \verb|sit| tunnels have no more options. \verb|gre|
+tunnels are more complicated:
+
+\begin{itemize}
+
+\item \verb|key K| --- use keyed GRE with key \verb|K|. \verb|K| is
+	either number or IP address-like dotted quad.
+
+\item \verb|csum| --- checksum tunneled packets.
+
+\item \verb|seq| --- serialize packets.
+\begin{NB}
+	I think this option does not
+	work. At least, I did not test it, did not debug it and
+	even do not understand,	how it is supposed to work and for what
+	purpose Cisco planned to use it.
+\end{NB}
+
+\end{itemize}
+
+
+Actually, these GRE options can be set separately for input and
+output directions by prefixing corresponding keywords with letter
+\verb|i| or \verb|o|. F.e.\ \verb|icsum| orders to accept only
+packets with correct checksum and \verb|ocsum| means, that
+our host will calculate and send checksum.
+
+Command \verb|ip tunnel add| is not the only operation,
+which can be made with tunnels. Certainly, you may get short help page
+with:
+\begin{verbatim}
+    ip tunnel help
+\end{verbatim}
+
+Besides that, you may view list of installed tunnels with the help of command:
+\begin{verbatim}
+    ip tunnel ls
+\end{verbatim}
+Also you may look at statistics:
+\begin{verbatim}
+    ip -s tunnel ls Cisco
+\end{verbatim}
+where \verb|Cisco| is name of tunnel device. Command
+\begin{verbatim}
+    ip tunnel del Cisco
+\end{verbatim}
+destroys tunnel \verb|Cisco|. And, finally,
+\begin{verbatim}
+    ip tunnel change Cisco mode sit local ME remote HE ttl 32
+\end{verbatim}
+changes its parameters.
+
+\section{Differences 2.2 and 2.0 tunnels revisited.}
+
+Now we can discuss more subtle differences between tunneling in 2.0
+and 2.2.
+
+\begin{itemize}
+
+\item In 2.0 all tunneled packets were received promiscuously
+as soon as you loaded module \verb|ipip|. 2.2 tries to select the best
+tunnel device and packet looks as received on this. F.e.\ if host
+received \verb|ipip| packet from host \verb|D| destined to our
+local address \verb|S|, kernel searches for matching tunnels
+in order:
+
+\begin{tabular}{ll}
+1 & \verb|remote| is \verb|D| and \verb|local| is \verb|S| \\
+2 & \verb|remote| is \verb|D| and \verb|local| is wildcard \\
+3 & \verb|remote| is wildcard and \verb|local| is \verb|S| \\
+4 & \verb|tunl0|
+\end{tabular}
+
+If tunnel exists, but it is not in \verb|UP| state, the tunnel is ignored.
+Note, that if \verb|tunl0| is \verb|UP| it receives all the IPIP packets,
+not acknowledged by more specific tunnels.
+Be careful, it means that without carefully installed firewall rules
+anyone on the Internet may inject to your network any packets with
+source addresses indistinguishable from local ones. It is not so bad idea
+to design tunnels in the way enforcing maximal route symmetry
+and to enable reversed path filter (\verb|rp_filter| sysctl option) on
+tunnel devices.
+
+\item In 2.2 you can monitor and debug tunnels with \verb|tcpdump|.
+F.e.\ \verb|tcpdump| \verb|-i Cisco| \verb|-nvv| will dump packets,
+which kernel output, via tunnel \verb|Cisco| and the packets received on it
+from kernel viewpoint.
+
+\end{itemize}
+
+
+\section{Linux and Cisco IOS tunnels.}
+
+Among another tunnels Cisco IOS supports IPIP and GRE.
+Essentially, Cisco setup is subset of options, available for Linux.
+Let us consider the simplest example:
+
+\begin{verbatim}
+interface Tunnel0
+ tunnel mode gre ip
+ tunnel source 10.10.14.1
+ tunnel destination 10.10.13.2
+\end{verbatim}
+
+
+This command set translates to:
+
+\begin{verbatim}
+    ip tunnel add Tunnel0 \
+        mode gre \
+        local 10.10.14.1 \
+        remote 10.10.13.2
+\end{verbatim}
+
+Any questions? No questions.
+
+\section{Interaction IPIP tunnels and DVMRP.}
+
+DVMRP exploits IPIP tunnels to route multicasts via Internet.
+\verb|mrouted| creates
+IPIP tunnels listed in its configuration file automatically.
+From kernel and user viewpoints there are no differences between
+tunnels, created in this way, and tunnels created by \verb|ip tunnel|.
+I.e.\ if \verb|mrouted| created some tunnel, it may be used to
+route unicast packets, provided appropriate routes are added.
+And vice versa, if administrator has already created a tunnel,
+it will be reused by \verb|mrouted|, if it requests DVMRP
+tunnel with the same local and remote addresses.
+
+Do not wonder, if your manually configured tunnel is
+destroyed, when mrouted exits.
+
+
+\section{Broadcast GRE ``tunnels''.}
+
+It is possible to set \verb|remote| for GRE tunnel to a multicast
+address. Such tunnel becomes {\bf broadcast} tunnel (though word
+tunnel is not quite appropriate in this case, it is rather virtual network).
+\begin{verbatim}
+  ip tunnel add Universe local 193.233.7.65 \
+                         remote 224.66.66.66 ttl 16
+  ip addr add 10.0.0.1/16 dev Universe
+  ip link set Universe up
+\end{verbatim}
+This tunnel is true broadcast network and broadcast packets are
+sent to multicast group 224.66.66.66. By default such tunnel starts
+to resolve both IP and IPv6 addresses via ARP/NDISC, so that
+if multicast routing is supported in surrounding network, all GRE nodes
+will find one another automatically and will form virtual Ethernet-like
+broadcast network. If multicast routing does not work, it is unpleasant
+but not fatal flaw. The tunnel becomes NBMA rather than broadcast network.
+You may disable dynamic ARPing by:
+\begin{verbatim}
+  echo 0 > /proc/sys/net/ipv4/neigh/Universe/mcast_solicit
+\end{verbatim}
+and to add required information to ARP tables manually:
+\begin{verbatim}
+  ip neigh add 10.0.0.2 lladdr 128.6.190.2 dev Universe nud permanent
+\end{verbatim}
+In this case packets sent to 10.0.0.2 will be encapsulated in GRE
+and sent to 128.6.190.2. It is possible to facilitate address resolution
+using methods typical for another NBMA networks f.e.\ to start user
+level \verb|arpd| daemon, which will maintain database of hosts attached
+to GRE virtual network or ask for information
+dedicated ARP or NHRP server.
+
+
+Actually, such setup is the most natural for tunneling,
+it is really flexible, scalable and easily managable, so that
+it is strongly recommended to be used with GRE tunnels instead of ugly
+hack with NBMA mode and \verb|onlink| modifier. Unfortunately,
+by historical reasons broadcast mode is not supported by IPIP tunnels,
+but this probably will change in future.
+
+
+
+\section{Traffic control issues.}
+
+Tunnels are devices, hence all the power of Linux traffic control
+applies to them. The simplest (and the most useful in practice)
+example is limiting tunnel bandwidth. The following command:
+\begin{verbatim}
+    tc qdisc add dev tunl0 root tbf \
+        rate 128Kbit burst 4K limit 10K
+\end{verbatim}
+will limit tunneled traffic to 128Kbit with maximal burst size of 4K
+and queuing not more than 10K.
+
+However, you should remember, that tunnels are {\em virtual} devices
+implemented in software and true queue management is impossible for them
+just because they have no queues. Instead, it is better to create classes
+on real physical interfaces and to map tunneled packets to them.
+In general case of dynamic routing you should create such classes
+on all outgoing interfaces, or, alternatively,
+to use option \verb|dev DEV| to bind tunnel to a fixed physical device.
+In the last case packets will be routed only via specified device
+and you need to setup corresponding classes only on it.
+Though you have to pay for this convenience,
+if routing will change, your tunnel will fail.
+
+Suppose that CBQ class \verb|1:ABC| has been created on device \verb|eth0| 
+specially for tunnel \verb|Cisco| with endpoints \verb|S| and \verb|D|.
+Now you can select IPIP packets with addresses \verb|S| and \verb|D|
+with some classifier and map them to class \verb|1:ABC|. F.e.\ 
+it is easy to make with \verb|rsvp| classifier:
+\begin{verbatim}
+    tc filter add dev eth0 pref 100 proto ip rsvp \
+        session D ipproto ipip filter S \
+        classid 1:ABC
+\end{verbatim}
+
+If you want to make more detailed classification of sub-flows
+transmitted via tunnel, you can build CBQ subtree,
+rooted at \verb|1:ABC| and attach to subroot set of rules parsing
+IPIP packets more deeply.
+
+\end{document}
diff --git a/doc/nstat.sgml b/doc/nstat.sgml
new file mode 100644
index 0000000..48cacc6
--- /dev/null
+++ b/doc/nstat.sgml
@@ -0,0 +1,110 @@
+<!doctype linuxdoc system>
+
+<article>
+
+<title>NSTAT, IFSTAT and RTACCT Utilities
+<author>Alexey Kuznetsov, <tt/kuznet@ms2.inr.ac.ru/
+<date>some_negative_number, 20 Sep 2001
+<abstract>
+<tt/nstat/, <tt/ifstat/ and <tt/rtacct/ are simple tools helping
+to monitor kernel snmp counters and network interface statistics.
+</abstract>
+
+<p> These utilities are very similar, so that I describe
+them simultaneously, using name <tt/Xstat/ in the places which apply
+to all of them.
+
+<p>The format of the command is:
+
+<tscreen><verb>
+       Xstat [ OPTIONS ] [ PATTERN [ PATTERN ... ] ]
+</verb></tscreen>
+
+<p>
+<tt/PATTERN/ is shell style pattern, selecting identifier
+of SNMP variables or interfaces to show. Variable is displayed
+if one of patterns matches its name. If no patterns are given,
+<tt/Xstat/ assumes that user wants to see all the variables.  
+
+<p> <tt/OPTIONS/ is list of single letter options, using common unix
+conventions.
+
+<itemize>
+<item><tt/-h/  - show help page
+<item><tt/-?/  - the same, of course
+<item><tt/-v/, <tt/-V/  - print version of <tt/Xstat/ and exit
+<item><tt/-z/ - dump zero counters too. By default they are not shown.
+<item><tt/-a/ - dump absolute values of counters. By default <tt/Xstat/
+                calculates increments since the previous use.
+<item><tt/-s/ - do not update history, so that the next time you will
+                see counters including values accumulated to the moment
+                of this measurement too.
+<item><tt/-n/ - do not display anything, only update history.
+<item><tt/-r/ - reset history.
+<item><tt/-d INTERVAL/ - <tt/Xstat/ is run in daemon mode collecting
+                statistics. <tt/INTERVAL/ is interval between measurements
+                in seconds.
+<item><tt/-t INTERVAL/ - time interval to average rates. Default value
+                is 60 seconds. 
+<item><tt/-e/ - display extended information about errors (<tt/ifstat/ only).
+</itemize>
+
+<p>
+History is just dump saved in file <tt>/tmp/.Xstat.uUID</tt>
+or in file given by environment variables <tt/NSTAT_HISTORY/,
+<tt/IFSTAT_HISTORY/ and <tt/RTACCT_HISTORY/.
+Each time when you use <tt/Xstat/ values there are updated.
+If you use patterns, only the values which you _really_ see
+are updated. If you want to skip an unintersting period,
+use option <tt/-n/, or just output to <tt>/dev/null</tt>.
+
+<p>
+<tt/Xstat/ understands when history is invalidated by system reboot
+or source of information switched between different instances
+of daemonic <tt/Xstat/ and kernel SNMP tables and does not
+use invalid history.
+
+<p> Beware, <tt/Xstat/ will not produce sane output,
+when many processes use it simultaneously. If several processes
+under single user need this utility they should use environment
+variables to put their history in safe places
+or to use it with options <tt/-a -s/.
+
+<p>
+Well, that's all. The utility is very simple, but nevertheless
+very handy.
+
+<p> <bf/Output of XSTAT/
+<p> The first line of output is <tt/#/ followed by identifier
+of source of information, it may be word <tt/kernel/, when <tt/Xstat/
+gets information from kernel or some dotted decimal number followed
+by parameters, when it obtains information from running <tt/Xstat/ daemon.
+
+<p>In the case of <tt/nstat/ the rest of output consists of three columns:
+SNMP MIB identifier,
+its value (or increment since previous measurement) and average
+rate of increase of the counter per second. <tt/ifstat/ outputs
+interface name followed by pairs of counter and rate of its change.
+
+<p> <bf/Daemonic Xstat/
+<p> <tt/Xstat/ may be started as daemon by any user. This makes sense
+to avoid wrapped counters and to obtain reasonable long counters
+for large time. Also <tt/Xstat/ daemon calculates average rates.
+For the first goal sampling interval (option <tt/-d/) may be large enough,
+f.e. for gigabit rates byte counters overflow not more frequently than
+each 40 seconds and you may select interval of 20 seconds.
+From the other hand, when <tt/Xstat/ is used for estimating rates
+interval should be less than averaging period (option <tt/-t/), otherwise
+estimation loses in quality.
+
+Client <tt/Xstat/, before trying to get information from the kernel,
+contacts daemon started by this user, then it tries system wide
+daemon, which is supposed to be started by superuser. And only if
+none of them replied it gets information from kernel.
+
+<p> <bf/Environment/
+<p> <tt/NSTAT_HISTORY/ - name of history file for <tt/nstat/.
+<p> <tt/IFSTAT_HISTORY/ - name of history file for <tt/ifstat/.
+<p> <tt/RTACCT_HISTORY/ - name of history file for <tt/rtacct/.
+
+</article>
diff --git a/doc/preamble.tex b/doc/preamble.tex
new file mode 100644
index 0000000..80ca508
--- /dev/null
+++ b/doc/preamble.tex
@@ -0,0 +1,26 @@
+\textwidth   6.0in
+\textheight  8.5in
+
+\input SNAPSHOT
+
+\pagestyle{myheadings}
+\markboth{\protect\TITLE}{}
+\markright{{\protect\sc iproute2-ss\Draft}}
+
+% To print it in compact form: both sides on one sheet (psnup -2)
+\evensidemargin=\oddsidemargin
+
+\newenvironment{NB}{\bgroup \vskip 1mm\leftskip 1cm \footnotesize \noindent NB.
+}{\par\egroup \vskip 1mm}
+
+\def\threeonly{[2.3.15+ only] }
+
+\begin{document}
+
+\makeatletter
+\renewcommand{\@oddhead}{{\protect\sc iproute2-ss\Draft} \hfill \protect\arabic{page}}
+\makeatother
+\let\oldthefootnote\thefootnote
+\def\thefootnote{}
+\footnotetext{Copyright \copyright~1999 A.N.Kuznetsov}
+
diff --git a/doc/rtstat.sgml b/doc/rtstat.sgml
new file mode 100644
index 0000000..07391c3
--- /dev/null
+++ b/doc/rtstat.sgml
@@ -0,0 +1,52 @@
+<!doctype linuxdoc system>
+
+<article>
+
+<title>RTACCT Utility
+<author>Robert Olsson
+<date>some_negative_number, 20 Dec 2001
+
+<p>
+Here is some code for monitoring the route cache. For systems handling high
+network load, servers, routers, firewalls etc the route cache and its garbage
+collection is crucial. Linux has a solid implementation.
+
+<p>
+The kernel patch (not required since linux-2.4.7) adds statistics counters
+from route cache process into 
+/proc/net/rt_cache_stat. A companion user mode program presents the statistics
+in a vmstat or iostat manner. The ratio between cache hits and misses gives 
+the flow length.
+
+<p>
+Hopefully it can help understanding performance and DoS and other related
+issues.
+
+<p> An URL where newer versions of this utility can be (probably) found
+is ftp://robur.slu.se/pub/Linux/net-development/rt_cache_stat/
+
+
+<p><bf/Description/
+
+<p>The format of the command is:
+
+<tscreen><verb>
+       rtstat [ OPTIONS ]
+</verb></tscreen>
+
+<p> <tt/OPTIONS/ are:
+
+<itemize>
+
+<item><tt/-h/, <tt/-help/ - show help page and version of the utility.
+
+<item><tt/-i INTERVAL/ - interval between snapshots, default value is
+2 seconds.
+
+<item><tt/-s NUMBER/ - whether to print header line. 0 inhibits header line,
+1 prescribes to print it once and 2 (this is default setting) forces header
+line each 20 lines. 
+
+</itemize>
+
+</article>
diff --git a/doc/ss.sgml b/doc/ss.sgml
new file mode 100644
index 0000000..3024b57
--- /dev/null
+++ b/doc/ss.sgml
@@ -0,0 +1,525 @@
+<!doctype linuxdoc system>
+
+<article>
+
+<title>SS Utility: Quick Intro
+<author>Alexey Kuznetsov, <tt/kuznet@ms2.inr.ac.ru/
+<date>some_negative_number, 20 Sep 2001
+<abstract>
+<tt/ss/ is one another utility to investigate sockets.
+Functionally it is NOT better than <tt/netstat/ combined
+with some perl/awk scripts and though it is surely faster
+it is not enough to make it much better. :-)
+So, stop reading this now and do not waste your time.
+Well, certainly, it proposes some functionality, which current
+netstat is still not able to do, but surely will soon.
+</abstract>
+
+<sect>Why?
+
+<p> <tt>/proc</tt> interface is inadequate, unfortunately.
+When amount of sockets is enough large, <tt/netstat/ or even
+plain <tt>cat /proc/net/tcp/</tt> cause nothing but pains and curses.
+In linux-2.4 the desease became worse: even if amount
+of sockets is small reading <tt>/proc/net/tcp/</tt> is slow enough.
+
+This utility presents a new approach, which is supposed to scale
+well. I am not going to describe technical details here and
+will concentrate on description of the command.
+The only important thing to say is that it is not so bad idea
+to load module <tt/tcp_diag/, which can be found in directory
+<tt/Modules/ of <tt/iproute2/. If you do not make this <tt/ss/
+will work, but it falls back to <tt>/proc</tt> and becomes slow
+like <tt/netstat/, well, a bit faster yet (see section "Some numbers"). 
+
+<sect>Old news
+
+<p>
+In the simplest form <tt/ss/ is equivalent to netstat
+with some small deviations.
+
+<itemize>
+<item><tt/ss -t -a/ dumps all TCP sockets
+<item><tt/ss -u -a/ dumps all UDP sockets
+<item><tt/ss -w -a/ dumps all RAW sockets
+<item><tt/ss -x -a/ dumps all UNIX sockets
+</itemize>
+
+<p>
+Option <tt/-o/ shows TCP timers state.
+Option <tt/-e/ shows some extended information.
+Etc. etc. etc. Seems, all the options of netstat related to sockets
+are supported. Though not AX.25 and other bizarres. :-)
+If someone wants, he can make support for decnet and ipx.
+Some rudimentary support for them is already present in iproute2 libutils,
+and I will be glad to see these new members.
+
+<p>
+However, standard functionality is a bit different:
+
+<p>
+The first: without option <tt/-a/ sockets in states
+<tt/TIME-WAIT/ and <tt/SYN-RECV/ are skipped too.
+It is more reasonable default, I think.
+
+<p>
+The second: format of UNIX sockets is different. It coincides
+with tcp/udp. Though standard kernel still does not allow to
+see write/read queues and peer address of connected UNIX sockets,
+the patch doing this exists.
+
+<p>
+The third: default is to dump only TCP sockets, rather than all of the types.
+
+<p>
+The next: by default it does not resolve numeric host addresses (like <tt/ip/)!
+Resolving is enabled with option <tt/-r/. Service names, usually stored
+in local files, are resolved by default. Also, if service database
+does not contain references to a port, <tt/ss/ queries system
+<tt/rpcbind/. RPC services are prefixed with <tt/rpc./
+Resolution of services may be suppressed with option <tt/-n/.
+
+<p>
+It does not accept "long" options (I dislike them, sorry).
+So, address family is given with family identifier following
+option <tt/-f/ to be algined to iproute2 conventions.
+Mostly, it is to allow option parser to parse
+addresses correctly, but as side effect it really limits dumping
+to sockets supporting only given family. Option <tt/-A/ followed
+by list of socket tables to dump is also supported.
+Logically, id of socket table is different of _address_ family, which is
+another point of incompatibility. So, id is one of
+<tt/all/, <tt/tcp/, <tt/udp/,
+<tt/raw/, <tt/inet/, <tt/unix/, <tt/packet/, <tt/netlink/. See?
+Well, <tt/inet/ is just abbreviation for <tt/tcp|udp|raw/
+and it is not difficult to guess that <tt/packet/ allows
+to look at packet sockets. Actually, there are also some other abbreviations,
+f.e. <tt/unix_dgram/ selects only datagram UNIX sockets.
+
+<p>
+The next: well, I still do not know. :-)
+
+
+
+
+<sect>Time to talk about new functionality.
+
+<p>It is builtin filtering of socket lists. 
+
+<sect1> Filtering by state.
+
+<p>
+<tt/ss/ allows to filter socket states, using keywords
+<tt/state/ and <tt/exclude/, followed by some state
+identifier.
+
+<p>
+State identifier are standard TCP state names (not listed,
+they are useless for you if you already do not know them)
+or abbreviations:
+
+<itemize>
+<item><tt/all/        - for all the states
+<item><tt/bucket/     - for TCP minisockets (<tt/TIME-WAIT|SYN-RECV/)
+<item><tt/big/	      - all except for minisockets
+<item><tt/connected/  - not closed and not listening
+<item><tt/synchronized/ - connected and not <tt/SYN-SENT/
+</itemize>
+
+<p>
+   F.e. to dump all tcp sockets except <tt/SYN-RECV/:
+
+<tscreen><verb>
+   ss exclude SYN-RECV
+</verb></tscreen>
+
+<p>
+   If neither <tt/state/ nor <tt/exclude/ directives
+   are present,
+   state filter defaults to <tt/all/ with option <tt/-a/
+   or to <tt/all/,
+   excluding listening, syn-recv, time-wait and closed sockets.
+
+<sect1> Filtering by addresses and ports.
+
+<p>
+Option list may contain address/port filter.
+It is boolean expression which consists of boolean operation
+<tt/or/, <tt/and/, <tt/not/ and predicates. 
+Actually, all the flavors of names for boolean operations are eaten:
+<tt/&amp/, <tt/&amp&amp/, <tt/|/, <tt/||/, <tt/!/, but do not forget
+about special sense given to these symbols by unix shells and escape
+them correctly, when used from command line.
+
+<p>
+Predicates may be of the folowing kinds:
+
+<itemize>
+<item>A. Address/port match, where address is checked against mask
+      and port is either wildcard or exact. It is one of:
+ 
+<tscreen><verb>
+	dst prefix:port
+	src prefix:port
+	src unix:STRING
+	src link:protocol:ifindex
+	src nl:channel:pid
+</verb></tscreen>
+
+      Both prefix and port may be absent or replaced with <tt/*/,
+      which means wildcard. UNIX socket use more powerful scheme
+      matching to socket names by shell wildcards. Also, prefixes
+      unix: and link: may be omitted, if address family is evident
+      from context (with option <tt/-x/ or with <tt/-f unix/
+      or with <tt/unix/ keyword) 
+
+<p>
+      F.e.
+
+<tscreen><verb>
+	dst 10.0.0.1
+	dst 10.0.0.1:
+	dst 10.0.0.1/32:
+	dst 10.0.0.1:*
+</verb></tscreen>
+   are equivalent and mean socket connected to
+	                 any port on host 10.0.0.1
+
+<tscreen><verb>
+	dst 10.0.0.0/24:22
+</verb></tscreen>
+   sockets connected to port 22 on network
+                          10.0.0.0...255.
+
+<p>
+      Note that port separated of address with colon, which creates
+      troubles with IPv6 addresses. Generally, we interpret the last
+      colon as splitting port. To allow to give IPv6 addresses,
+      trick like used in IPv6 HTTP URLs may be used:
+
+<tscreen><verb>
+      dst [::1]
+</verb></tscreen>
+       are sockets connected to ::1 on any port
+
+<p>
+      Another way is <tt/dst ::1/128/. / helps to understand that
+      colon is part of IPv6 address.
+
+<p>
+      Now we can add another alias for <tt/dst 10.0.0.1/:
+      <tt/dst [10.0.0.1]/. :-)
+
+<p>   Address may be a DNS name. In this case all the addresses are looked
+      up (in all the address families, if it is not limited by option <tt/-f/
+      or special address prefix <tt/inet:/, <tt/inet6/) and resulting
+      expression is <tt/or/ over all of them.  
+
+<item>   B. Port expressions:
+<tscreen><verb>
+      dport &gt= :1024
+      dport != :22
+      sport &lt :32000
+</verb></tscreen>
+      etc.
+
+      All the relations: <tt/&lt/, <tt/&gt/, <tt/=/, <tt/>=/, <tt/=/, <tt/==/,
+      <tt/!=/, <tt/eq/, <tt/ge/, <tt/lt/, <tt/ne/...
+      Use variant which you like more, but not forget to escape special
+      characters when typing them in command line. :-) 
+
+      Note that port number syntactically coincides to the case A!
+      You may even add an IP address, but it will not participate
+      incomparison, except for <tt/==/ and <tt/!=/, which are equivalent
+      to corresponding predicates of type A. F.e.
+<p>
+<tt/dst 10.0.0.1:22/
+    is equivalent to  <tt/dport eq 10.0.0.1:22/
+      and
+      <tt/not dst 10.0.0.1:22/     is equivalent to
+ <tt/dport neq 10.0.0.1:22/
+
+<item>C. Keyword <tt/autobound/. It matches to sockets bound automatically
+      on local system.
+
+</itemize>
+
+
+<sect> Examples
+
+<p>
+<itemize>
+<item>1. List all the tcp sockets in state <tt/FIN-WAIT-1/ for our apache
+   to network 193.233.7/24 and look at their timers:
+
+<tscreen><verb>
+   ss -o state fin-wait-1 \( sport = :http or sport = :https \) \
+                          dst 193.233.7/24
+</verb></tscreen>
+
+   Oops, forgot to say that missing logical operation is
+   equivalent to <tt/and/.
+
+<item> 2. Well, now look at the rest...
+
+<tscreen><verb>
+   ss -o excl fin-wait-1
+   ss state fin-wait-1 \( sport neq :http and sport neq :https \) \
+                       or not dst 193.233.7/24
+</verb></tscreen>
+
+   Note that we have to do _two_ calls of ss to do this.
+   State match is always anded to address/port match.
+   The reason for this is purely technical: ss does fast skip of
+   not matching states before parsing addresses and I consider the
+   ability to skip fastly gobs of time-wait and syn-recv sockets
+   as more important than logical generality.
+
+<item> 3. So, let's look at all our sockets using autobound ports:
+
+<tscreen><verb>
+   ss -a -A all autobound
+</verb></tscreen>
+
+
+<item> 4. And eventually find all the local processes connected
+   to local X servers:
+
+<tscreen><verb>
+   ss -xp dst "/tmp/.X11-unix/*"
+</verb></tscreen>
+
+   Pardon, this does not work with current kernel, patching is required.
+   But we still can look at server side:
+   
+<tscreen><verb>
+   ss -x src "/tmp/.X11-unix/*"
+</verb></tscreen>
+
+</itemize>
+
+
+<sect> Returning to ground: real manual  
+
+<p>
+<sect1> Command arguments
+
+<p> General format of arguments to <tt/ss/ is:
+
+<tscreen><verb>
+       ss [ OPTIONS ] [ STATE-FILTER ] [ ADDRESS-FILTER ]
+</verb></tscreen>
+
+<sect2><tt/OPTIONS/
+<p> <tt/OPTIONS/ is list of single letter options, using common unix
+conventions.
+
+<itemize>
+<item><tt/-h/  - show help page
+<item><tt/-?/  - the same, of course
+<item><tt/-v/, <tt/-V/  - print version of <tt/ss/ and exit
+<item><tt/-s/  - print summary statistics. This option does not parse
+socket lists obtaining summary from various sources. It is useful
+when amount of sockets is so huge that parsing <tt>/proc/net/tcp</tt>
+is painful.
+<item><tt/-D FILE/  - do not display anything, just dump raw information
+about TCP sockets to <tt/FILE/ after applying filters. If <tt/FILE/ is <tt/-/
+<tt/stdout/ is used. 
+<item><tt/-F FILE/  - read continuation of filter from <tt/FILE/.
+Each line of <tt/FILE/ is interpreted like single command line option.
+If <tt/FILE/ is <tt/-/ <tt/stdin/ is used. 
+<item><tt/-r/  - try to resolve numeric address/ports
+<item><tt/-n/  - do not try to resolve ports
+<item><tt/-o/  - show some optional information, f.e. TCP timers
+<item><tt/-i/  - show some infomration specific to TCP (RTO, congestion
+window, slow start threshould etc.)
+<item><tt/-e/  - show even more optional information
+<item><tt/-m/  - show extended information on memory used by the socket.
+It is available only with <tt/tcp_diag/ enabled.
+<item><tt/-p/  - show list of processes owning the socket
+<item><tt/-f FAMILY/ - default address family used for parsing addresses.
+                 Also this option limits listing to sockets supporting
+                 given address family. Currently the following families
+                 are supported: <tt/unix/, <tt/inet/, <tt/inet6/, <tt/link/,
+                 <tt/netlink/.
+<item><tt/-4/ - alias for <tt/-f inet/
+<item><tt/-6/ - alias for <tt/-f inet6/
+<item><tt/-0/ - alias for <tt/-f link/
+<item><tt/-A LIST-OF-TABLES/ - list of socket tables to dump, separated
+                 by commas. The following identifiers are understood:
+                 <tt/all/, <tt/inet/, <tt/tcp/, <tt/udp/, <tt/raw/,
+                 <tt/unix/, <tt/packet/, <tt/netlink/, <tt/unix_dgram/,
+                 <tt/unix_stream/, <tt/packet_raw/, <tt/packet_dgram/.
+<item><tt/-x/ - alias for <tt/-A unix/
+<item><tt/-t/ - alias for <tt/-A tcp/
+<item><tt/-u/ - alias for <tt/-A udp/
+<item><tt/-w/ - alias for <tt/-A raw/
+<item><tt/-a/ - show sockets of all the states. By default sockets
+                in states <tt/LISTEN/, <tt/TIME-WAIT/, <tt/SYN_RECV/
+                and <tt/CLOSE/ are skipped.
+<item><tt/-l/ - show only sockets in state <tt/LISTEN/ 
+</itemize>
+
+<sect2><tt/STATE-FILTER/
+
+<p><tt/STATE-FILTER/ allows to construct arbitrary set of
+states to match. Its syntax is sequence of keywords <tt/state/
+and <tt/exclude/ followed by identifier of state.
+Available identifiers are:
+
+<p>
+<itemize>
+<item> All standard TCP states: <tt/established/, <tt/syn-sent/,
+<tt/syn-recv/, <tt/fin-wait-1/, <tt/fin-wait-2/, <tt/time-wait/,
+<tt/closed/, <tt/close-wait/, <tt/last-ack/, <tt/listen/ and <tt/closing/.
+
+<item><tt/all/ - for all the states 
+<item><tt/connected/ - all the states except for <tt/listen/ and <tt/closed/ 
+<item><tt/synchronized/ - all the <tt/connected/ states except for 
+<tt/syn-sent/
+<item><tt/bucket/ - states, which are maintained as minisockets, i.e.
+<tt/time-wait/ and <tt/syn-recv/.
+<item><tt/big/ - opposite to <tt/bucket/
+</itemize>
+
+<sect2><tt/ADDRESS_FILTER/
+
+<p><tt/ADDRESS_FILTER/ is boolean expression with operations <tt/and/, <tt/or/
+and <tt/not/, which can be abbreviated in C style f.e. as <tt/&amp/,
+<tt/&amp&amp/.
+
+<p>
+Predicates check socket addresses, both local and remote.
+There are the following kinds of predicates:
+
+<itemize>
+<item> <tt/dst ADDRESS_PATTERN/ - matches remote address and port
+<item> <tt/src ADDRESS_PATTERN/ - matches local address and port
+<item> <tt/dport RELOP PORT/    - compares remote port to a number
+<item> <tt/sport RELOP PORT/    - compares local port to a number
+<item> <tt/autobound/           - checks that socket is bound to an ephemeral
+                                  port
+</itemize>
+
+<p><tt/RELOP/ is some of <tt/&lt=/, <tt/&gt=/, <tt/==/ etc.
+To make this more convinient for use in unix shell, alphabetic
+FORTRAN-like notations <tt/le/, <tt/gt/ etc. are accepted as well.
+
+<p>The format and semantics of <tt/ADDRESS_PATTERN/ depends on address
+family.
+
+<itemize>
+<item><tt/inet/ - <tt/ADDRESS_PATTERN/ consists of IP prefix, optionally
+followed by colon and port. If prefix or port part is absent or replaced
+with <tt/*/, this means wildcard match.
+<item><tt/inet6/ - The same as <tt/inet/, only prefix refers to an IPv6
+address. Unlike <tt/inet/ colon becomes ambiguous, so that <tt/ss/ allows
+to use scheme, like used in URLs, where address is suppounded with
+<tt/[/ ... <tt/]/.
+<item><tt/unix/ - <tt/ADDRESS_PATTERN/ is shell-style wildcard.
+<item><tt/packet/ - format looks like <tt/inet/, only interface index
+stays instead of port and link layer protocol id instead of address.
+<item><tt/netlink/ - format looks like <tt/inet/, only socket pid
+stays instead of port and netlink channel instead of address.
+</itemize>
+
+<p><tt/PORT/ is syntactically <tt/ADDRESS_PATTERN/ with wildcard
+address part. Certainly, it is undefined for UNIX sockets. 
+
+<sect1> Environment variables
+
+<p>
+<tt/ss/ allows to change source of information using various
+environment variables:
+
+<p>
+<itemize>
+<item> <tt/PROC_SLABINFO/  to override <tt>/proc/slabinfo</tt>
+<item> <tt/PROC_NET_TCP/  to override <tt>/proc/net/tcp</tt>
+<item> <tt/PROC_NET_UDP/  to override <tt>/proc/net/udp</tt>
+<item> etc.
+</itemize> 
+
+<p>
+Variable <tt/PROC_ROOT/ allows to change root of all the <tt>/proc/</tt>
+hierarchy.
+
+<p>
+Variable <tt/TCPDIAG_FILE/ prescribes to open a file instead of
+requesting kernel to dump information about TCP sockets.
+
+
+<p> This option is used mainly to investigate bug reports,
+when dumps of files usually found in <tt>/proc/</tt> are recevied
+by e-mail.
+
+<sect1> Output format
+
+<p>Six columns. The first is <tt/Netid/, it denotes socket type and
+transport protocol, when it is ambiguous: <tt/tcp/, <tt/udp/, <tt/raw/,
+<tt/u_str/ is abbreviation for <tt/unix_stream/, <tt/u_dgr/ for UNIX
+datagram sockets, <tt/nl/ for netlink, <tt/p_raw/ and <tt/p_dgr/ for
+raw and datagram packet sockets. This column is optional, it will
+be hidden, if filter selects an unique netid.
+
+<p>
+The second column is <tt/State/. Socket state is displayed here.
+The names are standard TCP names, except for <tt/UNCONN/, which
+cannot happen for TCP, but normal for not connected sockets
+of another types. Again, this column can be hidden.
+
+<p>
+Then two columns (<tt/Recv-Q/ and <tt/Send-Q/) showing amount of data
+queued for receive and transmit.
+
+<p>
+And the last two columns display local address and port of the socket
+and its peer address, if the socket is connected.
+
+<p>
+If options <tt/-o/, <tt/-e/ or <tt/-p/ were given, options are
+displayed not in fixed positions but separated by spaces pairs:
+<tt/option:value/. If value is not a single number, it is presented
+as list of values, enclosed to <tt/(/ ... <tt/)/ and separated with
+commas. F.e.
+
+<tscreen><verb>
+   timer:(keepalive,111min,0)
+</verb></tscreen>
+is typical format for TCP timer (option <tt/-o/).
+
+<tscreen><verb>
+   users:((X,113,3))
+</verb></tscreen>
+is typical for list of users (option <tt/-p/).
+
+
+<sect>Some numbers
+
+<p>
+Well, let us use <tt/pidentd/ and a tool <tt/ibench/ to measure
+its performance. It is 30 requests per second here. Nothing to test,
+it is too slow. OK, let us patch pidentd with patch from directory
+Patches. After this it handles about 4300 requests per second
+and becomes handy tool to pollute socket tables with lots of timewait
+buckets.
+
+<p>
+So, each test starts from pollution tables with 30000 sockets
+and then doing full dump of the table piped to wc and measuring
+timings with time:
+
+<p>Results:
+
+<itemize>
+<item> <tt/netstat -at/ - 15.6 seconds
+<item> <tt/ss -atr/, but without <tt/tcp_diag/     - 5.4 seconds
+<item> <tt/ss -atr/ with <tt/tcp_diag/     - 0.47 seconds
+</itemize>
+
+No comments. Though one comment is necessary, most of time
+without <tt/tcp_diag/ is wasted inside kernel with completely
+blocked networking. More than 10 seconds, yes. <tt/tcp_diag/
+does the same work for 100 milliseconds of system time.
+
+</article>
diff --git a/etc/iproute2/ematch_map b/etc/iproute2/ematch_map
index 4d6bb2f..1823983 100644
--- a/etc/iproute2/ematch_map
+++ b/etc/iproute2/ematch_map
@@ -5,4 +5,3 @@
 4	meta
 7	canid
 8	ipset
-9	ipt
diff --git a/etc/iproute2/nl_protos b/etc/iproute2/nl_protos
index 7c17cf0..43418f3 100644
--- a/etc/iproute2/nl_protos
+++ b/etc/iproute2/nl_protos
@@ -12,7 +12,7 @@
 9   audit
 10  fiblookup
 11  connector
-12  nft
+12  nft 
 13  ip6fw
 14  dec-rt
 15  uevent
@@ -20,4 +20,4 @@
 18  scsi-trans
 19  ecryptfs
 20  rdma
-21  crypto
+21  crypto 
diff --git a/etc/iproute2/rt_protos b/etc/iproute2/rt_protos
index b3a0ec8..82cf9c4 100644
--- a/etc/iproute2/rt_protos
+++ b/etc/iproute2/rt_protos
@@ -16,8 +16,16 @@
 15	ntk
 16      dhcp
 42	babel
-186	bgp
-187	isis
-188	ospf
-189	rip
-192	eigrp
+
+#
+#	Used by me for gated
+#
+254	gated/aggr
+253	gated/bgp
+252	gated/ospf
+251	gated/ospfase
+250	gated/rip
+249	gated/static
+248	gated/conn
+247	gated/inet
+246	gated/default
diff --git a/etc/iproute2/rt_protos.d/README b/etc/iproute2/rt_protos.d/README
deleted file mode 100644
index f9c599c..0000000
--- a/etc/iproute2/rt_protos.d/README
+++ /dev/null
@@ -1,2 +0,0 @@
-Each file in this directory is an rt_protos configuration file. iproute2
-commands scan this directory processing all files that end in '.conf'.
diff --git a/etc/iproute2/rt_tables.d/README b/etc/iproute2/rt_tables.d/README
index 0920cb1..79386f8 100644
--- a/etc/iproute2/rt_tables.d/README
+++ b/etc/iproute2/rt_tables.d/README
@@ -1,2 +1,3 @@
 Each file in this directory is an rt_tables configuration file. iproute2
 commands scan this directory processing all files that end in '.conf'.
+
diff --git a/examples/README.cbq b/examples/README.cbq
new file mode 100644
index 0000000..38c1089
--- /dev/null
+++ b/examples/README.cbq
@@ -0,0 +1,122 @@
+# CHANGES
+# -------
+# v0.3a2- fixed bug in "if" operator. Thanks kad@dgtu.donetsk.ua.
+# v0.3a-  added TIME parameter. Example:
+#         TIME=00:00-19:00;64Kbit/6Kbit
+#         So, between 00:00 and 19:00 RATE will be 64Kbit.
+#         Just start "cbq.init timecheck" periodically from cron (every 10
+#         minutes for example).
+#         !!! Anyway you MUST start "cbq.init start" for CBQ initialize.
+# v0.2 -  Some cosmetique changes. Now it more compatible with
+#         old bash version. Thanks to Stanislav V. Voronyi
+#         <stas@cnti.uanet.kharkov.ua>.
+# v0.1 -  First public release
+# 
+# README
+# ------
+# 
+# First of all - this is just a SIMPLE EXAMPLE of CBQ power.
+# Don't ask me "why" and "how" :)
+# 
+# This is an example of using CBQ (Class Based Queueing) and policy-based
+# filter for building smart ethernet shapers. All CBQ parameters are
+# correct only for ETHERNET (eth0,1,2..) linux interfaces. It works for
+# ARCNET too (just set bandwidth parameter to 2Mbit). It was tested
+# on 2.1.125-2.1.129 linux kernels (KSI linux, Nostromo version) and 
+# ip-route utility by A.Kuznetsov (iproute2-ss981101 version). 
+# You can download ip-route from ftp://ftp.inr.ac.ru/ip-routing or
+# get iproute2*.rpm (compiled with glibc) from ftp.ksi-linux.com.
+# 
+# 
+# HOW IT WORKS
+# 
+# Each shaper must be described by config file in $CBQ_PATH
+# (/etc/sysconfig/cbq/) directory - one config file for each CBQ shaper.
+# 
+# Some words about config file name:
+# Each shaper has its personal ID - two byte HEX number. Really ID is 
+# CBQ class.
+# So, filename looks like:
+# 
+# cbq-1280.My_first_shaper
+# ^^^ ^^^  ^^^^^^^^^^^^^
+#  |  |            |______ Shaper name - any word
+#  |  |___________________ ID (0000-FFFF), let ID looks like shaper's rate
+#  |______________________ Filename must begin from "cbq-" 
+# 
+# 
+# Config file describes shaper parameters and source[destination] 
+# address[port].
+# For example let's prepare /etc/sysconfig/cbq/cbq-1280.My_first_shaper:
+# 
+# ----------8<---------------------
+# DEVICE=eth0,10Mbit,1Mbit
+# RATE=128Kbit
+# WEIGHT=10Kbit
+# PRIO=5
+# RULE=192.168.1.0/24
+# ----------8<---------------------
+# 
+# This is minimal configuration, where:
+# DEVICE:  eth0   - device where we do control our traffic
+#          10Mbit - REAL ethernet card bandwidth
+#          1Mbit  - "weight" of :1 class (parent for all shapers for eth0),
+#                   as a rule of thumb weight=batdwidth/10.
+#          100Mbit adapter's example: DEVICE=eth0,100Mbit,10Mbit
+#          *** If you want to build more than one shaper per device it's
+#              enough to describe bandwidth and weight once  - cbq.init
+#              is smart :) You can put only 'DEVICE=eth0' into cbq-* 
+#              config file for eth0.
+# 
+# RATE:    Shaper's speed - Kbit,Mbit or bps (bytes per second)
+# 
+# WEIGHT:  "weight" of shaper (CBQ class). Like for DEVICE - approx. RATE/10
+# 
+# PRIO:    shaper's priority from 1 to 8 where 1 is the highest one.
+#          I do always use "5" for all my shapers.
+# 
+# RULE:    [source addr][:source port],[dest addr][:dest port]
+#          Some examples:
+# RULE=10.1.1.0/24:80         - all traffic for network 10.1.1.0 to port 80
+#                               will be shaped.
+# RULE=10.2.2.5               - shaper works only for IP address 10.2.2.5   
+# RULE=:25,10.2.2.128/25:5000 - all traffic from any address and port 25 to
+#                               address 10.2.2.128 - 10.2.2.255 and port 5000
+#                               will be shaped.
+# RULE=10.5.5.5:80,           - shaper active only for traffic from port 80 of
+#                               address 10.5.5.5
+# Multiple RULE fields per one config file are allowed. For example:
+# RULE=10.1.1.2:80
+# RULE=10.1.1.2:25
+# RULE=10.1.1.2:110
+# 
+# *** ATTENTION!!!
+# All shapers do work only for outgoing traffic!
+# So, if you want to build bidirectional shaper you must set it up for
+# both ethernet card. For example let's build shaper for our linux box like:
+# 
+#                     ---------             192.168.1.1
+# BACKBONE -----eth0-|  linux  |-eth1------*[our client]
+#                     ---------
+# 
+# Let all traffic from backbone to client will be shaped at 28Kbit and
+# traffic from client to backbone - at 128Kbit. We need two config files:
+# 
+# ---8<-----/etc/sysconfig/cbq/cbq-28.client-out----
+# DEVICE=eth1,10Mbit,1Mbit
+# RATE=28Kbit
+# WEIGHT=2Kbit
+# PRIO=5
+# RULE=192.168.1.1
+# ---8<---------------------------------------------
+# 
+# ---8<-----/etc/sysconfig/cbq/cbq-128.client-in----
+# DEVICE=eth0,10Mbit,1Mbit
+# RATE=128Kbit
+# WEIGHT=10Kbit
+# PRIO=5
+# RULE=192.168.1.1,
+# ---8<---------------------------------------------
+#                 ^pay attention to "," - this is source address!
+# 
+# Enjoy.
diff --git a/examples/SYN-DoS.rate.limit b/examples/SYN-DoS.rate.limit
new file mode 100644
index 0000000..8766b67
--- /dev/null
+++ b/examples/SYN-DoS.rate.limit
@@ -0,0 +1,49 @@
+#! /bin/sh -x
+#
+# sample script on using the ingress capabilities
+# this script shows how one can rate limit incoming SYNs
+# Useful for TCP-SYN attack protection. You can use
+# IPchains to have more powerful additions to the SYN (eg 
+# in addition the subnet)
+#
+#path to various utilities;
+#change to reflect yours.
+#
+IPROUTE=/root/DS-6-beta/iproute2-990530-dsing
+TC=$IPROUTE/tc/tc
+IP=$IPROUTE/ip/ip
+IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains
+INDEV=eth2
+#
+# tag all incoming SYN packets through $INDEV as mark value 1
+############################################################ 
+$IPCHAINS -A input -i $INDEV -y -m 1
+############################################################ 
+#
+# install the ingress qdisc on the ingress interface
+############################################################ 
+$TC qdisc add dev $INDEV handle ffff: ingress
+############################################################ 
+
+#
+# 
+# SYN packets are 40 bytes (320 bits) so three SYNs equals
+# 960 bits (approximately 1kbit); so we rate limit below
+# the incoming SYNs to 3/sec (not very sueful really; but
+#serves to show the point - JHS
+############################################################ 
+$TC filter add dev $INDEV parent ffff: protocol ip prio 50 handle 1 fw \
+police rate 1kbit burst 40 mtu 9k drop flowid :1
+############################################################ 
+
+
+#
+echo "---- qdisc parameters Ingress  ----------"
+$TC qdisc ls dev $INDEV
+echo "---- Class parameters Ingress  ----------"
+$TC class ls dev $INDEV
+echo "---- filter parameters Ingress ----------"
+$TC filter ls dev $INDEV parent ffff:
+
+#deleting the ingress qdisc
+#$TC qdisc del $INDEV ingress
diff --git a/examples/bpf/README b/examples/bpf/README
index 1bbdda3..4247257 100644
--- a/examples/bpf/README
+++ b/examples/bpf/README
@@ -1,8 +1,13 @@
 eBPF toy code examples (running in kernel) to familiarize yourself
 with syntax and features:
 
+ - bpf_prog.c		-> Classifier examples with using maps
  - bpf_shared.c		-> Ingress/egress map sharing example
  - bpf_tailcall.c	-> Using tail call chains
  - bpf_cyclic.c		-> Simple cycle as tail calls
  - bpf_graft.c		-> Demo on altering runtime behaviour
- - bpf_map_in_map.c     -> Using map in map example
+
+User space code example:
+
+ - bpf_agent.c		-> Counterpart to bpf_prog.c for user
+                           space to transfer/read out map data
diff --git a/examples/bpf/bpf_agent.c b/examples/bpf/bpf_agent.c
new file mode 100644
index 0000000..f9b9ce3
--- /dev/null
+++ b/examples/bpf/bpf_agent.c
@@ -0,0 +1,258 @@
+/*
+ * eBPF user space agent part
+ *
+ * Simple, _self-contained_ user space agent for the eBPF kernel
+ * ebpf_prog.c program, which gets all map fds passed from tc via unix
+ * domain socket in one transaction and can thus keep referencing
+ * them from user space in order to read out (or possibly modify)
+ * map data. Here, just as a minimal example to display counters.
+ *
+ * The agent only uses the bpf(2) syscall API to read or possibly
+ * write to eBPF maps, it doesn't need to be aware of the low-level
+ * bytecode parts and/or ELF parsing bits.
+ *
+ * ! For more details, see header comment in bpf_prog.c !
+ *
+ * gcc bpf_agent.c -o bpf_agent -Wall -O2
+ *
+ * For example, a more complex user space agent could run on each
+ * host, reading and writing into eBPF maps used by tc classifier
+ * and actions. It would thus allow for implementing a distributed
+ * tc architecture, for example, which would push down central
+ * policies into eBPF maps, and thus altering run-time behaviour.
+ *
+ *   -- Happy eBPF hacking! ;)
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include <sys/un.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+/* Just some misc macros as min(), offsetof(), etc. */
+#include "../../include/utils.h"
+/* Common code from fd passing. */
+#include "../../include/bpf_scm.h"
+/* Common, shared definitions with ebpf_prog.c */
+#include "bpf_shared.h"
+/* Mini syscall wrapper */
+#include "bpf_sys.h"
+
+static void bpf_dump_drops(int fd)
+{
+	int cpu, max;
+
+	max = sysconf(_SC_NPROCESSORS_ONLN);
+
+	printf(" `- number of drops:");
+	for (cpu = 0; cpu < max; cpu++) {
+		long drops;
+
+		assert(bpf_lookup_elem(fd, &cpu, &drops) == 0);
+		printf("\tcpu%d: %5ld", cpu, drops);
+	}
+	printf("\n");
+}
+
+static void bpf_dump_queue(int fd)
+{
+	/* Just for the same of the example. */
+	int max_queue = 4, i;
+
+	printf("  | nic queues:");
+	for (i = 0; i < max_queue; i++) {
+		struct count_queue cq;
+		int ret;
+
+		memset(&cq, 0, sizeof(cq));
+		ret = bpf_lookup_elem(fd, &i, &cq);
+		assert(ret == 0 || (ret < 0 && errno == ENOENT));
+
+		printf("\tq%d:[pkts: %ld, mis: %ld]",
+		       i, cq.total, cq.mismatch);
+	}
+	printf("\n");
+}
+
+static void bpf_dump_proto(int fd)
+{
+	uint8_t protos[] = { IPPROTO_TCP, IPPROTO_UDP, IPPROTO_ICMP };
+	char *names[] = { "tcp", "udp", "icmp" };
+	int i;
+
+	printf("  ` protos:");
+	for (i = 0; i < ARRAY_SIZE(protos); i++) {
+		struct count_tuple ct;
+		int ret;
+
+		memset(&ct, 0, sizeof(ct));
+		ret = bpf_lookup_elem(fd, &protos[i], &ct);
+		assert(ret == 0 || (ret < 0 && errno == ENOENT));
+
+		printf("\t%s:[pkts: %ld, bytes: %ld]",
+		       names[i], ct.packets, ct.bytes);
+	}
+	printf("\n");
+}
+
+static void bpf_dump_map_data(int *tfd)
+{
+	int i;
+
+	for (i = 0; i < 30; i++) {
+		const int period = 5;
+
+		printf("data, period: %dsec\n", period);
+
+		bpf_dump_drops(tfd[BPF_MAP_ID_DROPS]);
+		bpf_dump_queue(tfd[BPF_MAP_ID_QUEUE]);
+		bpf_dump_proto(tfd[BPF_MAP_ID_PROTO]);
+
+		sleep(period);
+	}
+}
+
+static void bpf_info_loop(int *fds, struct bpf_map_aux *aux)
+{
+	int i, tfd[BPF_MAP_ID_MAX];
+
+	printf("ver: %d\nobj: %s\ndev: %lu\nino: %lu\nmaps: %u\n",
+	       aux->uds_ver, aux->obj_name, aux->obj_st.st_dev,
+	       aux->obj_st.st_ino, aux->num_ent);
+
+	for (i = 0; i < aux->num_ent; i++) {
+		printf("map%d:\n", i);
+		printf(" `- fd: %u\n", fds[i]);
+		printf("  | serial: %u\n", aux->ent[i].id);
+		printf("  | type: %u\n", aux->ent[i].type);
+		printf("  | max elem: %u\n", aux->ent[i].max_elem);
+		printf("  | size key: %u\n", aux->ent[i].size_key);
+		printf("  ` size val: %u\n", aux->ent[i].size_value);
+
+		tfd[aux->ent[i].id] = fds[i];
+	}
+
+	bpf_dump_map_data(tfd);
+}
+
+static void bpf_map_get_from_env(int *tfd)
+{
+	char key[64], *val;
+	int i;
+
+	for (i = 0; i < BPF_MAP_ID_MAX; i++) {
+		memset(key, 0, sizeof(key));
+		snprintf(key, sizeof(key), "BPF_MAP%d", i);
+
+		val = getenv(key);
+		assert(val != NULL);
+
+		tfd[i] = atoi(val);
+	}
+}
+
+static int bpf_map_set_recv(int fd, int *fds,  struct bpf_map_aux *aux,
+			    unsigned int entries)
+{
+	struct bpf_map_set_msg msg;
+	int *cmsg_buf, min_fd, i;
+	char *amsg_buf, *mmsg_buf;
+
+	cmsg_buf = bpf_map_set_init(&msg, NULL, 0);
+	amsg_buf = (char *)msg.aux.ent;
+	mmsg_buf = (char *)&msg.aux;
+
+	for (i = 0; i < entries; i += min_fd) {
+		struct cmsghdr *cmsg;
+		int ret;
+
+		min_fd = min(BPF_SCM_MAX_FDS * 1U, entries - i);
+
+		bpf_map_set_init_single(&msg, min_fd);
+
+		ret = recvmsg(fd, &msg.hdr, 0);
+		if (ret <= 0)
+			return ret ? : -1;
+
+		cmsg = CMSG_FIRSTHDR(&msg.hdr);
+		if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS)
+			return -EINVAL;
+		if (msg.hdr.msg_flags & MSG_CTRUNC)
+			return -EIO;
+
+		min_fd = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof(fd);
+		if (min_fd > entries || min_fd <= 0)
+			return -1;
+
+		memcpy(&fds[i], cmsg_buf, sizeof(fds[0]) * min_fd);
+		memcpy(&aux->ent[i], amsg_buf, sizeof(aux->ent[0]) * min_fd);
+		memcpy(aux, mmsg_buf, offsetof(struct bpf_map_aux, ent));
+
+		if (i + min_fd == aux->num_ent)
+			break;
+	}
+
+	return 0;
+}
+
+int main(int argc, char **argv)
+{
+	int fds[BPF_SCM_MAX_FDS];
+	struct bpf_map_aux aux;
+	struct sockaddr_un addr;
+	int fd, ret, i;
+
+	/* When arguments are being passed, we take it as a path
+	 * to a Unix domain socket, otherwise we grab the fds
+	 * from the environment to demonstrate both possibilities.
+	 */
+	if (argc == 1) {
+		int tfd[BPF_MAP_ID_MAX];
+
+		bpf_map_get_from_env(tfd);
+		bpf_dump_map_data(tfd);
+
+		return 0;
+	}
+
+	fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		fprintf(stderr, "Cannot open socket: %s\n",
+			strerror(errno));
+		exit(1);
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	strncpy(addr.sun_path, argv[argc - 1], sizeof(addr.sun_path));
+
+	ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
+	if (ret < 0) {
+		fprintf(stderr, "Cannot bind to socket: %s\n",
+			strerror(errno));
+		exit(1);
+	}
+
+	memset(fds, 0, sizeof(fds));
+	memset(&aux, 0, sizeof(aux));
+
+	ret = bpf_map_set_recv(fd, fds, &aux, BPF_SCM_MAX_FDS);
+	if (ret >= 0)
+		bpf_info_loop(fds, &aux);
+
+	for (i = 0; i < aux.num_ent; i++)
+		close(fds[i]);
+
+	close(fd);
+	return 0;
+}
diff --git a/examples/bpf/bpf_cyclic.c b/examples/bpf/bpf_cyclic.c
index 11d1c06..c66cbec 100644
--- a/examples/bpf/bpf_cyclic.c
+++ b/examples/bpf/bpf_cyclic.c
@@ -6,19 +6,14 @@
  */
 #define JMP_MAP_ID	0xabccba
 
-struct bpf_elf_map __section_maps jmp_tc = {
-	.type		= BPF_MAP_TYPE_PROG_ARRAY,
-	.id		= JMP_MAP_ID,
-	.size_key	= sizeof(uint32_t),
-	.size_value	= sizeof(uint32_t),
-	.pinning	= PIN_OBJECT_NS,
-	.max_elem	= 1,
-};
+BPF_PROG_ARRAY(jmp_tc, JMP_MAP_ID, PIN_OBJECT_NS, 1);
 
 __section_tail(JMP_MAP_ID, 0)
 int cls_loop(struct __sk_buff *skb)
 {
-	printt("cb: %u\n", skb->cb[0]++);
+	char fmt[] = "cb: %u\n";
+
+	trace_printk(fmt, sizeof(fmt), skb->cb[0]++);
 	tail_call(skb, &jmp_tc, 0);
 
 	skb->tc_classid = TC_H_MAKE(1, 42);
diff --git a/examples/bpf/bpf_graft.c b/examples/bpf/bpf_graft.c
index 07113d4..f48fd02 100644
--- a/examples/bpf/bpf_graft.c
+++ b/examples/bpf/bpf_graft.c
@@ -33,33 +33,34 @@
  *   [...]
  */
 
-struct bpf_elf_map __section_maps jmp_tc = {
-	.type		= BPF_MAP_TYPE_PROG_ARRAY,
-	.size_key	= sizeof(uint32_t),
-	.size_value	= sizeof(uint32_t),
-	.pinning	= PIN_GLOBAL_NS,
-	.max_elem	= 1,
-};
+BPF_PROG_ARRAY(jmp_tc, 0, PIN_GLOBAL_NS, 1);
 
 __section("aaa")
 int cls_aaa(struct __sk_buff *skb)
 {
-	printt("aaa\n");
+	char fmt[] = "aaa\n";
+
+	trace_printk(fmt, sizeof(fmt));
 	return TC_H_MAKE(1, 42);
 }
 
 __section("bbb")
 int cls_bbb(struct __sk_buff *skb)
 {
-	printt("bbb\n");
+	char fmt[] = "bbb\n";
+
+	trace_printk(fmt, sizeof(fmt));
 	return TC_H_MAKE(1, 43);
 }
 
 __section_cls_entry
 int cls_entry(struct __sk_buff *skb)
 {
+	char fmt[] = "fallthrough\n";
+
 	tail_call(skb, &jmp_tc, 0);
-	printt("fallthrough\n");
+	trace_printk(fmt, sizeof(fmt));
+
 	return BPF_H_DEFAULT;
 }
 
diff --git a/examples/bpf/bpf_map_in_map.c b/examples/bpf/bpf_map_in_map.c
deleted file mode 100644
index ff0e623..0000000
--- a/examples/bpf/bpf_map_in_map.c
+++ /dev/null
@@ -1,56 +0,0 @@
-#include "../../include/bpf_api.h"
-
-#define MAP_INNER_ID	42
-
-struct bpf_elf_map __section_maps map_inner = {
-	.type		= BPF_MAP_TYPE_ARRAY,
-	.size_key	= sizeof(uint32_t),
-	.size_value	= sizeof(uint32_t),
-	.id		= MAP_INNER_ID,
-	.inner_idx	= 0,
-	.pinning	= PIN_GLOBAL_NS,
-	.max_elem	= 1,
-};
-
-struct bpf_elf_map __section_maps map_outer = {
-	.type		= BPF_MAP_TYPE_ARRAY_OF_MAPS,
-	.size_key	= sizeof(uint32_t),
-	.size_value	= sizeof(uint32_t),
-	.inner_id	= MAP_INNER_ID,
-	.pinning	= PIN_GLOBAL_NS,
-	.max_elem	= 1,
-};
-
-__section("egress")
-int emain(struct __sk_buff *skb)
-{
-	struct bpf_elf_map *map_inner;
-	int key = 0, *val;
-
-	map_inner = map_lookup_elem(&map_outer, &key);
-	if (map_inner) {
-		val = map_lookup_elem(map_inner, &key);
-		if (val)
-			lock_xadd(val, 1);
-	}
-
-	return BPF_H_DEFAULT;
-}
-
-__section("ingress")
-int imain(struct __sk_buff *skb)
-{
-	struct bpf_elf_map *map_inner;
-	int key = 0, *val;
-
-	map_inner = map_lookup_elem(&map_outer, &key);
-	if (map_inner) {
-		val = map_lookup_elem(map_inner, &key);
-		if (val)
-			printt("map val: %d\n", *val);
-	}
-
-	return BPF_H_DEFAULT;
-}
-
-BPF_LICENSE("GPL");
diff --git a/examples/bpf/bpf_prog.c b/examples/bpf/bpf_prog.c
new file mode 100644
index 0000000..4728049
--- /dev/null
+++ b/examples/bpf/bpf_prog.c
@@ -0,0 +1,499 @@
+/*
+ * eBPF kernel space program part
+ *
+ * Toy eBPF program for demonstration purposes, some parts derived from
+ * kernel tree's samples/bpf/sockex2_kern.c example.
+ *
+ * More background on eBPF, kernel tree: Documentation/networking/filter.txt
+ *
+ * Note, this file is rather large, and most classifier and actions are
+ * likely smaller to accomplish one specific use-case and are tailored
+ * for high performance. For performance reasons, you might also have the
+ * classifier and action already merged inside the classifier.
+ *
+ * In order to show various features it serves as a bigger programming
+ * example, which you should feel free to rip apart and experiment with.
+ *
+ * Compilation, configuration example:
+ *
+ *  Note: as long as the BPF backend in LLVM is still experimental,
+ *  you need to build LLVM with LLVM with --enable-experimental-targets=BPF
+ *  Also, make sure your 4.1+ kernel is compiled with CONFIG_BPF_SYSCALL=y,
+ *  and you have libelf.h and gelf.h headers and can link tc against -lelf.
+ *
+ *  In case you need to sync kernel headers, go to your kernel source tree:
+ *  # make headers_install INSTALL_HDR_PATH=/usr/
+ *
+ *  $ export PATH=/home/<...>/llvm/Debug+Asserts/bin/:$PATH
+ *  $ clang -O2 -emit-llvm -c bpf_prog.c -o - | llc -march=bpf -filetype=obj -o bpf.o
+ *  $ objdump -h bpf.o
+ *  [...]
+ *  3 classifier    000007f8  0000000000000000  0000000000000000  00000040  2**3
+ *                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ *  4 action-mark   00000088  0000000000000000  0000000000000000  00000838  2**3
+ *                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ *  5 action-rand   00000098  0000000000000000  0000000000000000  000008c0  2**3
+ *                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ *  6 maps          00000030  0000000000000000  0000000000000000  00000958  2**2
+ *                  CONTENTS, ALLOC, LOAD, DATA
+ *  7 license       00000004  0000000000000000  0000000000000000  00000988  2**0
+ *                  CONTENTS, ALLOC, LOAD, DATA
+ *  [...]
+ *  # echo 1 > /proc/sys/net/core/bpf_jit_enable
+ *  $ gcc bpf_agent.c -o bpf_agent -Wall -O2
+ *  # ./bpf_agent /tmp/bpf-uds      (e.g. on a different terminal)
+ *  # tc filter add dev em1 parent 1: bpf obj bpf.o exp /tmp/bpf-uds flowid 1:1 \
+ *                             action bpf obj bpf.o sec action-mark            \
+ *                             action bpf obj bpf.o sec action-rand ok
+ *  # tc filter show dev em1
+ *  filter parent 1: protocol all pref 49152 bpf
+ *  filter parent 1: protocol all pref 49152 bpf handle 0x1 flowid 1:1 bpf.o:[classifier]
+ *    action order 1: bpf bpf.o:[action-mark] default-action pipe
+ *    index 52 ref 1 bind 1
+ *
+ *    action order 2: bpf bpf.o:[action-rand] default-action pipe
+ *    index 53 ref 1 bind 1
+ *
+ *    action order 3: gact action pass
+ *    random type none pass val 0
+ *    index 38 ref 1 bind 1
+ *
+ * The same program can also be installed on ingress side (as opposed to above
+ * egress configuration), e.g.:
+ *
+ * # tc qdisc add dev em1 handle ffff: ingress
+ * # tc filter add dev em1 parent ffff: bpf obj ...
+ *
+ * Notes on BPF agent:
+ *
+ * In the above example, the bpf_agent creates the unix domain socket
+ * natively. "tc exec" can also spawn a shell and hold the socktes there:
+ *
+ *  # tc exec bpf imp /tmp/bpf-uds
+ *  # tc filter add dev em1 parent 1: bpf obj bpf.o exp /tmp/bpf-uds flowid 1:1 \
+ *                             action bpf obj bpf.o sec action-mark            \
+ *                             action bpf obj bpf.o sec action-rand ok
+ *  sh-4.2# (shell spawned from tc exec)
+ *  sh-4.2# bpf_agent
+ *  [...]
+ *
+ * This will read out fds over environment and produce the same data dump
+ * as below. This has the advantage that the spawned shell owns the fds
+ * and thus if the agent is restarted, it can reattach to the same fds, also
+ * various programs can easily read/modify the data simultaneously from user
+ * space side.
+ *
+ * If the shell is unnecessary, the agent can also just be spawned directly
+ * via tc exec:
+ *
+ *  # tc exec bpf imp /tmp/bpf-uds run bpf_agent
+ *  # tc filter add dev em1 parent 1: bpf obj bpf.o exp /tmp/bpf-uds flowid 1:1 \
+ *                             action bpf obj bpf.o sec action-mark            \
+ *                             action bpf obj bpf.o sec action-rand ok
+ *
+ * BPF agent example output:
+ *
+ * ver: 1
+ * obj: bpf.o
+ * dev: 64770
+ * ino: 6045133
+ * maps: 3
+ * map0:
+ *  `- fd: 4
+ *   | serial: 1
+ *   | type: 1
+ *   | max elem: 256
+ *   | size key: 1
+ *   ` size val: 16
+ * map1:
+ *  `- fd: 5
+ *   | serial: 2
+ *   | type: 1
+ *   | max elem: 1024
+ *   | size key: 4
+ *   ` size val: 16
+ * map2:
+ *  `- fd: 6
+ *   | serial: 3
+ *   | type: 2
+ *   | max elem: 64
+ *   | size key: 4
+ *   ` size val: 8
+ * data, period: 5sec
+ *  `- number of drops:	cpu0:     0	cpu1:     0	cpu2:     0	cpu3:     0
+ *   | nic queues:	q0:[pkts: 0, mis: 0]	q1:[pkts: 0, mis: 0]	q2:[pkts: 0, mis: 0]	q3:[pkts: 0, mis: 0]
+ *   ` protos:	tcp:[pkts: 0, bytes: 0]	udp:[pkts: 0, bytes: 0]	icmp:[pkts: 0, bytes: 0]
+ * data, period: 5sec
+ *  `- number of drops:	cpu0:     5	cpu1:     0	cpu2:     0	cpu3:     1
+ *   | nic queues:	q0:[pkts: 0, mis: 0]	q1:[pkts: 0, mis: 0]	q2:[pkts: 24, mis: 14]	q3:[pkts: 0, mis: 0]
+ *   ` protos:	tcp:[pkts: 13, bytes: 1989]	udp:[pkts: 10, bytes: 710]	icmp:[pkts: 0, bytes: 0]
+ * data, period: 5sec
+ *  `- number of drops:	cpu0:     5	cpu1:     0	cpu2:     3	cpu3:     3
+ *   | nic queues:	q0:[pkts: 0, mis: 0]	q1:[pkts: 0, mis: 0]	q2:[pkts: 39, mis: 21]	q3:[pkts: 0, mis: 0]
+ *   ` protos:	tcp:[pkts: 20, bytes: 3549]	udp:[pkts: 18, bytes: 1278]	icmp:[pkts: 0, bytes: 0]
+ * [...]
+ *
+ * This now means, the below classifier and action pipeline has been loaded
+ * as eBPF bytecode into the kernel, the kernel has verified that the
+ * execution of the bytecode is "safe", and it has JITed the programs
+ * afterwards, so that upon invocation they're running on native speed. tc
+ * has transferred all map file descriptors to the bpf_agent via IPC and
+ * even after tc exits, the agent can read out or modify all map data.
+ *
+ * Note that the export to the uds is done only once in the classifier and
+ * not in the action. It's enough to export the (here) shared descriptors
+ * once.
+ *
+ * If you need to disassemble the generated JIT image (echo with 2), the
+ * kernel tree has under tools/net/ a small helper, you can invoke e.g.
+ * `bpf_jit_disasm -o`.
+ *
+ * Please find in the code below further comments.
+ *
+ *   -- Happy eBPF hacking! ;)
+ */
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <asm/types.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/if_tunnel.h>
+#include <linux/filter.h>
+#include <linux/bpf.h>
+
+/* Common, shared definitions with ebpf_agent.c. */
+#include "bpf_shared.h"
+/* BPF helper functions for our example. */
+#include "../../include/bpf_api.h"
+
+/* Could be defined here as well, or included from the header. */
+#define TC_ACT_UNSPEC		(-1)
+#define TC_ACT_OK		0
+#define TC_ACT_RECLASSIFY	1
+#define TC_ACT_SHOT		2
+#define TC_ACT_PIPE		3
+#define TC_ACT_STOLEN		4
+#define TC_ACT_QUEUED		5
+#define TC_ACT_REPEAT		6
+
+/* Other, misc stuff. */
+#define IP_MF			0x2000
+#define IP_OFFSET		0x1FFF
+
+/* eBPF map definitions, all placed in section "maps". */
+struct bpf_elf_map __section("maps") map_proto = {
+	.type		=	BPF_MAP_TYPE_HASH,
+	.id		=	BPF_MAP_ID_PROTO,
+	.size_key	=	sizeof(uint8_t),
+	.size_value	=	sizeof(struct count_tuple),
+	.max_elem	=	256,
+};
+
+struct bpf_elf_map __section("maps") map_queue = {
+	.type		=	BPF_MAP_TYPE_HASH,
+	.id		=	BPF_MAP_ID_QUEUE,
+	.size_key	=	sizeof(uint32_t),
+	.size_value	=	sizeof(struct count_queue),
+	.max_elem	=	1024,
+};
+
+struct bpf_elf_map __section("maps") map_drops = {
+	.type		=	BPF_MAP_TYPE_ARRAY,
+	.id		=	BPF_MAP_ID_DROPS,
+	.size_key	=	sizeof(uint32_t),
+	.size_value	=	sizeof(long),
+	.max_elem	=	64,
+};
+
+/* Helper functions and definitions for the flow dissector used by the
+ * example classifier. This resembles the kernel's flow dissector to
+ * some extend and is just used as an example to show what's possible
+ * with eBPF.
+ */
+struct sockaddr;
+
+struct vlan_hdr {
+	__be16 h_vlan_TCI;
+	__be16 h_vlan_encapsulated_proto;
+};
+
+struct flow_keys {
+	__u32 src;
+	__u32 dst;
+	union {
+		__u32 ports;
+		__u16 port16[2];
+	};
+	__s32 th_off;
+	__u8 ip_proto;
+};
+
+static inline int flow_ports_offset(__u8 ip_proto)
+{
+	switch (ip_proto) {
+	case IPPROTO_TCP:
+	case IPPROTO_UDP:
+	case IPPROTO_DCCP:
+	case IPPROTO_ESP:
+	case IPPROTO_SCTP:
+	case IPPROTO_UDPLITE:
+	default:
+		return 0;
+	case IPPROTO_AH:
+		return 4;
+	}
+}
+
+static inline bool flow_is_frag(struct __sk_buff *skb, int nh_off)
+{
+	return !!(load_half(skb, nh_off + offsetof(struct iphdr, frag_off)) &
+		  (IP_MF | IP_OFFSET));
+}
+
+static inline int flow_parse_ipv4(struct __sk_buff *skb, int nh_off,
+				  __u8 *ip_proto, struct flow_keys *flow)
+{
+	__u8 ip_ver_len;
+
+	if (unlikely(flow_is_frag(skb, nh_off)))
+		*ip_proto = 0;
+	else
+		*ip_proto = load_byte(skb, nh_off + offsetof(struct iphdr,
+							     protocol));
+	if (*ip_proto != IPPROTO_GRE) {
+		flow->src = load_word(skb, nh_off + offsetof(struct iphdr, saddr));
+		flow->dst = load_word(skb, nh_off + offsetof(struct iphdr, daddr));
+	}
+
+	ip_ver_len = load_byte(skb, nh_off + 0 /* offsetof(struct iphdr, ihl) */);
+	if (likely(ip_ver_len == 0x45))
+		nh_off += 20;
+	else
+		nh_off += (ip_ver_len & 0xF) << 2;
+
+	return nh_off;
+}
+
+static inline __u32 flow_addr_hash_ipv6(struct __sk_buff *skb, int off)
+{
+	__u32 w0 = load_word(skb, off);
+	__u32 w1 = load_word(skb, off + sizeof(w0));
+	__u32 w2 = load_word(skb, off + sizeof(w0) * 2);
+	__u32 w3 = load_word(skb, off + sizeof(w0) * 3);
+
+	return w0 ^ w1 ^ w2 ^ w3;
+}
+
+static inline int flow_parse_ipv6(struct __sk_buff *skb, int nh_off,
+				  __u8 *ip_proto, struct flow_keys *flow)
+{
+	*ip_proto = load_byte(skb, nh_off + offsetof(struct ipv6hdr, nexthdr));
+
+	flow->src = flow_addr_hash_ipv6(skb, nh_off + offsetof(struct ipv6hdr, saddr));
+	flow->dst = flow_addr_hash_ipv6(skb, nh_off + offsetof(struct ipv6hdr, daddr));
+
+	return nh_off + sizeof(struct ipv6hdr);
+}
+
+static inline bool flow_dissector(struct __sk_buff *skb,
+				  struct flow_keys *flow)
+{
+	int poff, nh_off = BPF_LL_OFF + ETH_HLEN;
+	__be16 proto = skb->protocol;
+	__u8 ip_proto;
+
+	/* TODO: check for skb->vlan_tci, skb->vlan_proto first */
+	if (proto == htons(ETH_P_8021AD)) {
+		proto = load_half(skb, nh_off +
+				  offsetof(struct vlan_hdr, h_vlan_encapsulated_proto));
+		nh_off += sizeof(struct vlan_hdr);
+	}
+	if (proto == htons(ETH_P_8021Q)) {
+		proto = load_half(skb, nh_off +
+				  offsetof(struct vlan_hdr, h_vlan_encapsulated_proto));
+		nh_off += sizeof(struct vlan_hdr);
+	}
+
+	if (likely(proto == htons(ETH_P_IP)))
+		nh_off = flow_parse_ipv4(skb, nh_off, &ip_proto, flow);
+	else if (proto == htons(ETH_P_IPV6))
+		nh_off = flow_parse_ipv6(skb, nh_off, &ip_proto, flow);
+	else
+		return false;
+
+	switch (ip_proto) {
+	case IPPROTO_GRE: {
+		struct gre_hdr {
+			__be16 flags;
+			__be16 proto;
+		};
+
+		__u16 gre_flags = load_half(skb, nh_off +
+					    offsetof(struct gre_hdr, flags));
+		__u16 gre_proto = load_half(skb, nh_off +
+					    offsetof(struct gre_hdr, proto));
+
+		if (gre_flags & (GRE_VERSION | GRE_ROUTING))
+			break;
+
+		nh_off += 4;
+		if (gre_flags & GRE_CSUM)
+			nh_off += 4;
+		if (gre_flags & GRE_KEY)
+			nh_off += 4;
+		if (gre_flags & GRE_SEQ)
+			nh_off += 4;
+
+		if (gre_proto == ETH_P_8021Q) {
+			gre_proto = load_half(skb, nh_off +
+					      offsetof(struct vlan_hdr,
+						       h_vlan_encapsulated_proto));
+			nh_off += sizeof(struct vlan_hdr);
+		}
+		if (gre_proto == ETH_P_IP)
+			nh_off = flow_parse_ipv4(skb, nh_off, &ip_proto, flow);
+		else if (gre_proto == ETH_P_IPV6)
+			nh_off = flow_parse_ipv6(skb, nh_off, &ip_proto, flow);
+		else
+			return false;
+		break;
+	}
+	case IPPROTO_IPIP:
+		nh_off = flow_parse_ipv4(skb, nh_off, &ip_proto, flow);
+		break;
+	case IPPROTO_IPV6:
+		nh_off = flow_parse_ipv6(skb, nh_off, &ip_proto, flow);
+	default:
+		break;
+	}
+
+	nh_off += flow_ports_offset(ip_proto);
+
+	flow->ports = load_word(skb, nh_off);
+	flow->th_off = nh_off;
+	flow->ip_proto = ip_proto;
+
+	return true;
+}
+
+static inline void cls_update_proto_map(const struct __sk_buff *skb,
+					const struct flow_keys *flow)
+{
+	uint8_t proto = flow->ip_proto;
+	struct count_tuple *ct, _ct;
+
+	ct = map_lookup_elem(&map_proto, &proto);
+	if (likely(ct)) {
+		lock_xadd(&ct->packets, 1);
+		lock_xadd(&ct->bytes, skb->len);
+		return;
+	}
+
+	/* No hit yet, we need to create a new entry. */
+	_ct.packets = 1;
+	_ct.bytes = skb->len;
+
+	map_update_elem(&map_proto, &proto, &_ct, BPF_ANY);
+}
+
+static inline void cls_update_queue_map(const struct __sk_buff *skb)
+{
+	uint32_t queue = skb->queue_mapping;
+	struct count_queue *cq, _cq;
+	bool mismatch;
+
+	mismatch = skb->queue_mapping != get_smp_processor_id();
+
+	cq = map_lookup_elem(&map_queue, &queue);
+	if (likely(cq)) {
+		lock_xadd(&cq->total, 1);
+		if (mismatch)
+			lock_xadd(&cq->mismatch, 1);
+		return;
+	}
+
+	/* No hit yet, we need to create a new entry. */
+	_cq.total = 1;
+	_cq.mismatch = mismatch ? 1 : 0;
+
+	map_update_elem(&map_queue, &queue, &_cq, BPF_ANY);
+}
+
+/* eBPF program definitions, placed in various sections, which can
+ * have custom section names. If custom names are in use, it's
+ * required to point tc to the correct section, e.g.
+ *
+ *     tc filter add [...] bpf obj cls.o sec cls-tos [...]
+ *
+ * in case the program resides in __section("cls-tos").
+ *
+ * Default section for cls_bpf is: "classifier", for act_bpf is:
+ * "action". Naturally, if for example multiple actions are present
+ * in the same file, they need to have distinct section names.
+ *
+ * It is however not required to have multiple programs sharing
+ * a file.
+ */
+__section("classifier")
+int cls_main(struct __sk_buff *skb)
+{
+	struct flow_keys flow;
+
+	if (!flow_dissector(skb, &flow))
+		return 0; /* No match in cls_bpf. */
+
+	cls_update_proto_map(skb, &flow);
+	cls_update_queue_map(skb);
+
+	return flow.ip_proto;
+}
+
+static inline void act_update_drop_map(void)
+{
+	uint32_t *count, cpu = get_smp_processor_id();
+
+	count = map_lookup_elem(&map_drops, &cpu);
+	if (count)
+		/* Only this cpu is accessing this element. */
+		(*count)++;
+}
+
+__section("action-mark")
+int act_mark_main(struct __sk_buff *skb)
+{
+	/* You could also mangle skb data here with the helper function
+	 * BPF_FUNC_skb_store_bytes, etc. Or, alternatively you could
+	 * do that already in the classifier itself as a merged combination
+	 * of classifier'n'action model.
+	 */
+
+	if (skb->mark == 0xcafe) {
+		act_update_drop_map();
+		return TC_ACT_SHOT;
+	}
+
+	/* Default configured tc opcode. */
+	return TC_ACT_UNSPEC;
+}
+
+__section("action-rand")
+int act_rand_main(struct __sk_buff *skb)
+{
+	/* Sorry, we're near event horizon ... */
+	if ((get_prandom_u32() & 3) == 0) {
+		act_update_drop_map();
+		return TC_ACT_SHOT;
+	}
+
+	return TC_ACT_UNSPEC;
+}
+
+/* Last but not least, the file contains a license. Some future helper
+ * functions may only be available with a GPL license.
+ */
+BPF_LICENSE("GPL");
diff --git a/examples/bpf/bpf_shared.c b/examples/bpf/bpf_shared.c
index 21fe6f1..accc0ad 100644
--- a/examples/bpf/bpf_shared.c
+++ b/examples/bpf/bpf_shared.c
@@ -18,13 +18,7 @@
  * instance is being created.
  */
 
-struct bpf_elf_map __section_maps map_sh = {
-	.type		= BPF_MAP_TYPE_ARRAY,
-	.size_key	= sizeof(uint32_t),
-	.size_value	= sizeof(uint32_t),
-	.pinning	= PIN_OBJECT_NS, /* or PIN_GLOBAL_NS, or PIN_NONE */
-	.max_elem	= 1,
-};
+BPF_ARRAY4(map_sh, 0, PIN_OBJECT_NS, 1); /* or PIN_GLOBAL_NS, or PIN_NONE */
 
 __section("egress")
 int emain(struct __sk_buff *skb)
@@ -41,11 +35,12 @@
 __section("ingress")
 int imain(struct __sk_buff *skb)
 {
+	char fmt[] = "map val: %d\n";
 	int key = 0, *val;
 
 	val = map_lookup_elem(&map_sh, &key);
 	if (val)
-		printt("map val: %d\n", *val);
+		trace_printk(fmt, sizeof(fmt), *val);
 
 	return BPF_H_DEFAULT;
 }
diff --git a/examples/bpf/bpf_shared.h b/examples/bpf/bpf_shared.h
new file mode 100644
index 0000000..a24038d
--- /dev/null
+++ b/examples/bpf/bpf_shared.h
@@ -0,0 +1,22 @@
+#ifndef __BPF_SHARED__
+#define __BPF_SHARED__
+
+enum {
+	BPF_MAP_ID_PROTO,
+	BPF_MAP_ID_QUEUE,
+	BPF_MAP_ID_DROPS,
+	__BPF_MAP_ID_MAX,
+#define BPF_MAP_ID_MAX	__BPF_MAP_ID_MAX
+};
+
+struct count_tuple {
+	long packets; /* type long for lock_xadd() */
+	long bytes;
+};
+
+struct count_queue {
+	long total;
+	long mismatch;
+};
+
+#endif /* __BPF_SHARED__ */
diff --git a/examples/bpf/bpf_sys.h b/examples/bpf/bpf_sys.h
new file mode 100644
index 0000000..6e4f09e
--- /dev/null
+++ b/examples/bpf/bpf_sys.h
@@ -0,0 +1,23 @@
+#ifndef __BPF_SYS__
+#define __BPF_SYS__
+
+#include <sys/syscall.h>
+#include <linux/bpf.h>
+
+static inline __u64 bpf_ptr_to_u64(const void *ptr)
+{
+	return (__u64) (unsigned long) ptr;
+}
+
+static inline int bpf_lookup_elem(int fd, void *key, void *value)
+{
+	union bpf_attr attr = {
+		.map_fd		= fd,
+		.key		= bpf_ptr_to_u64(key),
+		.value		= bpf_ptr_to_u64(value),
+	};
+
+	return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
+}
+
+#endif /* __BPF_SYS__ */
diff --git a/examples/bpf/bpf_tailcall.c b/examples/bpf/bpf_tailcall.c
index 161eb60..040790d 100644
--- a/examples/bpf/bpf_tailcall.c
+++ b/examples/bpf/bpf_tailcall.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #include "../../include/bpf_api.h"
 
 #define ENTRY_INIT	3
@@ -27,40 +26,20 @@
  * classifier behaviour.
  */
 
-struct bpf_elf_map __section_maps jmp_tc = {
-	.type		= BPF_MAP_TYPE_PROG_ARRAY,
-	.id		= FOO,
-	.size_key	= sizeof(uint32_t),
-	.size_value	= sizeof(uint32_t),
-	.pinning	= PIN_OBJECT_NS,
-	.max_elem	= MAX_JMP_SIZE,
-};
+BPF_PROG_ARRAY(jmp_tc, FOO, PIN_OBJECT_NS, MAX_JMP_SIZE);
+BPF_PROG_ARRAY(jmp_ex, BAR, PIN_OBJECT_NS, 1);
 
-struct bpf_elf_map __section_maps jmp_ex = {
-	.type		= BPF_MAP_TYPE_PROG_ARRAY,
-	.id		= BAR,
-	.size_key	= sizeof(uint32_t),
-	.size_value	= sizeof(uint32_t),
-	.pinning	= PIN_OBJECT_NS,
-	.max_elem	= 1,
-};
-
-struct bpf_elf_map __section_maps map_sh = {
-	.type		= BPF_MAP_TYPE_ARRAY,
-	.size_key	= sizeof(uint32_t),
-	.size_value	= sizeof(uint32_t),
-	.pinning	= PIN_OBJECT_NS,
-	.max_elem	= 1,
-};
+BPF_ARRAY4(map_sh, 0, PIN_OBJECT_NS, 1);
 
 __section_tail(FOO, ENTRY_0)
 int cls_case1(struct __sk_buff *skb)
 {
+	char fmt[] = "case1: map-val: %d from:%u\n";
 	int key = 0, *val;
 
 	val = map_lookup_elem(&map_sh, &key);
 	if (val)
-		printt("case1: map-val: %d from:%u\n", *val, skb->cb[0]);
+		trace_printk(fmt, sizeof(fmt), *val, skb->cb[0]);
 
 	skb->cb[0] = ENTRY_0;
 	tail_call(skb, &jmp_ex, ENTRY_0);
@@ -71,11 +50,12 @@
 __section_tail(FOO, ENTRY_1)
 int cls_case2(struct __sk_buff *skb)
 {
+	char fmt[] = "case2: map-val: %d from:%u\n";
 	int key = 0, *val;
 
 	val = map_lookup_elem(&map_sh, &key);
 	if (val)
-		printt("case2: map-val: %d from:%u\n", *val, skb->cb[0]);
+		trace_printk(fmt, sizeof(fmt), *val, skb->cb[0]);
 
 	skb->cb[0] = ENTRY_1;
 	tail_call(skb, &jmp_tc, ENTRY_0);
@@ -86,11 +66,12 @@
 __section_tail(BAR, ENTRY_0)
 int cls_exit(struct __sk_buff *skb)
 {
+	char fmt[] = "exit: map-val: %d from:%u\n";
 	int key = 0, *val;
 
 	val = map_lookup_elem(&map_sh, &key);
 	if (val)
-		printt("exit: map-val: %d from:%u\n", *val, skb->cb[0]);
+		trace_printk(fmt, sizeof(fmt), *val, skb->cb[0]);
 
 	/* Termination point. */
 	return BPF_H_DEFAULT;
@@ -99,6 +80,7 @@
 __section_cls_entry
 int cls_entry(struct __sk_buff *skb)
 {
+	char fmt[] = "fallthrough\n";
 	int key = 0, *val;
 
 	/* For transferring state, we can use skb->cb[0] ... skb->cb[4]. */
@@ -110,7 +92,7 @@
 		tail_call(skb, &jmp_tc, skb->hash & (MAX_JMP_SIZE - 1));
 	}
 
-	printt("fallthrough\n");
+	trace_printk(fmt, sizeof(fmt));
 	return BPF_H_DEFAULT;
 }
 
diff --git a/examples/cbq.init-v0.7.3 b/examples/cbq.init-v0.7.3
new file mode 100644
index 0000000..1bc0d44
--- /dev/null
+++ b/examples/cbq.init-v0.7.3
@@ -0,0 +1,983 @@
+#!/bin/bash
+#
+#    cbq.init v0.7.3
+#    Copyright (C) 1999  Pavel Golubev <pg@ksi-linux.com>
+#    Copyright (C) 2001-2004  Lubomir Bulej <pallas@kadan.cz>
+#
+#    chkconfig:   2345 11 89
+#    description: sets up CBQ-based traffic control
+#
+#    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, see <http://www.gnu.org/licenses/>.
+#
+#    To get the latest version, check on Freshmeat for actual location:
+#
+#		http://freshmeat.net/projects/cbq.init
+#
+#
+# VERSION HISTORY
+# ---------------
+# v0.7.3- Deepak Singhal <singhal at users.sourceforge.net>
+#	  - fix timecheck to not ignore regular TIME rules after
+#	    encountering a TIME rule that spans over midnight
+#	- Nathan Shafer <nicodemus at users.sourceforge.net>
+#	  - allow symlinks to class files
+#	- Seth J. Blank <antifreeze at users.sourceforge.net>
+#	  - replace hardcoded ip/tc location with variables
+#	- Mark Davis <mark.davis at gmx.de>
+#	  - allow setting of PRIO_{MARK,RULE,REALM} in class file
+#	- Fernando Sanch <toptnc at users.sourceforge.net>
+#	  - allow underscores in interface names
+# v0.7.2- Paulo Sedrez
+#	  - fix time2abs to allow hours with leading zero in TIME rules
+#	- Svetlin Simeonov <zvero at yahoo.com>
+#	  - fix cbq_device_list to allow VLAN interfaces
+#	- Mark Davis <mark.davis at gmx.de>
+#	  - ignore *~ backup files when looking for classes
+#	- Mike Boyer <boyer at administrative.com>
+#	  - fix to allow arguments to be passed to "restart" command
+# v0.7.1- Lubomir Bulej <pallas at kadan.cz>
+#	  - default value for PERTURB
+#	  - fixed small bug in RULE parser to correctly parse rules with
+#	    identical source and destination fields
+#	  - faster initial scanning of DEVICE fields
+# v0.7	- Lubomir Bulej <pallas at kadan.cz>
+#	  - lots of various cleanups and reorganizations; the parsing is now
+#	    some 40% faster, but the class ID must be in range 0x0002-0xffff
+#	    (again). Because of the number of internal changes and the above
+#	    class ID restriction, I bumped the version to 0.7 to indicate
+#	    something might have got broken :)
+#	  - changed PRIO_{U32,FW,ROUTE} to PRIO_{RULE,MARK,REALM}
+#	    for consistency with filter keywords
+#	  - exposed "compile" command
+#	- Catalin Petrescu <taz at dntis.ro>
+#	  - support for port masks in RULE (u32) filter
+#	- Jordan Vrtanoski <obeliks at mt.net.mk>
+#	  - support for week days in TIME rules
+# v0.6.4- Lubomir Bulej <pallas at kadan.cz>
+#	  - added PRIO_* variables to allow easy control of filter priorities
+#	  - added caching to speed up CBQ start, the cache is invalidated
+#	    whenever any of the configuration files changes
+#	  - updated the readme section + some cosmetic fixes
+# v0.6.3- Lubomir Bulej <pallas at kadan.cz>
+#	  - removed setup of (unnecessary) class 1:1 - all classes
+#	    now use qdisc's default class 1:0 as their parent
+#	  - minor fix in the timecheck branch - classes
+#	    without leaf qdisc were not updated
+#	  - minor fix to avoid timecheck failure when run
+#	    at time with minutes equal to 08 or 09
+#	  - respect CBQ_PATH setting in environment
+#	  - made PRIO=5 default, rendering it optional in configs
+#	  - added support for route filter, see notes about REALM keyword
+#	  - added support for fw filter, see notes about MARK keyword
+#	  - added filter display to "list" and "stats" commands
+#	  - readme section update + various cosmetic fixes
+# v0.6.2- Catalin Petrescu <taz at dntis.ro>
+#	  - added tunnels interface handling
+# v0.6.1- Pavel Golubev <pg at ksi-linux.com>
+#	  - added sch_prio module loading
+#	    (thanks johan at iglo.virtual.or.id for reminding)
+#	  - resolved errors resulting from stricter syntax checking in bash2
+#	- Lubomir Bulej <pallas at kadan.cz>
+#	  - various cosmetic fixes
+# v0.6	- Lubomir Bulej <pallas at kadan.cz>
+#	  - attempt to limit number of spawned processes by utilizing
+#	    more of sed power (use sed instead of grep+cut)
+#	  - simplified TIME parser, using bash builtins
+#	  - added initial support for SFQ as leaf qdisc
+#	  - reworked the documentation part a little
+#	  - incorporated pending patches and ideas submitted by
+#	    following people for versions 0.3 into version 0.6
+#	- Miguel Freitas <miguel at cetuc.puc-rio.br>
+#	  - in case of overlapping TIME parameters, the last match is taken
+#	- Juanjo Ciarlante <jjo at mendoza.gov.ar>
+#	  - chkconfig tags, list + stats startup parameters
+#	  - optional tc & ip command logging (into /var/run/cbq-*)
+#	- Rafal Maszkowski <rzm at icm.edu.pl>
+#	  - PEAK parameter for setting TBF's burst peak rate
+#	  - fix for many config files (use find instead of ls)
+# v0.5.1- Lubomir Bulej <pallas at kadan.cz>
+#	  - fixed little but serious bug in RULE parser
+# v0.5	- Lubomir Bulej <pallas at kadan.cz>
+#	  - added options PARENT, LEAF, ISOLATED and BOUNDED. This allows
+#	    (with some attention to config file ordering) for creating
+#	    hierarchical structures of shapers with classes able (or unable)
+#	    to borrow bandwidth from their parents.
+#	  - class ID check allows hexadecimal numbers
+#	  - rewritten & simplified RULE parser
+#	  - cosmetic changes to improve readability
+#	  - reorganization to avoid duplicate code (timecheck etc.)
+#	  - timecheck doesn't check classes without TIME fields anymore
+# v0.4  - Lubomir Bulej <pallas at kadan.cz>
+#	  - small bugfix in RULE parsing code
+#	  - simplified configuration parsing code
+#	  - several small cosmetic changes
+#	  - TIME parameter can be now specified more than once allowing you to
+#	    differentiate RATE throughout the whole day. Time overlapping is
+#	    not checked, first match is taken. Midnight wrap (eg. 20:00-6:00)
+#	    is allowed and taken care of.
+# v0.3a4- fixed small bug in IF operator. Thanks to
+#	  Rafal Maszkowski <rzm at icm.edu.pl>
+# v0.3a3- fixed grep bug when using more than 10 eth devices. Thanks to David
+#	  Trcka <trcka at poda.cz>.
+# v0.3a2- fixed bug in "if" operator. Thanks kad at dgtu.donetsk.ua.
+# v0.3a - added TIME parameter. Example: TIME=00:00-19:00;64Kbit/6Kbit
+#	  So, between 00:00 and 19:00 the RATE will be 64Kbit.
+#	  Just start "cbq.init timecheck" periodically from cron
+#	  (every 10 minutes for example). DON'T FORGET though, to run
+#	  "cbq.init start" for CBQ to initialize.
+# v0.2  - Some cosmetic changes. Now it is more compatible with old bash
+#	  version. Thanks to Stanislav V. Voronyi <stas at cnti.uanet.kharkov.ua>.
+# v0.1  - First public release
+#
+#
+# README
+# ------
+#
+# First of all - this is just a SIMPLE EXAMPLE of CBQ power.
+# Don't ask me "why" and "how" :)
+#
+# This script is meant to simplify setup and management of relatively simple
+# CBQ-based traffic control on Linux. Access to advanced networking features
+# of Linux kernel is provided by "ip" and "tc" utilities from A. Kuznetsov's
+# iproute2 package, available at ftp://ftp.inr.ac.ru/ip-routing. Because the
+# utilities serve primarily to translate user wishes to RTNETLINK commands,
+# their interface is rather spartan, intolerant and requires quite a lot of
+# typing. And typing is what this script attempts to reduce :)
+#
+# The advanced networking stuff in Linux is pretty flexible and this script
+# aims to bring some of its features to the not-so-hard-core Linux users. Of
+# course, there is a tradeoff between simplicity and flexibility and you may
+# realize that the flexibility suffered too much for your needs -- time to
+# face "ip" and "tc" interface.
+#
+# To speed up the "start" command, simple caching was introduced in version
+# 0.6.4. The caching works so that the sequence of "tc" commands for given
+# configuration is stored in a file (/var/cache/cbq.init by default) which
+# is used next time the "start" command is run to avoid repeated parsing of
+# configuration files. This cache is invalidated whenever any of the CBQ
+# configuration files changes. If you want to run "cbq.init start" without
+# caching, run it as "cbq.init start nocache". If you want to force cache
+# invalidation, run it as "cbq.init start invalidate". Caching is disabled
+# if you have logging enabled (ie. CBQ_DEBUG is not empty).
+#
+# If you only want cqb.init to translate your configuration to "tc" commands,
+# use "compile" command which will output "tc" commands required to build
+# your configuration. Bear in mind that "compile" does not check if the "tc"
+# commands were successful - this is done (in certain places) only when the
+# "start nocache" command is used, which is also useful when creating the
+# configuration to check whether it is completely valid.
+#
+# All CBQ parameters are valid for Ethernet interfaces only, The script was
+# tested on various Linux kernel versions from series 2.1 to 2.4 and several
+# distributions with KSI Linux (Nostromo version) as the premier one.
+#
+#
+# HOW DOES IT WORK?
+# -----------------
+#
+# Every traffic class must be described by a file in the $CBQ_PATH directory
+# (/etc/sysconfig/cbq by default) - one file per class.
+#
+# The config file names must obey mandatory format: cbq-<clsid>.<name> where
+# <clsid> is two-byte hexadecimal number in range <0002-FFFF> (which in fact
+# is a CBQ class ID) and <name> is the name of the class -- anything to help
+# you distinguish the configuration files. For small amount of classes it is
+# often possible (and convenient) to let <clsid> resemble bandwidth of the
+# class.
+#
+# Example of valid config name:
+#	cbq-1280.My_first_shaper
+#
+#
+# The configuration file may contain the following parameters:
+#
+### Device parameters
+#
+# DEVICE=<ifname>,<bandwidth>[,<weight>]	mandatory
+# DEVICE=eth0,10Mbit,1Mbit
+#
+#	<ifname> is the name of the interface you want to control
+#		traffic on, e.g. eth0
+#	<bandwidth> is the physical bandwidth of the device, e.g. for
+#		ethernet 10Mbit or 100Mbit, for arcnet 2Mbit
+#	<weight> is tuning parameter that should be proportional to
+#		<bandwidth>. As a rule of thumb: <weight> = <bandwidth> / 10
+#
+# When you have more classes on one interface, it is enough to specify
+# <bandwidth> [and <weight>] only once, therefore in other files you only
+# need to set DEVICE=<ifname>.
+#
+### Class parameters
+#
+# RATE=<speed>					mandatory
+# RATE=5Mbit
+#
+#	Bandwidth allocated to the class. Traffic going through the class is
+#	shaped to conform to specified rate. You can use Kbit, Mbit or bps,
+#	Kbps and Mbps as suffices. If you don't specify any unit, bits/sec
+#	are used. Also note that "bps" means "bytes per second", not bits.
+#
+# WEIGHT=<speed> 				mandatory
+# WEIGHT=500Kbit
+#
+#	Tuning parameter that should be proportional to RATE. As a rule
+#	of thumb, use WEIGHT ~= RATE / 10.
+#
+# PRIO=<1-8>					optional, default 5
+# PRIO=5
+#
+#	Priority of class traffic. The higher the number, the lesser
+#	the priority. Priority of 5 is just fine.
+#
+# PARENT=<clsid>				optional, default not set
+# PARENT=1280
+#
+#	Specifies ID of the parent class to which you want this class be
+#	attached. You might want to use LEAF=none for the parent class as
+#	mentioned below. By using this parameter and carefully ordering the
+#	configuration files, it is possible to create simple hierarchical
+#	structures of CBQ classes. The ordering is important so that parent
+#	classes are constructed prior to their children.
+#
+# LEAF=none|tbf|sfq				optional, default "tbf"
+#
+#	Tells the script to attach specified leaf queueing discipline to CBQ
+#	class. By default, TBF is used. Note that attaching TBF to CBQ class
+#	shapes the traffic to conform to TBF parameters and prevents the class
+#	from borrowing bandwidth from its parent even if you have BOUNDED set
+#	to "no". To allow the class to borrow bandwith (provided it is not
+#	bounded), you must set LEAF to "none" or "sfq".
+#
+#	If you want to ensure (approximately) fair sharing of bandwidth among
+#	several hosts in the same class, you might want to specify LEAF=sfq to
+#	attach SFQ as leaf queueing discipline to that class.
+#
+# BOUNDED=yes|no				optional, default "yes"
+#
+#	If set to "yes", the class is not allowed to borrow bandwidth from
+#	its parent class in overlimit situation. If set to "no", the class
+#	will be allowed to borrow bandwidth from its parent.
+#
+# Note:	Don't forget to set LEAF to "none" or "sfq", otherwise the class will
+#	have TBF attached to itself and will not be able to borrow unused
+#	bandwith from its parent.
+#
+# ISOLATED=yes|no				optional, default "no"
+#
+#	If set to "yes", the class will not lend unused bandwidth to
+#	its children.
+#
+### TBF qdisc parameters
+#
+# BUFFER=<bytes>[/<bytes>]			optional, default "10Kb/8"
+#
+#	This parameter controls the depth of the token bucket. In other
+#	words it represents the maximal burst size the class can send.
+#	The optional part of parameter is used to determine the length
+#	of intervals in packet sizes, for which the transmission times
+#	are kept.
+#
+# LIMIT=<bytes>					optional, default "15Kb"
+#
+#	This parameter determines the maximal length of backlog. If
+#	the queue contains more data than specified by LIMIT, the
+#	newly arriving packets are dropped. The length of backlog
+#	determines queue latency in case of congestion.
+#
+# PEAK=<speed>					optional, default not set
+#
+#	Maximal peak rate for short-term burst traffic. This allows you
+#	to control the absolute peak rate the class can send at, because
+#	single TBF that allows 256Kbit/s would of course allow rate of
+#	512Kbit for half a second or 1Mbit for a quarter of second.
+#
+# MTU=<bytes>  					optional, default "1500"
+#
+#	Maximum number of bytes that can be sent at once over the
+#	physical medium. This parameter is required when you specify
+#	PEAK parameter. It defaults to MTU of ethernet - for other
+#	media types you might want to change it.
+#
+# Note: Setting TBF as leaf qdisc will effectively prevent the class from
+#	borrowing bandwidth from the ancestor class, because even if the
+#	class allows more traffic to pass through, it is then shaped to
+#	conform to TBF.
+#
+### SFQ qdisc parameters
+#
+# The SFQ queueing discipline is a cheap way for sharing class bandwidth
+# among several hosts. As it is stochastic, the fairness is approximate but
+# it will do the job in most cases. If you want real fairness, you should
+# probably use WRR (weighted round robin) or WFQ queueing disciplines. Note
+# that SFQ does not do any traffic shaping - the shaping is done by the CBQ
+# class the SFQ is attached to.
+#
+# QUANTUM=<bytes>				optional, default not set
+#
+#	This parameter should not be set lower than link MTU, for ethernet
+#	it is 1500b, or (with MAC header) 1514b which is the value used
+#	in Alexey Kuznetsov's examples.
+#
+# PERTURB=<seconds>				optional, default "10"
+#
+#	Period of hash function perturbation. If unset, hash reconfiguration
+#	will never take place which is what you probably don't want. The
+#	default value of 10 seconds is probably a good one.
+#
+### Filter parameters
+#
+# RULE=[[saddr[/prefix]][:port[/mask]],][daddr[/prefix]][:port[/mask]]
+#
+#	These parameters make up "u32" filter rules that select traffic for
+#	each of the classes. You can use multiple RULE fields per config.
+#
+#	The optional port mask should only be used by advanced users who
+#	understand how the u32 filter works.
+#
+# Some examples:
+#
+#	RULE=10.1.1.0/24:80
+#		selects traffic going to port 80 in network 10.1.1.0
+#
+#	RULE=10.2.2.5
+#		selects traffic going to any port on single host 10.2.2.5
+#
+#	RULE=10.2.2.5:20/0xfffe
+#		selects traffic going to ports 20 and 21 on host 10.2.2.5
+#
+#	RULE=:25,10.2.2.128/26:5000
+#		selects traffic going from anywhere on port 50 to
+#		port 5000 in network 10.2.2.128
+#
+#	RULE=10.5.5.5:80,
+#		selects traffic going from port 80 of single host 10.5.5.5
+#
+#
+#
+# REALM=[srealm,][drealm]
+#
+#	These parameters make up "route" filter rules that classify traffic
+#	according to packet source/destination realms. For information about
+#	realms, see Alexey Kuznetsov's IP Command Reference. This script
+#	does not define any realms, it justs builds "tc filter" commands
+#	for you if you need to classify traffic this way.
+#
+#	Realm is either a decimal number or a string referencing entry in
+#	/etc/iproute2/rt_realms (usually).
+#
+# Some examples:
+#
+#	REALM=russia,internet
+#		selects traffic going from realm "russia" to realm "internet"
+#
+#	REALM=freenet,
+#		selects traffic going from realm "freenet"
+#
+#	REALM=10
+#		selects traffic going to realm 10
+#
+#
+#
+# MARK=<mark>
+#
+#	These parameters make up "fw" filter rules that select traffic for
+#	each of the classes accoring to firewall "mark". Mark is a decimal
+#	number packets are tagged with if firewall rules say so. You can
+#	use multiple MARK fields per config.
+#
+#
+# Note: Rules for different filter types can be combined. Attention must be
+#	paid to the priority of filter rules, which can be set below using
+#	PRIO_{RULE,MARK,REALM} variables.
+#
+### Time ranging parameters
+#
+# TIME=[<dow>,<dow>, ...,<dow>/]<from>-<till>;<rate>/<weight>[/<peak>]
+# TIME=0,1,2,5/18:00-06:00;256Kbit/25Kbit
+# TIME=60123/18:00-06:00;256Kbit/25Kbit
+# TIME=18:00-06:00;256Kbit/25Kbit
+#
+#	This parameter allows you to differentiate the class bandwidth
+#	throughout the day. You can specify multiple TIME parameters, if
+#	the times overlap, last match is taken. The fields <rate>, <weight>
+#	and <peak> correspond to parameters RATE, WEIGHT and PEAK (which
+#	is optional and applies to TBF leaf qdisc only).
+#
+#	You can also specify days of week when the TIME rule applies. <dow>
+#	is numeric, 0 corresponds to sunday, 1 corresponds to monday, etc.
+#
+###
+#
+# Sample configuration file: cbq-1280.My_first_shaper
+#
+# --------------------------------------------------------------------------
+# DEVICE=eth0,10Mbit,1Mbit
+# RATE=128Kbit
+# WEIGHT=10Kbit
+# PRIO=5
+# RULE=192.128.1.0/24
+# --------------------------------------------------------------------------
+#
+# The configuration says that we will control traffic on 10Mbit ethernet
+# device eth0 and the traffic going to network 192.168.1.0 will be
+# processed with priority 5 and shaped to rate of 128Kbit.
+#
+# Note that you can control outgoing traffic only. If you want to control
+# traffic in both directions, you must set up CBQ for both interfaces.
+#
+# Consider the following example:
+#
+#                    +---------+      192.168.1.1
+# BACKBONE -----eth0-|  linux  |-eth1------*-[client]
+#                    +---------+
+#
+# Imagine you want to shape traffic from backbone to the client to 28Kbit
+# and traffic in the opposite direction to 128Kbit. You need to setup CBQ
+# on both eth0 and eth1 interfaces, thus you need two config files:
+#
+# cbq-028.backbone-client
+# --------------------------------------------------------------------------
+# DEVICE=eth1,10Mbit,1Mbit
+# RATE=28Kbit
+# WEIGHT=2Kbit
+# PRIO=5
+# RULE=192.168.1.1
+# --------------------------------------------------------------------------
+#
+# cbq-128.client-backbone
+# --------------------------------------------------------------------------
+# DEVICE=eth0,10Mbit,1Mbit
+# RATE=128Kbit
+# WEIGHT=10Kbit
+# PRIO=5
+# RULE=192.168.1.1,
+# --------------------------------------------------------------------------
+#
+# Pay attention to comma "," in the RULE field - it denotes source address!
+#
+# Enjoy.
+#
+#############################################################################
+
+export LC_ALL=C
+
+### Command locations
+TC=/sbin/tc
+IP=/sbin/ip
+MP=/sbin/modprobe
+
+### Default filter priorities (must be different)
+PRIO_RULE_DEFAULT=${PRIO_RULE:-100}
+PRIO_MARK_DEFAULT=${PRIO_MARK:-200}
+PRIO_REALM_DEFAULT=${PRIO_REALM:-300}
+
+### Default CBQ_PATH & CBQ_CACHE settings
+CBQ_PATH=${CBQ_PATH:-/etc/sysconfig/cbq}
+CBQ_CACHE=${CBQ_CACHE:-/var/cache/cbq.init}
+
+### Uncomment to enable logfile for debugging
+#CBQ_DEBUG="/var/run/cbq-$1"
+
+### Modules to probe for. Uncomment the last CBQ_PROBE
+### line if you have QoS support compiled into kernel
+CBQ_PROBE="sch_cbq sch_tbf sch_sfq sch_prio"
+CBQ_PROBE="$CBQ_PROBE cls_fw cls_u32 cls_route"
+#CBQ_PROBE=""
+
+### Keywords required for qdisc & class configuration
+CBQ_WORDS="DEVICE|RATE|WEIGHT|PRIO|PARENT|LEAF|BOUNDED|ISOLATED"
+CBQ_WORDS="$CBQ_WORDS|PRIO_MARK|PRIO_RULE|PRIO_REALM|BUFFER"
+CBQ_WORDS="$CBQ_WORDS|LIMIT|PEAK|MTU|QUANTUM|PERTURB"
+
+### Source AVPKT if it exists
+[ -r /etc/sysconfig/cbq/avpkt ] && . /etc/sysconfig/cbq/avpkt
+AVPKT=${AVPKT:-3000}
+
+
+#############################################################################
+############################# SUPPORT FUNCTIONS #############################
+#############################################################################
+
+### Get list of network devices
+cbq_device_list () {
+	ip link show| sed -n "/^[0-9]/ \
+		{ s/^[0-9]\+: \([a-z0-9._]\+\)[:@].*/\1/; p; }"
+} # cbq_device_list
+
+
+### Remove root class from device $1
+cbq_device_off () {
+	tc qdisc del dev $1 root 2> /dev/null
+} # cbq_device_off
+
+
+### Remove CBQ from all devices
+cbq_off () {
+	for dev in `cbq_device_list`; do
+		cbq_device_off $dev
+	done
+} # cbq_off
+
+
+### Prefixed message
+cbq_message () {
+	echo -e "**CBQ: $@"
+} # cbq_message
+
+### Failure message
+cbq_failure () {
+	cbq_message "$@"
+	exit 1
+} # cbq_failure
+
+### Failure w/ cbq-off
+cbq_fail_off () {
+	cbq_message "$@"
+	cbq_off
+	exit 1
+} # cbq_fail_off
+
+
+### Convert time to absolute value
+cbq_time2abs () {
+	local min=${1##*:}; min=${min##0}
+	local hrs=${1%%:*}; hrs=${hrs##0}
+	echo $[hrs*60 + min]
+} # cbq_time2abs
+
+
+### Display CBQ setup
+cbq_show () {
+	for dev in `cbq_device_list`; do
+		[ `tc qdisc show dev $dev| wc -l` -eq 0 ] && continue
+		echo -e "### $dev: queueing disciplines\n"
+		tc $1 qdisc show dev $dev; echo
+
+		[ `tc class show dev $dev| wc -l` -eq 0 ] && continue
+		echo -e "### $dev: traffic classes\n"
+		tc $1 class show dev $dev; echo
+
+		[ `tc filter show dev $dev| wc -l` -eq 0 ] && continue
+		echo -e "### $dev: filtering rules\n"
+		tc $1 filter show dev $dev; echo
+	done
+} # cbq_show
+
+
+### Check configuration and load DEVICES, DEVFIELDS and CLASSLIST from $1
+cbq_init () {
+	### Get a list of configured classes
+	CLASSLIST=`find $1 -maxdepth 1 \( -type f -or -type l \) -name 'cbq-*' \
+		-not -name '*~' -printf "%f\n"| sort`
+	[ -z "$CLASSLIST" ] &&
+		cbq_failure "no configuration files found in $1!"
+
+	### Gather all DEVICE fields from $1/cbq-*
+	DEVFIELDS=`find $1 -maxdepth 1 \( -type f -or -type l \) -name 'cbq-*' \
+		  -not -name '*~' | xargs sed -n 's/#.*//; \
+		  s/[[:space:]]//g; /^DEVICE=[^,]*,[^,]*\(,[^,]*\)\?/ \
+		  { s/.*=//; p; }'| sort -u`
+	[ -z "$DEVFIELDS" ] &&
+		cbq_failure "no DEVICE field found in $1/cbq-*!"
+
+	### Check for different DEVICE fields for the same device
+	DEVICES=`echo "$DEVFIELDS"| sed 's/,.*//'| sort -u`
+	[ `echo "$DEVICES"| wc -l` -ne `echo "$DEVFIELDS"| wc -l` ] &&
+		cbq_failure "different DEVICE fields for single device!\n$DEVFIELDS"
+} # cbq_init
+
+
+### Load class configuration from $1/$2
+cbq_load_class () {
+	CLASS=`echo $2| sed 's/^cbq-0*//; s/^\([0-9a-fA-F]\+\).*/\1/'`
+	CFILE=`sed -n 's/#.*//; s/[[:space:]]//g; /^[[:alnum:]_]\+=[[:alnum:].,:;/*@-_]\+$/ p' $1/$2`
+
+	### Check class number
+	IDVAL=`/usr/bin/printf "%d" 0x$CLASS 2> /dev/null`
+	[ $? -ne 0 -o $IDVAL -lt 2 -o $IDVAL -gt 65535 ] &&
+		cbq_fail_off "class ID of $2 must be in range <0002-FFFF>!"
+
+	### Set defaults & load class
+	RATE=""; WEIGHT=""; PARENT=""; PRIO=5
+	LEAF=tbf; BOUNDED=yes; ISOLATED=no
+	BUFFER=10Kb/8; LIMIT=15Kb; MTU=1500
+	PEAK=""; PERTURB=10; QUANTUM=""
+
+	PRIO_RULE=$PRIO_RULE_DEFAULT
+	PRIO_MARK=$PRIO_MARK_DEFAULT
+	PRIO_REALM=$PRIO_REALM_DEFAULT
+
+	eval `echo "$CFILE"| grep -E "^($CBQ_WORDS)="`
+
+	### Require RATE/WEIGHT
+	[ -z "$RATE" -o -z "$WEIGHT" ] &&
+		cbq_fail_off "missing RATE or WEIGHT in $2!"
+
+	### Class device
+	DEVICE=${DEVICE%%,*}
+	[ -z "$DEVICE" ] && cbq_fail_off "missing DEVICE field in $2!"
+
+	BANDWIDTH=`echo "$DEVFIELDS"| sed -n "/^$DEVICE,/ \
+		  { s/[^,]*,\([^,]*\).*/\1/; p; q; }"`
+
+	### Convert to "tc" options
+	PEAK=${PEAK:+peakrate $PEAK}
+	PERTURB=${PERTURB:+perturb $PERTURB}
+	QUANTUM=${QUANTUM:+quantum $QUANTUM}
+
+	[ "$BOUNDED" = "no" ] && BOUNDED="" || BOUNDED="bounded"
+	[ "$ISOLATED" = "yes" ] && ISOLATED="isolated" || ISOLATED=""
+} # cbq_load_class
+
+
+#############################################################################
+#################################### INIT ###################################
+#############################################################################
+
+### Check for presence of ip-route2 in usual place
+[ -x $TC -a -x $IP ] ||
+	cbq_failure "ip-route2 utilities not installed or executable!"
+
+
+### ip/tc wrappers
+if [ "$1" = "compile" ]; then
+	### no module probing
+	CBQ_PROBE=""
+
+	ip () {
+		$IP "$@"
+	} # ip
+
+	### echo-only version of "tc" command
+	tc () {
+		echo "$TC $@"
+	} # tc
+
+elif [ -n "$CBQ_DEBUG" ]; then
+	echo -e "# `date`" > $CBQ_DEBUG
+
+	### Logging version of "ip" command
+	ip () {
+		echo -e "\n# ip $@" >> $CBQ_DEBUG
+		$IP "$@" 2>&1 | tee -a $CBQ_DEBUG
+	} # ip
+
+	### Logging version of "tc" command
+	tc () {
+		echo -e "\n# tc $@" >> $CBQ_DEBUG
+		$TC "$@" 2>&1 | tee -a $CBQ_DEBUG
+	} # tc
+else
+	### Default wrappers
+	
+	ip () {
+		$IP "$@"
+	} # ip
+	
+	tc () {
+		$TC "$@"
+	} # tc
+fi # ip/tc wrappers
+
+
+case "$1" in
+
+#############################################################################
+############################### START/COMPILE ###############################
+#############################################################################
+
+start|compile)
+
+### Probe QoS modules (start only)
+for module in $CBQ_PROBE; do
+	$MP $module || cbq_failure "failed to load module $module"
+done
+
+### If we are in compile/nocache/logging mode, don't bother with cache
+if [ "$1" != "compile" -a "$2" != "nocache" -a -z "$CBQ_DEBUG" ]; then
+	VALID=1
+
+	### validate the cache
+	[ "$2" = "invalidate" -o ! -f $CBQ_CACHE ] && VALID=0
+	if [ $VALID -eq 1 ]; then
+		[ `find $CBQ_PATH -maxdepth 1 -newer $CBQ_CACHE| \
+		  wc -l` -gt 0 ] && VALID=0
+	fi
+
+	### compile the config if the cache is invalid
+	if [ $VALID -ne 1 ]; then
+		$0 compile > $CBQ_CACHE ||
+			cbq_fail_off "failed to compile CBQ configuration!"
+	fi
+
+	### run the cached commands
+	exec /bin/sh $CBQ_CACHE 2> /dev/null
+fi
+
+### Load DEVICES, DEVFIELDS and CLASSLIST
+cbq_init $CBQ_PATH
+
+
+### Setup root qdisc on all configured devices
+for dev in $DEVICES; do
+	### Retrieve device bandwidth and, optionally, weight
+	DEVTEMP=`echo "$DEVFIELDS"| sed -n "/^$dev,/ { s/$dev,//; p; q; }"`
+	DEVBWDT=${DEVTEMP%%,*};	DEVWGHT=${DEVTEMP##*,}
+	[ "$DEVBWDT" = "$DEVWGHT" ] && DEVWGHT=""
+
+	### Device bandwidth is required
+	if [ -z "$DEVBWDT" ]; then
+		cbq_message "could not determine bandwidth for device $dev!"
+		cbq_failure "please set up the DEVICE fields properly!"
+	fi
+
+	### Check if the device is there
+	ip link show $dev &> /dev/null ||
+		cbq_fail_off "device $dev not found!"
+
+	### Remove old root qdisc from device
+	cbq_device_off $dev
+
+
+	### Setup root qdisc + class for device
+	tc qdisc add dev $dev root handle 1 cbq \
+	bandwidth $DEVBWDT avpkt $AVPKT cell 8
+
+	### Set weight of the root class if set
+	[ -n "$DEVWGHT" ] &&
+		tc class change dev $dev root cbq weight $DEVWGHT allot 1514
+
+	[ "$1" = "compile" ] && echo
+done # dev
+
+
+### Setup traffic classes
+for classfile in $CLASSLIST; do
+	cbq_load_class $CBQ_PATH $classfile
+
+	### Create the class
+	tc class add dev $DEVICE parent 1:$PARENT classid 1:$CLASS cbq \
+	bandwidth $BANDWIDTH rate $RATE weight $WEIGHT prio $PRIO \
+	allot 1514 cell 8 maxburst 20 avpkt $AVPKT $BOUNDED $ISOLATED ||
+		cbq_fail_off "failed to add class $CLASS with parent $PARENT on $DEVICE!"
+
+	### Create leaf qdisc if set
+	if [ "$LEAF" = "tbf" ]; then
+		tc qdisc add dev $DEVICE parent 1:$CLASS handle $CLASS tbf \
+		rate $RATE buffer $BUFFER limit $LIMIT mtu $MTU $PEAK
+	elif [ "$LEAF" = "sfq" ]; then
+		tc qdisc add dev $DEVICE parent 1:$CLASS handle $CLASS sfq \
+		$PERTURB $QUANTUM
+	fi
+
+
+	### Create fw filter for MARK fields
+	for mark in `echo "$CFILE"| sed -n '/^MARK/ { s/.*=//; p; }'`; do
+		### Attach fw filter to root class
+		tc filter add dev $DEVICE parent 1:0 protocol ip \
+		prio $PRIO_MARK handle $mark fw classid 1:$CLASS
+	done ### mark
+
+	### Create route filter for REALM fields
+	for realm in `echo "$CFILE"| sed -n '/^REALM/ { s/.*=//; p; }'`; do
+		### Split realm into source & destination realms
+		SREALM=${realm%%,*}; DREALM=${realm##*,}
+		[ "$SREALM" = "$DREALM" ] && SREALM=""
+
+		### Convert asterisks to empty strings
+		SREALM=${SREALM#\*}; DREALM=${DREALM#\*}
+
+		### Attach route filter to the root class
+		tc filter add dev $DEVICE parent 1:0 protocol ip \
+		prio $PRIO_REALM route ${SREALM:+from $SREALM} \
+		${DREALM:+to $DREALM} classid 1:$CLASS
+	done ### realm
+
+	### Create u32 filter for RULE fields
+	for rule in `echo "$CFILE"| sed -n '/^RULE/ { s/.*=//; p; }'`; do
+		### Split rule into source & destination
+		SRC=${rule%%,*}; DST=${rule##*,}
+		[ "$SRC" = "$rule" ] && SRC=""
+
+
+		### Split destination into address, port & mask fields
+		DADDR=${DST%%:*}; DTEMP=${DST##*:}
+		[ "$DADDR" = "$DST" ] && DTEMP=""
+
+		DPORT=${DTEMP%%/*}; DMASK=${DTEMP##*/}
+		[ "$DPORT" = "$DTEMP" ] && DMASK="0xffff"
+
+
+		### Split up source (if specified)
+		SADDR=""; SPORT=""
+		if [ -n "$SRC" ]; then
+			SADDR=${SRC%%:*}; STEMP=${SRC##*:}
+			[ "$SADDR" = "$SRC" ] && STEMP=""
+
+			SPORT=${STEMP%%/*}; SMASK=${STEMP##*/}
+			[ "$SPORT" = "$STEMP" ] && SMASK="0xffff"
+		fi
+
+
+		### Convert asterisks to empty strings
+		SADDR=${SADDR#\*}; DADDR=${DADDR#\*}
+
+		### Compose u32 filter rules
+		u32_s="${SPORT:+match ip sport $SPORT $SMASK}"
+		u32_s="${SADDR:+match ip src $SADDR} $u32_s"
+		u32_d="${DPORT:+match ip dport $DPORT $DMASK}"
+		u32_d="${DADDR:+match ip dst $DADDR} $u32_d"
+
+		### Uncomment the following if you want to see parsed rules
+		#echo "$rule: $u32_s $u32_d"
+
+		### Attach u32 filter to the appropriate class
+		tc filter add dev $DEVICE parent 1:0 protocol ip \
+		prio $PRIO_RULE u32 $u32_s $u32_d classid 1:$CLASS
+	done ### rule
+
+	[ "$1" = "compile" ] && echo
+done ### classfile
+;;
+
+
+#############################################################################
+################################# TIME CHECK ################################
+#############################################################################
+
+timecheck)
+
+### Get time + weekday
+TIME_TMP=`date +%w/%k:%M`
+TIME_DOW=${TIME_TMP%%/*}
+TIME_NOW=${TIME_TMP##*/}
+
+### Load DEVICES, DEVFIELDS and CLASSLIST
+cbq_init $CBQ_PATH
+
+### Run through all classes
+for classfile in $CLASSLIST; do
+	### Gather all TIME rules from class config
+	TIMESET=`sed -n 's/#.*//; s/[[:space:]]//g; /^TIME/ { s/.*=//; p; }' \
+		$CBQ_PATH/$classfile`
+	[ -z "$TIMESET" ] && continue
+
+	MATCH=0; CHANGE=0
+	for timerule in $TIMESET; do
+		TIME_ABS=`cbq_time2abs $TIME_NOW`
+		
+		### Split TIME rule to pieces
+		TIMESPEC=${timerule%%;*}; PARAMS=${timerule##*;}
+		WEEKDAYS=${TIMESPEC%%/*}; INTERVAL=${TIMESPEC##*/}
+		BEG_TIME=${INTERVAL%%-*}; END_TIME=${INTERVAL##*-}
+
+		### Check the day-of-week (if present)
+		[ "$WEEKDAYS" != "$INTERVAL" -a \
+		  -n "${WEEKDAYS##*$TIME_DOW*}" ] && continue
+
+		### Compute interval boundaries
+		BEG_ABS=`cbq_time2abs $BEG_TIME`
+		END_ABS=`cbq_time2abs $END_TIME`
+
+		### Midnight wrap fixup
+		if [ $BEG_ABS -gt $END_ABS ]; then
+			[ $TIME_ABS -le $END_ABS ] &&
+				TIME_ABS=$[TIME_ABS + 24*60]
+
+			END_ABS=$[END_ABS + 24*60]
+		fi
+
+		### If the time matches, remember params and set MATCH flag
+		if [ $TIME_ABS -ge $BEG_ABS -a $TIME_ABS -lt $END_ABS ]; then
+			TMP_RATE=${PARAMS%%/*}; PARAMS=${PARAMS#*/}
+			TMP_WGHT=${PARAMS%%/*}; TMP_PEAK=${PARAMS##*/}
+
+			[ "$TMP_PEAK" = "$TMP_WGHT" ] && TMP_PEAK=""
+			TMP_PEAK=${TMP_PEAK:+peakrate $TMP_PEAK}
+
+			MATCH=1
+		fi
+	done ### timerule
+
+
+	cbq_load_class $CBQ_PATH $classfile
+
+	### Get current RATE of CBQ class
+	RATE_NOW=`tc class show dev $DEVICE| sed -n \
+		 "/cbq 1:$CLASS / { s/.*rate //; s/ .*//; p; q; }"`
+	[ -z "$RATE_NOW" ] && continue
+
+	### Time interval matched
+	if [ $MATCH -ne 0 ]; then
+
+		### Check if there is any change in class RATE
+		if [ "$RATE_NOW" != "$TMP_RATE" ]; then
+			NEW_RATE="$TMP_RATE"
+			NEW_WGHT="$TMP_WGHT"
+			NEW_PEAK="$TMP_PEAK"
+			CHANGE=1
+		fi
+
+	### Match not found, reset to default RATE if necessary
+	elif [ "$RATE_NOW" != "$RATE" ]; then
+		NEW_WGHT="$WEIGHT"
+		NEW_RATE="$RATE"
+		NEW_PEAK="$PEAK"
+		CHANGE=1
+	fi
+
+	### If there are no changes, go for next class
+	[ $CHANGE -eq 0 ] && continue
+
+	### Replace CBQ class
+	tc class replace dev $DEVICE classid 1:$CLASS cbq \
+	bandwidth $BANDWIDTH rate $NEW_RATE weight $NEW_WGHT prio $PRIO \
+	allot 1514 cell 8 maxburst 20 avpkt $AVPKT $BOUNDED $ISOLATED
+
+	### Replace leaf qdisc (if any)
+	if [ "$LEAF" = "tbf" ]; then
+		tc qdisc replace dev $DEVICE handle $CLASS tbf \
+		rate $NEW_RATE buffer $BUFFER limit $LIMIT mtu $MTU $NEW_PEAK
+	fi
+
+	cbq_message "$TIME_NOW: class $CLASS on $DEVICE changed rate ($RATE_NOW -> $NEW_RATE)"
+done ### class file
+;;
+
+
+#############################################################################
+################################## THE REST #################################
+#############################################################################
+
+stop)
+	cbq_off
+	;;
+
+list)
+	cbq_show
+	;;
+
+stats)
+	cbq_show -s
+	;;
+
+restart)
+	shift
+	$0 stop
+	$0 start "$@"
+	;;
+
+*)
+	echo "Usage: `basename $0` {start|compile|stop|restart|timecheck|list|stats}"
+esac
diff --git a/examples/cbqinit.eth1 b/examples/cbqinit.eth1
new file mode 100644
index 0000000..226ec1c
--- /dev/null
+++ b/examples/cbqinit.eth1
@@ -0,0 +1,76 @@
+#! /bin/sh
+
+TC=/home/root/tc
+IP=/home/root/ip
+DEVICE=eth1
+BANDWIDTH="bandwidth 10Mbit"
+
+# Attach CBQ on $DEVICE. It will have handle 1:.
+#   $BANDWIDTH is real $DEVICE bandwidth (10Mbit).
+#   avpkt is average packet size.
+#   mpu is minimal packet size.
+
+$TC qdisc add dev $DEVICE  root  handle 1:  cbq \
+$BANDWIDTH avpkt 1000 mpu 64
+
+# Create root class with classid 1:1. This step is not necessary.
+#   bandwidth is the same as on CBQ itself.
+#   rate == all the bandwidth
+#   allot is MTU + MAC header
+#   maxburst measure allowed class burstiness (please,read S.Floyd and VJ papers)
+#   est 1sec 8sec means, that kernel will evaluate average rate
+#                 on this class with period 1sec and time constant 8sec.
+#                 This rate is viewed with "tc -s class ls dev $DEVICE"
+
+$TC class add dev $DEVICE parent 1:0 classid :1 est 1sec 8sec cbq \
+$BANDWIDTH rate 10Mbit allot 1514 maxburst 50 avpkt 1000
+
+# Bulk.
+#    New parameters are: 
+#    weight, which is set to be proportional to
+#            "rate". It is not necessary, weight=1 will work as well.
+#    defmap and split say that best effort ttraffic, not classfied
+#            by another means will fall to this class.
+
+$TC class add dev $DEVICE parent 1:1 classid :2 est 1sec 8sec cbq \
+$BANDWIDTH rate 4Mbit allot 1514 weight 500Kbit \
+prio 6 maxburst 50 avpkt 1000 split 1:0 defmap ff3d
+
+# OPTIONAL.
+# Attach "sfq" qdisc to this class, quantum is MTU, perturb
+# gives period of hash function perturbation in seconds.
+#
+$TC qdisc add dev $DEVICE parent 1:2 sfq quantum 1514b perturb 15
+
+# Interactive-burst class
+
+$TC class add dev $DEVICE parent 1:1 classid :3 est 2sec 16sec cbq \
+$BANDWIDTH rate 1Mbit allot 1514 weight 100Kbit \
+prio 2 maxburst 100 avpkt 1000 split 1:0 defmap c0
+
+$TC qdisc add dev $DEVICE parent 1:3 sfq quantum 1514b perturb 15
+
+# Background.
+
+$TC class add dev $DEVICE parent 1:1 classid :4 est 1sec 8sec cbq \
+  $BANDWIDTH rate 100Kbit allot 1514 weight 10Mbit \
+  prio 7 maxburst 10 avpkt 1000 split 1:0 defmap 2
+
+$TC qdisc add dev $DEVICE parent 1:4 sfq quantum 1514b perturb 15
+
+# Realtime class for RSVP
+
+$TC class add dev $DEVICE parent 1:1 classid 1:7FFE cbq \
+rate 5Mbit $BANDWIDTH allot 1514b avpkt 1000 \
+maxburst 20
+
+# Reclassified realtime traffic
+#
+# New element: split is not 1:0, but 1:7FFE. It means,
+#     that only real-time packets, which violated policing filters
+#     or exceeded reshaping buffers will fall to it.
+
+$TC class add dev $DEVICE parent 1:7FFE classid 1:7FFF  est 4sec 32sec cbq \
+rate 1Mbit $BANDWIDTH allot 1514b avpkt 1000 weight 10Kbit \
+prio 6 maxburst 10 split 1:7FFE defmap ffff
+
diff --git a/examples/dhcp-client-script b/examples/dhcp-client-script
new file mode 100644
index 0000000..f39bc10
--- /dev/null
+++ b/examples/dhcp-client-script
@@ -0,0 +1,446 @@
+#!/bin/bash
+#
+# dhclient-script for Linux.
+#
+#		This program is free software; you can redistribute it and/or
+#		modify it under the terms of the GNU General Public License
+#		as published by the Free Software Foundation; either version
+#		2 of the License, or (at your option) any later version.
+#
+# Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+#
+# Probably, I did not understand, what this funny feature as "alias"
+# means exactly. For now I suppose, that it is a static address, which
+# we should install and preserve.
+#
+
+exec >> /var/log/DHS.log 2>&1
+
+echo dhc-script $* reason=$reason
+set | grep "^\(old_\|new_\|check_\)"
+
+LOG () {
+    echo LOG $* ;
+}
+
+# convert 8bit mask to length
+# arg: $1 = mask
+#
+Mask8ToLen() {
+	local l=0;
+
+	while [ $l -le 7 ]; do
+		if [ $[ ( 1 << $l ) + $1 ] -eq 256 ]; then
+			return	$[ 8 - $l ]
+		fi
+		l=$[ $l + 1 ]
+	done
+	return 0;
+}
+
+# convert inet dotted quad mask to length
+# arg: $1 = dotquad mask
+#
+MaskToLen() {
+ local masklen=0
+ local mask8=$1
+
+ case $1 in
+ 0.0.0.0)
+	return 0;
+	;;
+ 255.*.0.0)
+	masklen=8
+	mask8=${mask8#255.}
+	mask8=${mask8%.0.0}
+	;;
+ 255.255.*.0)
+	masklen=16
+	mask8=${mask8#255.255.}
+	mask8=${mask8%.0}
+	;;
+ 255.255.255.*)
+	masklen=24
+	mask8=${mask8#255.255.255.}
+	;;
+ *)
+	return 255
+	;;
+ esac
+ Mask8ToLen $mask8
+ return $[ $? + $masklen ]
+}
+
+# calculate ABC "natural" mask
+# arg: $1 = dotquad address
+#
+ABCMask () {
+ local class;
+
+ class=${1%%.*}
+
+ if [ "$1" = "255.255.255.255" ]; then
+    echo $1
+ elif [ "$1" = "0.0.0.0" ]; then
+    echo $1
+ elif [ $class -ge 224 ]; then
+    echo 240.0.0.0
+ elif [ $class -ge 192 ]; then
+    echo 255.255.255.0
+ elif [ $class -ge 128 ]; then
+    echo 255.255.0.0
+ else
+    echo 255.0.0.0
+ fi
+}
+
+# calculate ABC "natural" mask length
+# arg: $1 = dotquad address
+#
+ABCMaskLen () {
+ local class;
+
+ class=${1%%.*}
+
+ if [ "$1" = "255.255.255.255" ]; then
+    return 32
+ elif [ "$1" = "0.0.0.0" ]; then
+    return 0
+ elif [ $class -ge 224 ]; then
+    return 4;
+ elif [ $class -ge 192 ]; then
+    return 24;
+ elif [ $class -ge 128 ]; then
+    return 16;
+ else
+    return 8;
+ fi
+}
+
+# Delete IP address
+# args: $1 = interface
+#       $2 = address
+#       $3 = mask
+#       $4 = broadcast
+#       $5 = label
+#
+DelINETAddr () {
+  local masklen=32
+  local addrid=$1
+
+  LOG DelINETAddr $*
+
+  if [ "$5" ]; then
+    addrid=$addrid:$5
+  fi
+  LOG ifconfig $addrid down
+  ifconfig $addrid down
+}
+
+# Add IP address
+# args: $1 = interface
+#       $2 = address
+#       $3 = mask
+#       $4 = broadcast
+#       $5 = label
+#
+AddINETAddr () {
+  local mask_arg
+  local brd_arg
+  local addrid=$1
+
+  LOG AddINETAddr $*
+
+  if [ "$5" ]; then
+    addrid=$addrid:$5
+  fi
+  if [ "$3" ]; then
+    mask_arg="netmask $3"
+  fi
+  if [ "$4" ]; then
+    brd_arg="broadcast $4"
+  fi
+
+  LOG ifconfig $addrid $2 $mask_arg $brd_arg up
+  ifconfig $addrid $2 $mask_arg $brd_arg up
+}
+
+# Add default routes
+# args: $1 = routers list
+#
+AddDefaultRoutes() {
+    local router
+
+    if [ "$1" ]; then
+      LOG AddDefaultRoutes $*
+      for router in $1; do
+        LOG route add default gw $router
+        route add default gw $router
+      done ;
+    fi
+}
+
+# Delete default routes
+# args: $1 = routers list
+#
+DelDefaultRoutes() {
+    local router
+
+    if [ "$1" ]; then
+      LOG DelDefaultRoutes $*
+
+      for router in $1; do
+        LOG route del default gw $router
+        route del default gw $router
+      done
+    fi
+}
+
+# ping a host
+# args: $1 = dotquad address of the host
+#
+PingNode() {
+    LOG PingNode $*
+    if ping -q -c 1 -w 2 $1 ; then
+	return 0;
+    fi
+    return 1;
+}
+
+# Check (and add route, if alive) default routers
+# args: $1 = routers list
+# returns: 0 if at least one router is alive.
+#
+CheckRouterList() {
+    local router
+    local succeed=1
+
+    LOG CheckRouterList $*
+
+    for router in $1; do
+      if PingNode $router ; then
+	succeed=0
+        route add default gw $router
+      fi
+    done
+    return $succeed
+}
+
+# Delete/create static routes.
+# args: $1 = operation (del/add)
+#       $2 = routes list in format "dst1 nexthop1 dst2 ..."
+#
+# BEWARE: this feature of DHCP is obsolete, because does not
+#         support subnetting.
+#
+X-StaticRouteList() {
+    local op=$1
+    local lst="$2"
+    local masklen
+
+    LOG X-StaticRouteList $*
+
+    if [ "$lst" ]; then
+      set $lst
+      while [ $# -gt 1 ]; do
+	route $op -net $1 netmask `ABCMask "$1"` gw $2
+	shift; shift;
+      done
+   fi
+}
+
+# Create static routes.
+# arg: $1 = routes list in format "dst1 nexthop1 dst2 ..."
+#
+AddStaticRouteList() {
+    LOG AddStaticRouteList $*
+    X-StaticRouteList add "$1"
+}
+
+# Delete static routes.
+# arg: $1 = routes list in format "dst1 nexthop1 dst2 ..."
+#
+DelStaticRouteList() {
+    LOG DelStaticRouteList $*
+    X-StaticRouteList del "$1"
+}
+
+# Broadcast unsolicited ARP to update neighbours' caches.
+# args: $1 = interface
+#       $2 = address
+#
+UnsolicitedARP() {
+    if [ -f /sbin/arping ]; then
+	/sbin/arping -A -c 1 -I "$1" "$2" &
+	(sleep 2 ; /sbin/arping -U -c 1 -I "$1" "$2" ) &
+    fi
+}
+
+# Duplicate address detection.
+# args: $1 = interface
+#       $2 = test address
+# returns: 0, if DAD succeeded.
+DAD() {
+  if [ -f /sbin/arping ]; then
+	/sbin/arping -c 2 -w 3 -D -I "$1" "$2"
+	return $?
+  fi
+  return 0
+}
+
+
+# Setup resolver.
+# args: NO
+#       domain and nameserver list are passed in global variables.
+#
+# NOTE: we try to be careful and not to break user supplied resolv.conf.
+#       The script mangles it, only if it has dhcp magic signature.
+#
+UpdateDNS() {
+    local nameserver
+    local idstring="#### Generated by DHCPCD"
+
+    LOG UpdateDNS $*
+
+    if [ "$new_domain_name" = "" -a "$new_domain_name_servers" = "" ]; then
+	return 0;
+    fi
+
+    echo $idstring > /etc/resolv.conf.dhcp
+    if [ "$new_domain_name" ]; then
+	echo search $new_domain_name >> /etc/resolv.conf.dhcp
+    fi
+    echo options ndots:1 >> /etc/resolv.conf.dhcp
+
+    if [ "$new_domain_name_servers" ]; then
+	for nameserver in $new_domain_name_servers; do
+	    echo nameserver $nameserver >> /etc/resolv.conf.dhcp
+	done
+    else
+	echo nameserver 127.0.0.1 >> /etc/resolv.conf.dhcp
+    fi
+
+    if [ -f /etc/resolv.conf ]; then
+	if [ "`head -1 /etc/resolv.conf`" != "$idstring" ]; then
+	    return 0
+	fi
+	if [ "$old_domain_name" = "$new_domain_name" -a
+	     "$new_domain_name_servers" = "$old_domain_name_servers" ]; then
+	     return 0
+	fi
+    fi
+    mv /etc/resolv.conf.dhcp /etc/resolv.conf
+}
+
+case $reason in
+NBI)
+  exit 1
+  ;;
+
+MEDIUM)
+  exit 0
+  ;;
+
+PREINIT)
+  ifconfig $interface:dhcp down
+  ifconfig $interface:dhcp1 down
+  if [ -d /proc/sys/net/ipv4/conf/$interface ]; then
+    ifconfig $interface:dhcp 10.10.10.10 netmask 255.255.255.255
+    ifconfig $interface:dhcp down
+    if [ -d /proc/sys/net/ipv4/conf/$interface ]; then
+	LOG The interface $interface already configured.
+    fi
+  fi
+  ifconfig $interface:dhcp up
+  exit 0
+  ;;
+
+ARPSEND)
+  exit 0
+  ;;
+
+ARPCHECK)
+  if DAD "$interface" "$check_ip_address" ; then
+    exit 0
+  fi
+  exit 1
+  ;;
+
+BOUND|RENEW|REBIND|REBOOT)
+  if [ "$old_ip_address" -a "$alias_ip_address" -a \
+	"$alias_ip_address" != "$old_ip_address" ]; then
+    DelINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
+  fi
+  if [ "$old_ip_address" -a "$old_ip_address" != "$new_ip_address" ]; then
+    DelINETAddr "$interface" "$old_ip_address" "$old_subnet_mask" "$old_broadcast_address" dhcp
+    DelDefaultRoutes "$old_routers"
+    DelStaticRouteList "$old_static_routes"
+  fi
+  if [ "$old_ip_address" = "" -o "$old_ip_address" != "$new_ip_address" -o \
+       "$reason" = "BOUND" -o "$reason" = "REBOOT" ]; then
+    AddINETAddr "$interface" "$new_ip_address" "$new_subnet_mask" "$new_broadcast_address" dhcp
+    AddStaticRouteList "$new_static_routes"
+    AddDefaultRoutes "$new_routers"
+    UnsolicitedARP "$interface" "$new_ip_address"
+  fi
+  if [ "$new_ip_address" != "$alias_ip_address" -a "$alias_ip_address" ]; then
+    AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
+  fi
+  UpdateDNS
+  exit 0
+  ;;
+
+EXPIRE|FAIL)
+  if [ "$alias_ip_address" ]; then
+    DelINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
+  fi
+  if [ "$old_ip_address" ]; then
+    DelINETAddr "$interface" "$old_ip_address" "$old_subnet_mask" "$old_broadcast_address" dhcp
+    DelDefaultRoutes "$old_routers"
+    DelStaticRouteList "$old_static_routes"
+  fi
+  if [ "$alias_ip_address" ]; then
+    AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
+  fi
+  exit 0
+  ;;
+
+TIMEOUT)
+  if [ "$alias_ip_address" ]; then
+    DelINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
+  fi
+# Seems, <null address> means, that no more old leases found.
+# Or does it mean bug in dhcpcd? 8) Fail for now.
+  if [ "$new_ip_address" = "<null address>" ]; then
+    if [ "$old_ip_address" ]; then
+	DelINETAddr "$interface" "$old_ip_address" "$old_subnet_mask" "$old_broadcast_address" dhcp
+    fi
+    if [ "$alias_ip_address" ]; then
+        AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
+    fi
+    exit 1
+  fi
+  if DAD "$interface" "$new_ip_address" ; then
+    AddINETAddr "$interface" "$new_ip_address" "$new_subnet_mask" "$new_broadcast_address" dhcp
+    UnsolicitedARP "$interface" "$new_ip_address"
+    if [ "$alias_ip_address" -a "$alias_ip_address" != "$new_ip_address" ]; then
+      AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
+      UnsolicitedARP "$interface" "$alias_ip_address"
+    fi
+    if CheckRouterList "$new_routers" ; then
+	AddStaticRouteList "$new_static_routes"
+	UpdateDNS
+	exit 0
+    fi
+  fi
+  DelINETAddr "$interface" "$new_ip_address" "$new_subnet_mask" "$new_broadcast_address" dhcp
+  DelDefaultRoutes "$old_routers"
+  DelStaticRouteList "$old_static_routes"
+  if [ "$alias_ip_address" ]; then
+    AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1
+  fi
+  exit 1
+  ;;
+esac
+
+exit 0
diff --git a/examples/diffserv/Edge1 b/examples/diffserv/Edge1
new file mode 100644
index 0000000..4ddffdd
--- /dev/null
+++ b/examples/diffserv/Edge1
@@ -0,0 +1,68 @@
+#! /bin/sh -x
+#
+# sample script on using the ingress capabilities
+# This script just tags on the ingress interfac using Ipchains
+# the result is used for fast classification and re-marking
+# on the egress interface
+#
+#path to various utilities;
+#change to reflect yours.
+#
+IPROUTE=/root/DS-6-beta/iproute2-990530-dsing
+TC=$IPROUTE/tc/tc
+IP=$IPROUTE/ip/ip
+IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains
+INDEV=eth2
+EGDEV="dev eth1"
+#
+# tag all incoming packets from host 10.2.0.24 to value 1
+# tag all incoming packets from host 10.2.0.3 to value 2
+# tag the rest of incoming packets from subnet 10.2.0.0/24 to value 3
+#These values are used in the egress
+#
+############################################################ 
+$IPCHAINS -A input -s 10.2.0.4/24 -m 3
+$IPCHAINS -A input -i $INDEV -s 10.2.0.24 -m 1
+$IPCHAINS -A input -i $INDEV -s 10.2.0.3 -m 2
+
+######################## Egress side ########################
+
+
+# attach a dsmarker
+#
+$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 set_tc_index
+#
+# values of the DSCP to change depending on the class
+#
+#becomes EF
+$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \
+       value 0xb8
+#becomes AF11
+$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \
+       value 0x28
+#becomes AF21
+$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \
+       value 0x48
+#
+#
+# The class mapping
+#
+$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 1 fw classid 1:1
+$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 2 fw classid 1:2
+$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 3 fw classid 1:3
+#
+
+#
+echo "---- qdisc parameters Ingress  ----------"
+$TC qdisc ls dev $INDEV
+echo "---- Class parameters Ingress  ----------"
+$TC class ls dev $INDEV
+echo "---- filter parameters Ingress ----------"
+$TC filter ls dev $INDEV parent 1:0
+
+echo "---- qdisc parameters Egress  ----------"
+$TC qdisc ls $EGDEV
+echo "---- Class parameters Egress  ----------"
+$TC class ls $EGDEV
+echo "---- filter parameters Egress ----------"
+$TC filter ls $EGDEV parent 1:0
diff --git a/examples/diffserv/Edge2 b/examples/diffserv/Edge2
new file mode 100644
index 0000000..2f78da2
--- /dev/null
+++ b/examples/diffserv/Edge2
@@ -0,0 +1,87 @@
+#! /bin/sh -x
+#
+# sample script on using the ingress capabilities
+# This script tags the fwmark on the ingress interface using IPchains
+# the result is used first for policing on the Ingress interface then
+# for fast classification and re-marking
+# on the egress interface
+#
+#path to various utilities;
+#change to reflect yours.
+#
+IPROUTE=/root/DS-6-beta/iproute2-990530-dsing
+TC=$IPROUTE/tc/tc
+IP=$IPROUTE/ip/ip
+IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains
+INDEV=eth2
+EGDEV="dev eth1"
+#
+# tag all incoming packets from host 10.2.0.24 to value 1
+# tag all incoming packets from host 10.2.0.3 to value 2
+# tag the rest of incoming packets from subnet 10.2.0.0/24 to value 3
+#These values are used in the egress
+############################################################ 
+$IPCHAINS -A input -s 10.2.0.0/24 -m 3
+$IPCHAINS -A input -i $INDEV -s 10.2.0.24 -m 1
+$IPCHAINS -A input -i $INDEV -s 10.2.0.3 -m 2
+############################################################ 
+#
+# install the ingress qdisc on the ingress interface
+############################################################ 
+$TC qdisc add dev $INDEV handle ffff: ingress
+############################################################ 
+
+#
+# attach a fw classifier to the ingress which polices anything marked
+# by ipchains to tag value 3 (The rest of the subnet packets -- not
+# tag 1 or 2) to not go beyond 1.5Mbps
+# Allow up to at least 60 packets to burst (assuming maximum packet 
+# size of # 1.5 KB) in the long run and upto about 6 packets in the
+# shot run
+
+############################################################ 
+$TC filter add dev $INDEV parent ffff: protocol ip prio 50 handle 3 fw \
+police rate 1500kbit burst 90k mtu 9k drop flowid :1
+############################################################ 
+
+######################## Egress side ########################
+
+
+# attach a dsmarker
+#
+$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64
+#
+# values of the DSCP to change depending on the class
+#
+$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \
+       value 0xb8
+$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \
+       value 0x28
+$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \
+       value 0x48
+#
+#
+# The class mapping
+#
+$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 1 fw classid 1:1
+$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 2 fw classid 1:2
+$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 3 fw classid 1:3
+#
+
+#
+echo "---- qdisc parameters Ingress  ----------"
+$TC qdisc ls dev $INDEV
+echo "---- Class parameters Ingress  ----------"
+$TC class ls dev $INDEV
+echo "---- filter parameters Ingress ----------"
+$TC filter ls dev $INDEV parent ffff:
+
+echo "---- qdisc parameters Egress  ----------"
+$TC qdisc ls $EGDEV
+echo "---- Class parameters Egress  ----------"
+$TC class ls $EGDEV
+echo "---- filter parameters Egress ----------"
+$TC filter ls $EGDEV parent 1:0
+#
+#deleting the ingress qdisc
+#$TC qdisc del $DEV ingress
diff --git a/examples/diffserv/Edge31-ca-u32 b/examples/diffserv/Edge31-ca-u32
new file mode 100644
index 0000000..25e6c0b
--- /dev/null
+++ b/examples/diffserv/Edge31-ca-u32
@@ -0,0 +1,170 @@
+#! /bin/sh -x
+#
+# sample script on using the ingress capabilities using u32 classifier
+# This script tags tcindex based on metering on the ingress 
+# interface the result is used for fast classification and re-marking
+# on the egress interface
+# This is an example of a color aware mode marker with PIR configured
+# based on draft-wahjak-mcm-00.txt (section 3.1)
+#
+# The colors are defined using the Diffserv Fields
+#path to various utilities;
+#change to reflect yours.
+#
+IPROUTE=/usr/src/iproute2-current
+TC=$IPROUTE/tc/tc
+IP=$IPROUTE/ip/ip
+INDEV=eth0
+EGDEV="dev eth1"
+CIR1=1500kbit
+CIR2=1000kbit
+
+#The CBS is about 60 MTU sized packets
+CBS1=90k
+CBS2=90k
+
+############################################################ 
+#
+# install the ingress qdisc on the ingress interface
+$TC qdisc add dev $INDEV handle ffff: ingress
+############################################################ 
+#
+# Create u32 filters 
+$TC filter add dev $INDEV parent ffff: protocol ip prio 4 handle 1: u32 \
+divisor 1
+############################################################ 
+
+# The meters: Note that we have shared meters in this case as identified
+# by the index parameter
+meter1=" police index 1 rate $CIR1 burst $CBS1 "
+meter2=" police index 2 rate $CIR2 burst $CBS1 "
+meter3=" police index 3 rate $CIR2 burst $CBS2 "
+meter4=" police index 4 rate $CIR1 burst $CBS2 "
+meter5=" police index 5 rate $CIR1 burst $CBS2 "
+
+# All packets are marked with a tcindex value which is used on the egress
+# tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE
+
+# *********************** AF41 *************************** 
+#AF41 (DSCP 0x22) is passed on with a tcindex value 1
+#if it doesnt exceed its CIR/CBS 
+#policer 1  is used.
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 4 u32 \
+match ip tos 0x88 0xfc \
+$meter1 \
+continue flowid :1
+#
+# if it exceeds the above but not the extra rate/burst below, it gets a 
+# tcindex value  of 2
+# policer 2 is used
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 5 u32 \
+match ip tos 0x88 0xfc \
+$meter2 \
+continue flowid :2
+#
+# if it exceeds the above but not the rule below, it gets a tcindex value
+# of 3 (policer 3)
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \
+match ip tos 0x88 0xfc \
+$meter3 \
+drop flowid :3
+#
+
+# *********************** AF42 *************************** 
+#AF42 (DSCP 0x24) from is passed on with a tcindex value 2
+#if it doesnt exceed its CIR/CBS 
+#policer 2 is used. Note that this is shared with the AF41
+#
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 5 u32 \
+match ip tos 0x90 0xfc \
+$meter2 \
+continue flowid :2
+#
+# if it exceeds the above but not the rule below, it gets a tcindex value
+# of 3 (policer 3)
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \
+match ip tos 0x90 0xfc \
+$meter3 \
+drop flowid :3
+#
+# *********************** AF43 *************************** 
+#
+#AF43 (DSCP 0x26) from is passed on with a tcindex value 3
+#if it doesnt exceed its CIR/CBS
+#policer 3 is used. Note that this is shared with the AF41 and AF42
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \
+match ip tos 0x98 0xfc \
+$meter3 \
+drop flowid :3
+#
+# *********************** BE *************************** 
+#
+# Anything else (not from the AF4*) gets discarded if it 
+# exceeds 1Mbps and by default goes to BE if it doesnt
+# Note that the BE class is also used by the AF4* in the worst
+# case
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 7 u32 \
+match ip src 0/0\
+$meter4 \
+drop flowid :4
+
+######################## Egress side ########################
+
+# attach a dsmarker
+#
+$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64
+#
+# values of the DSCP to change depending on the class
+#note that the ECN bits are masked out
+#
+#AF41 (0x88 is 0x22 shifted to the right by two bits)
+#
+$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \
+       value 0x88
+#AF42
+$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \
+       value 0x90
+#AF43
+$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \
+       value 0x98
+#BE
+$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \
+       value 0x0
+#
+#
+# The class mapping
+#
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 1 tcindex classid 1:1
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 2 tcindex  classid 1:2
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 3 tcindex  classid 1:3
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 4 tcindex  classid 1:4
+#
+
+#
+echo "---- qdisc parameters Ingress  ----------"
+$TC qdisc ls dev $INDEV
+echo "---- Class parameters Ingress  ----------"
+$TC class ls dev $INDEV
+echo "---- filter parameters Ingress ----------"
+$TC filter ls dev $INDEV parent ffff:
+
+echo "---- qdisc parameters Egress  ----------"
+$TC qdisc ls $EGDEV
+echo "---- Class parameters Egress  ----------"
+$TC class ls $EGDEV
+echo "---- filter parameters Egress ----------"
+$TC filter ls $EGDEV parent 1:0
+#
+#deleting the ingress qdisc
+#$TC qdisc del $INDEV ingress
diff --git a/examples/diffserv/Edge31-cb-chains b/examples/diffserv/Edge31-cb-chains
new file mode 100644
index 0000000..d7faae9
--- /dev/null
+++ b/examples/diffserv/Edge31-cb-chains
@@ -0,0 +1,132 @@
+#! /bin/sh -x
+#
+# sample script on using the ingress capabilities
+# This script fwmark tags(IPchains) based on metering on the ingress 
+# interface the result is used for fast classification and re-marking
+# on the egress interface
+# This is an example of a color blind mode marker with no PIR configured
+# based on draft-wahjak-mcm-00.txt (section 3.1)
+#
+#path to various utilities;
+#change to reflect yours.
+#
+IPROUTE=/root/DS-6-beta/iproute2-990530-dsing
+TC=$IPROUTE/tc/tc
+IP=$IPROUTE/ip/ip
+IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains
+INDEV=eth2
+EGDEV="dev eth1"
+CIR1=1500kbit
+CIR2=1000kbit
+
+#The CBS is about 60 MTU sized packets
+CBS1=90k
+CBS2=90k
+
+meter1="police rate $CIR1 burst $CBS1 "
+meter2="police rate $CIR1 burst $CBS2 "
+meter3="police rate $CIR2 burst $CBS1 "
+meter4="police rate $CIR2 burst $CBS2 "
+meter5="police rate $CIR2 burst $CBS2 "
+#
+# tag the rest of incoming packets from subnet 10.2.0.0/24 to fw value 1
+# tag all incoming packets from any other subnet to fw tag 2
+############################################################ 
+$IPCHAINS -A input -i $INDEV -s 0/0 -m 2
+$IPCHAINS -A input -i $INDEV -s 10.2.0.0/24 -m 1
+#
+############################################################ 
+# install the ingress qdisc on the ingress interface
+$TC qdisc add dev $INDEV handle ffff: ingress
+#
+############################################################ 
+
+# All packets are marked with a tcindex value which is used on the egress
+# tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE
+#
+############################################################ 
+# 
+# anything with fw tag of 1 is passed on with a tcindex value 1
+#if it doesnt exceed its allocated rate (CIR/CBS)
+# 
+$TC filter add dev $INDEV parent ffff: protocol ip prio 4 handle 1 fw \
+$meter1 \
+continue flowid 4:1
+#
+# if it exceeds the above but not the extra rate/burst below, it gets a 
+#tcindex value  of 2
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 5 handle 1 fw \
+$meter2 \
+continue flowid 4:2
+#
+# if it exceeds the above but not the rule below, it gets a tcindex value
+# of 3
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 6 handle 1 fw \
+$meter3 \
+drop flowid 4:3
+#
+# Anything else (not from the subnet 10.2.0.24/24) gets discarded if it 
+# exceeds 1Mbps and by default goes to BE if it doesnt
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 6 handle 2 fw \
+$meter5 \
+drop flowid 4:4
+
+
+######################## Egress side ########################
+
+
+# attach a dsmarker
+#
+$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64
+#
+# values of the DSCP to change depending on the class
+#note that the ECN bits are masked out
+#
+#AF41 (0x88 is 0x22 shifted to the right by two bits)
+#
+$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \
+       value 0x88
+#AF42
+$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \
+       value 0x90
+#AF43
+$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \
+       value 0x98
+#BE
+$TC class change $EGDEV classid 1:4 dsmark mask 0x3 \
+       value 0x0
+#
+#
+# The class mapping (using tcindex; could easily have
+# replaced it with the fw classifier instead)
+#
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 1 tcindex classid 1:1
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 2 tcindex  classid 1:2
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 3 tcindex  classid 1:3
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 4 tcindex  classid 1:4
+#
+
+#
+echo "---- qdisc parameters Ingress  ----------"
+$TC qdisc ls dev $INDEV
+echo "---- Class parameters Ingress  ----------"
+$TC class ls dev $INDEV
+echo "---- filter parameters Ingress ----------"
+$TC filter ls dev $INDEV parent ffff:
+
+echo "---- qdisc parameters Egress  ----------"
+$TC qdisc ls $EGDEV
+echo "---- Class parameters Egress  ----------"
+$TC class ls $EGDEV
+echo "---- filter parameters Egress ----------"
+$TC filter ls $EGDEV parent 1:0
+#
+#deleting the ingress qdisc
+#$TC qdisc del $INDEV ingress
diff --git a/examples/diffserv/Edge32-ca-u32 b/examples/diffserv/Edge32-ca-u32
new file mode 100644
index 0000000..edf21e4
--- /dev/null
+++ b/examples/diffserv/Edge32-ca-u32
@@ -0,0 +1,198 @@
+#! /bin/sh -x
+#
+# sample script on using the ingress capabilities using u32 classifier
+# This script tags tcindex based on metering on the ingress 
+# interface the result is used for fast classification and re-marking
+# on the egress interface
+# This is an example of a color aware mode marker with PIR configured
+# based on draft-wahjak-mcm-00.txt (section 3.2)
+#
+# The colors are defined using the Diffserv Fields
+#path to various utilities;
+#change to reflect yours.
+#
+IPROUTE=/root/DS-6-beta/iproute2-990530-dsing
+TC=$IPROUTE/tc/tc
+IP=$IPROUTE/ip/ip
+IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains
+INDEV=eth2
+EGDEV="dev eth1"
+CIR1=1000kbit
+CIR2=500kbit
+# the PIR is what is in excess of the CIR
+PIR1=1000kbit
+PIR2=500kbit
+
+#The CBS is about 60 MTU sized packets
+CBS1=90k
+CBS2=90k
+#the EBS is about 20 max sized packets
+EBS1=30k
+EBS2=30k
+
+# The meters: Note that we have shared meters in this case as identified
+# by the index parameter
+meter1=" police index 1 rate $CIR1 burst $CBS1 "
+meter1a=" police index 2 rate $PIR1 burst $EBS1 "
+meter2=" police index 3 rate $CIR2 burst $CBS1 "
+meter2a=" police index 4 rate $PIR2 burst $EBS1 "
+meter3=" police index 5 rate $CIR2 burst $CBS2 "
+meter3a=" police index 6 rate $PIR2 burst $EBS2 "
+meter4=" police index 7 rate $CIR1 burst $CBS2 "
+
+############################################################ 
+#
+# install the ingress qdisc on the ingress interface
+$TC qdisc add dev $INDEV handle ffff: ingress
+############################################################ 
+#
+# All packets are marked with a tcindex value which is used on the egress
+# tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE
+#
+# *********************** AF41 *************************** 
+#AF41 (DSCP 0x22) from is passed on with a tcindex value 1
+#if it doesnt exceed its CIR/CBS + PIR/EBS
+#policer 1  is used.
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 1 u32 \
+match ip tos 0x88 0xfc \
+$meter1 \
+continue flowid :1
+$TC filter add dev $INDEV parent ffff: protocol ip prio 2 u32 \
+match ip tos 0x88 0xfc \
+$meter1a \
+continue flowid :1
+#
+# if it exceeds the above but not the extra rate/burst below, it gets a 
+# tcindex value  of 2
+# policer 2 is used
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 3 u32 \
+match ip tos 0x88 0xfc \
+$meter2 \
+continue flowid :2
+$TC filter add dev $INDEV parent ffff: protocol ip prio 4 u32 \
+match ip tos 0x88 0xfc \
+$meter2a \
+continue flowid :2
+#
+# if it exceeds the above but not the rule below, it gets a tcindex value
+# of 3 (policer 3)
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 5 u32 \
+match ip tos 0x88 0xfc \
+$meter3 \
+continue flowid :3
+$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \
+match ip tos 0x88 0xfc \
+$meter3a \
+drop flowid :3
+#
+# *********************** AF42 *************************** 
+#AF42 (DSCP 0x24) from is passed on with a tcindex value 2
+#if it doesnt exceed its CIR/CBS + PIR/EBS
+#policer 2 is used. Note that this is shared with the AF41
+#
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 8 u32 \
+match ip tos 0x90 0xfc \
+$meter2 \
+continue flowid :2
+$TC filter add dev $INDEV parent ffff: protocol ip prio 9 u32 \
+match ip tos 0x90 0xfc \
+$meter2a \
+continue flowid :2
+#
+# if it exceeds the above but not the rule below, it gets a tcindex value
+# of 3 (policer 3)
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 10 u32 \
+match ip tos 0x90 0xfc \
+$meter3 \
+continue flowid :3
+$TC filter add dev $INDEV parent ffff: protocol ip prio 11 u32 \
+match ip tos 0x90 0xfc \
+$meter3a \
+drop flowid :3
+
+#
+# *********************** AF43 *************************** 
+#
+#AF43 (DSCP 0x26) from is passed on with a tcindex value 3
+#if it doesnt exceed its CIR/CBS + PIR/EBS
+#policer 3 is used. Note that this is shared with the AF41 and AF42
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 13 u32 \
+match ip tos 0x98 0xfc \
+$meter3 \
+continue flowid :3
+$TC filter add dev $INDEV parent ffff: protocol ip prio 14 u32 \
+match ip tos 0x98 0xfc \
+$meter3a \
+drop flowid :3
+#
+## *********************** BE *************************** 
+##
+## Anything else (not from the AF4*) gets discarded if it 
+## exceeds 1Mbps and by default goes to BE if it doesnt
+## Note that the BE class is also used by the AF4* in the worst
+## case
+##
+$TC filter add dev $INDEV parent ffff: protocol ip prio 16 u32 \
+match ip src 0/0\
+$meter4 \
+drop flowid :4
+
+######################## Egress side ########################
+
+# attach a dsmarker
+#
+$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64
+#
+# values of the DSCP to change depending on the class
+#note that the ECN bits are masked out
+#
+#AF41 (0x88 is 0x22 shifted to the right by two bits)
+#
+$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \
+       value 0x88
+#AF42
+$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \
+       value 0x90
+#AF43
+$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \
+       value 0x98
+#BE
+$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \
+       value 0x0
+#
+#
+# The class mapping
+#
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 1 tcindex classid 1:1
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 2 tcindex  classid 1:2
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 3 tcindex  classid 1:3
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 4 tcindex  classid 1:4
+#
+
+#
+echo "---- qdisc parameters Ingress  ----------"
+$TC qdisc ls dev $INDEV
+echo "---- Class parameters Ingress  ----------"
+$TC class ls dev $INDEV
+echo "---- filter parameters Ingress ----------"
+$TC filter ls dev $INDEV parent ffff:
+
+echo "---- qdisc parameters Egress  ----------"
+$TC qdisc ls $EGDEV
+echo "---- Class parameters Egress  ----------"
+$TC class ls $EGDEV
+echo "---- filter parameters Egress ----------"
+$TC filter ls $EGDEV parent 1:0
+#
+#deleting the ingress qdisc
+#$TC qdisc del $INDEV ingress
diff --git a/examples/diffserv/Edge32-cb-chains b/examples/diffserv/Edge32-cb-chains
new file mode 100644
index 0000000..804fad1
--- /dev/null
+++ b/examples/diffserv/Edge32-cb-chains
@@ -0,0 +1,144 @@
+#! /bin/sh -x
+#
+# sample script on using the ingress capabilities
+# This script fwmark tags(IPchains) based on metering on the ingress 
+# interface the result is used for fast classification and re-marking
+# on the egress interface
+# This is an example of a color blind mode marker with no PIR configured
+# based on draft-wahjak-mcm-00.txt (section 3.1)
+#
+#path to various utilities;
+#change to reflect yours.
+#
+IPROUTE=/root/DS-6-beta/iproute2-990530-dsing
+TC=$IPROUTE/tc/tc
+IP=$IPROUTE/ip/ip
+IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains
+INDEV=eth2
+EGDEV="dev eth1"
+CIR1=1500kbit
+CIR2=500kbit
+
+#The CBS is about 60 MTU sized packets
+CBS1=90k
+CBS2=90k
+
+meter1="police rate $CIR1 burst $CBS1 "
+meter1a="police rate $CIR2 burst $CBS1 "
+meter2="police rate $CIR1 burst $CBS2 "
+meter2a="police rate $CIR2 burst $CBS2 "
+meter3="police rate $CIR2 burst $CBS1 "
+meter3a="police rate $CIR2 burst $CBS1 "
+meter4="police rate $CIR2 burst $CBS2 "
+meter5="police rate $CIR1 burst $CBS2 "
+#
+# tag the rest of incoming packets from subnet 10.2.0.0/24 to fw value 1
+# tag all incoming packets from any other subnet to fw tag 2
+############################################################ 
+$IPCHAINS -A input -i $INDEV -s 0/0 -m 2
+$IPCHAINS -A input -i $INDEV -s 10.2.0.0/24 -m 1
+#
+############################################################ 
+# install the ingress qdisc on the ingress interface
+$TC qdisc add dev $INDEV handle ffff: ingress
+#
+############################################################ 
+
+# All packets are marked with a tcindex value which is used on the egress
+# tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE
+#
+############################################################ 
+# 
+# anything with fw tag of 1 is passed on with a tcindex value 1
+#if it doesnt exceed its allocated rate (CIR/CBS)
+# 
+$TC filter add dev $INDEV parent ffff: protocol ip prio 1 handle 1 fw \
+$meter1 \
+continue flowid 4:1
+$TC filter add dev $INDEV parent ffff: protocol ip prio 2 handle 1 fw \
+$meter1a \
+continue flowid 4:1
+#
+# if it exceeds the above but not the extra rate/burst below, it gets a 
+#tcindex value  of 2
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 3 handle 1 fw \
+$meter2 \
+continue flowid 4:2
+$TC filter add dev $INDEV parent ffff: protocol ip prio 4 handle 1 fw \
+$meter2a \
+continue flowid 4:2
+#
+# if it exceeds the above but not the rule below, it gets a tcindex value
+# of 3
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 5 handle 1 fw \
+$meter3 \
+continue flowid 4:3
+$TC filter add dev $INDEV parent ffff: protocol ip prio 6 handle 1 fw \
+$meter3a \
+drop flowid 4:3
+#
+# Anything else (not from the subnet 10.2.0.24/24) gets discarded if it 
+# exceeds 1Mbps and by default goes to BE if it doesnt
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 7 handle 2 fw \
+$meter5 \
+drop flowid 4:4
+
+
+######################## Egress side ########################
+
+
+# attach a dsmarker
+#
+$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64
+#
+# values of the DSCP to change depending on the class
+#note that the ECN bits are masked out
+#
+#AF41 (0x88 is 0x22 shifted to the right by two bits)
+#
+$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \
+       value 0x88
+#AF42
+$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \
+       value 0x90
+#AF43
+$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \
+       value 0x98
+#BE
+$TC class change $EGDEV classid 1:4 dsmark mask 0x3 \
+       value 0x0
+#
+#
+# The class mapping (using tcindex; could easily have
+# replaced it with the fw classifier instead)
+#
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 1 tcindex classid 1:1
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 2 tcindex  classid 1:2
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 3 tcindex  classid 1:3
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 4 tcindex  classid 1:4
+#
+
+#
+echo "---- qdisc parameters Ingress  ----------"
+$TC qdisc ls dev $INDEV
+echo "---- Class parameters Ingress  ----------"
+$TC class ls dev $INDEV
+echo "---- filter parameters Ingress ----------"
+$TC filter ls dev $INDEV parent ffff:
+
+echo "---- qdisc parameters Egress  ----------"
+$TC qdisc ls $EGDEV
+echo "---- Class parameters Egress  ----------"
+$TC class ls $EGDEV
+echo "---- filter parameters Egress ----------"
+$TC filter ls $EGDEV parent 1:0
+#
+#deleting the ingress qdisc
+#$TC qdisc del $INDEV ingress
diff --git a/examples/diffserv/Edge32-cb-u32 b/examples/diffserv/Edge32-cb-u32
new file mode 100644
index 0000000..cc2ebb4
--- /dev/null
+++ b/examples/diffserv/Edge32-cb-u32
@@ -0,0 +1,145 @@
+#! /bin/sh 
+#
+# sample script on using the ingress capabilities using u32 classifier
+# This script tags tcindex based on metering on the ingress 
+# interface the result is used for fast classification and re-marking
+# on the egress interface
+# This is an example of a color blind mode marker with PIR configured
+# based on draft-wahjak-mcm-00.txt (section 3.2)
+#
+#path to various utilities;
+#change to reflect yours.
+#
+IPROUTE=/root/DS-6-beta/iproute2-990530-dsing
+TC=$IPROUTE/tc/tc
+IP=$IPROUTE/ip/ip
+INDEV=eth2
+EGDEV="dev eth1"
+CIR1=1000kbit
+CIR2=1000kbit
+# The PIR is the excess (in addition to the CIR i.e if always
+# going to the PIR --> average rate is CIR+PIR)
+PIR1=1000kbit
+PIR2=500kbit
+
+#The CBS is about 60 MTU sized packets
+CBS1=90k
+CBS2=90k
+#the EBS is about 10 max sized packets
+EBS1=15k
+EBS2=15k
+# The meters
+meter1=" police rate $CIR1 burst $CBS1 "
+meter1a=" police rate $PIR1 burst $EBS1 "
+meter2=" police rate $CIR2 burst $CBS1 "
+meter2a="police rate $PIR2 burst $CBS1 "
+meter3=" police rate $CIR2 burst $CBS2 "
+meter3a=" police rate $PIR2 burst $EBS2 "
+meter4=" police rate $CIR1 burst $CBS2 "
+meter5=" police rate $CIR1 burst $CBS2 "
+
+
+# install the ingress qdisc on the ingress interface
+############################################################ 
+$TC qdisc add dev $INDEV handle ffff: ingress
+############################################################ 
+#
+############################################################ 
+
+# All packets are marked with a tcindex value which is used on the egress
+# NOTE: tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE
+# 
+#anything from subnet 10.2.0.2/24 is passed on with a tcindex value 1
+#if it doesnt exceed its CIR/CBS + PIR/EBS
+# 
+$TC filter add dev $INDEV parent ffff: protocol ip prio 1 u32 \
+match ip src 10.2.0.0/24 $meter1 \
+continue flowid :1
+$TC filter add dev $INDEV parent ffff: protocol ip prio 2 u32 \
+match ip src 10.2.0.0/24 $meter1a \
+continue flowid :1
+
+#
+# if it exceeds the above but not the extra rate/burst below, it gets a 
+#tcindex value  of 2
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 3 u32 \
+match ip src 10.2.0.0/24 $meter2 \
+continue flowid :2
+$TC filter add dev $INDEV parent ffff: protocol ip prio 4 u32 \
+match ip src 10.2.0.0/24 $meter2a \
+continue flowid :2
+#
+# if it exceeds the above but not the rule below, it gets a tcindex value
+# of 3
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 5 u32 \
+match ip src 10.2.0.0/24 $meter3 \
+continue flowid :3
+$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \
+match ip src 10.2.0.0/24 $meter3a \
+drop flowid :3
+#
+#
+# Anything else (not from the subnet 10.2.0.24/24) gets discarded if it 
+# exceeds 1Mbps and by default goes to BE if it doesnt
+#
+$TC filter add dev $INDEV parent ffff: protocol ip prio 7 u32 \
+match ip src 0/0 $meter5 \
+drop flowid :4
+
+
+######################## Egress side ########################
+
+
+# attach a dsmarker
+#
+$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64
+#
+# values of the DSCP to change depending on the class
+#note that the ECN bits are masked out
+#
+#AF41 (0x88 is 0x22 shifted to the right by two bits)
+#
+$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \
+       value 0x88
+#AF42
+$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \
+       value 0x90
+#AF43
+$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \
+       value 0x98
+#BE
+$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \
+       value 0x0
+#
+#
+# The class mapping
+#
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 1 tcindex classid 1:1
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 2 tcindex  classid 1:2
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 3 tcindex  classid 1:3
+$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \
+          handle 4 tcindex  classid 1:4
+#
+
+#
+echo "---- qdisc parameters Ingress  ----------"
+$TC qdisc ls dev $INDEV
+echo "---- Class parameters Ingress  ----------"
+$TC class ls dev $INDEV
+echo "---- filter parameters Ingress ----------"
+$TC filter ls dev $INDEV parent ffff:
+
+echo "---- qdisc parameters Egress  ----------"
+$TC qdisc ls $EGDEV
+echo "---- Class parameters Egress  ----------"
+$TC class ls $EGDEV
+echo "---- filter parameters Egress ----------"
+$TC filter ls $EGDEV parent 1:0
+#
+#deleting the ingress qdisc
+#$TC qdisc del $INDEV ingress
diff --git a/examples/diffserv/README b/examples/diffserv/README
new file mode 100644
index 0000000..ec91d63
--- /dev/null
+++ b/examples/diffserv/README
@@ -0,0 +1,98 @@
+
+Note all these are mere examples which can be customized to your needs
+
+AFCBQ
+-----
+AF PHB built using CBQ, DSMARK,GRED (default in GRIO mode) ,RED for BE 
+and the tcindex classifier with some algorithmic mapping
+
+EFCBQ
+-----
+EF PHB built using CBQ (for rate control and prioritization), 
+DSMARK( to remark DSCPs), tcindex  classifier and  RED for the BE
+traffic.
+
+EFPRIO
+------
+EF PHB using the PRIO scheduler, Token Bucket to rate control EF,
+tcindex classifier, DSMARK to remark, and RED for the BE traffic
+
+EDGE scripts
+==============
+
+CB-3(1|2)-(u32/chains)
+======================
+
+
+The major differences are that the classifier is u32 on -u32 extension
+and IPchains on the chains extension. CB stands for color Blind
+and 31 is for the mode where only a CIR and CBS are defined whereas
+32 stands for a mode where a CIR/CBS + PIR/EBS are defined.
+
+Color Blind (CB)
+==========-----=
+We look at one special subnet that we are interested in for simplicty
+reasons to demonstrate the capability. We send the packets from that
+subnet to AF4*, BE or end up dropping depending on the metering results. 
+
+
+The algorithm overview is as follows:
+
+*classify:
+
+**case: subnet X
+----------------
+  if !exceed meter1 tag as AF41
+	else
+	    if !exceed meter2  tag as AF42
+	        else
+		  if !exceed meter 3 tag as AF43
+		      else 
+			 drop 
+
+default case: Any other subnet
+-------------------------------
+  if !exceed meter 5 tag as AF43
+      else
+	 drop 
+
+
+One Egress side change the DSCPs of the packets to reflect AF4* and BE
+based on the tags from the ingress.
+
+-------------------------------------------------------------
+
+Color Aware
+===========
+
+Define some meters with + policing and give them IDs eg
+
+meter1=police index 1 rate $CIR1 burst $CBS1  
+meter2=police index 2 rate $CIR2 burst $CBS2   etc 
+
+General overview:
+classify based on the DSCPs and use the policer ids to decide tagging
+
+
+*classify on ingress:
+
+switch (dscp) {
+    case AF41: /* tos&0xfc == 0x88 */
+	if (!exceed meter1) break;
+    case AF42: /* tos&0xfc == 0x90 */
+	if (!exceed meter2) {
+	    tag as AF42;
+	    break;
+	}
+    case AF43: /* tos&0xfc == 0x98 */
+	if (!exceed meter3) {
+	    tag as AF43;
+	    break;
+	} else
+	  drop;
+    default:
+	if (!exceed meter4) tag as BE;
+	else drop;
+}
+
+On the Egress side mark the proper AF tags
diff --git a/examples/diffserv/afcbq b/examples/diffserv/afcbq
new file mode 100644
index 0000000..10d6d93
--- /dev/null
+++ b/examples/diffserv/afcbq
@@ -0,0 +1,105 @@
+#!/usr/bin/perl
+#
+#
+# AF using CBQ for a single interface eth0 
+# 4 AF classes using GRED and one BE using RED
+# Things you might want to change:
+#	- the device bandwidth (set at 10Mbits)
+#	- the bandwidth allocated for each AF class and the BE class	
+#	- the drop probability associated with each AF virtual queue
+#
+# AF DSCP values used (based on AF draft 04)
+# -----------------------------------------
+# AF DSCP values
+# AF1 1. 0x0a 2. 0x0c 3. 0x0e
+# AF2 1. 0x12 2. 0x14 3. 0x16
+# AF3 1. 0x1a 2. 0x1c 3. 0x1e
+# AF4 1. 0x22 2. 0x24 3. 0x26
+
+#
+# 
+# A simple DSCP-class relationship formula used to generate
+# values in the for loop of this script; $drop stands for the
+# DP
+#	$dscp = ($class*8+$drop*2)
+#
+#  if you use GRIO buffer sharing, then GRED priority is set as follows:
+#  $gprio=$drop+1; 
+#
+
+$TC = "/usr/src/iproute2-current/tc/tc";
+$DEV = "dev lo";
+$DEV = "dev eth1";
+$DEV = "dev eth0";
+# the BE-class number
+$beclass = "5";  
+
+#GRIO buffer sharing on or off?
+$GRIO = "";
+$GRIO = "grio";
+# The bandwidth of your device
+$linerate="10Mbit";
+# The BE and AF rates
+%rate_table=();
+$berate="1500Kbit";
+$rate_table{"AF1rate"}="1500Kbit";
+$rate_table{"AF2rate"}="1500Kbit";
+$rate_table{"AF3rate"}="1500Kbit";
+$rate_table{"AF4rate"}="1500Kbit";
+#
+#
+#
+print "\n# --- General setup  ---\n";
+print "$TC qdisc add $DEV handle 1:0 root dsmark indices 64 set_tc_index\n";
+print "$TC filter add $DEV parent 1:0 protocol ip prio 1 tcindex mask 0xfc " .
+   "shift 2 pass_on\n";
+   #"shift 2\n";
+print "$TC qdisc add $DEV parent 1:0 handle 2:0 cbq bandwidth $linerate ".
+  "cell 8 avpkt 1000 mpu 64\n";
+print "$TC filter add $DEV parent 2:0 protocol ip prio 1 tcindex ".
+  "mask 0xf0 shift 4 pass_on\n";
+for $class (1..4) {
+    print "\n# --- AF Class $class specific setup---\n";
+    $AFrate=sprintf("AF%drate",$class);
+    print "$TC class add $DEV parent 2:0 classid 2:$class cbq ".
+      "bandwidth $linerate rate $rate_table{$AFrate} avpkt 1000 prio ".
+      (6-$class)." bounded allot 1514 weight 1 maxburst 21\n";
+    print "$TC filter add $DEV parent 2:0 protocol ip prio 1 handle $class ".
+      "tcindex classid 2:$class\n";
+    print "$TC qdisc add $DEV parent 2:$class gred setup DPs 3 default 2 ".
+      "$GRIO\n";
+# 
+# per DP setup
+#
+    for $drop (1..3) {
+    print "\n# --- AF Class $class DP $drop---\n";
+	$dscp = $class*8+$drop*2;
+	$tcindex = sprintf("1%x%x",$class,$drop);
+	print "$TC filter add $DEV parent 1:0 protocol ip prio 1 ".
+	  "handle $dscp tcindex classid 1:$tcindex\n";
+	$prob = $drop*0.02;
+        if ($GRIO) {
+	$gprio = $drop+1;
+	print "$TC qdisc change $DEV parent 2:$class gred limit 60KB min 15KB ".
+	  "max 45KB burst 20 avpkt 1000 bandwidth $linerate DP $drop ".
+	  "probability $prob ".
+          "prio $gprio\n";
+        } else {
+	print "$TC qdisc change $DEV parent 2:$class gred limit 60KB min 15KB ".
+	  "max 45KB burst 20 avpkt 1000 bandwidth $linerate DP $drop ".
+	  "probability $prob \n";
+	}
+    }
+}
+#
+#
+print "\n#------BE Queue setup------\n";
+print "$TC filter add $DEV parent 1:0 protocol ip prio 2 ".
+          "handle 0 tcindex mask 0 classid 1:1\n";
+print "$TC class add $DEV parent 2:0 classid 2:$beclass cbq ".
+      "bandwidth $linerate rate $berate avpkt 1000 prio 6 " .
+      "bounded allot 1514 weight 1 maxburst 21 \n";
+print "$TC filter add $DEV parent 2:0 protocol ip prio 1 handle 0 tcindex ".
+  "classid 2:5\n";
+print "$TC qdisc add $DEV parent 2:5 red limit 60KB min 15KB max 45KB ".
+  "burst 20 avpkt 1000 bandwidth $linerate probability 0.4\n";
diff --git a/examples/diffserv/ef-prio b/examples/diffserv/ef-prio
new file mode 100644
index 0000000..48611bd
--- /dev/null
+++ b/examples/diffserv/ef-prio
@@ -0,0 +1,25 @@
+#!/usr/bin/perl
+$TC = "/root/DS-6-beta/iproute2-990530-dsing/tc/tc";
+$DEV = "dev eth1";
+$efrate="1.5Mbit";
+$MTU="1.5kB";
+print "$TC qdisc add $DEV handle 1:0 root dsmark indices 64 set_tc_index\n";
+print "$TC filter add $DEV parent 1:0 protocol ip prio 1 tcindex ".
+  "mask 0xfc shift 2\n";
+print "$TC qdisc add $DEV parent 1:0 handle 2:0 prio\n";
+#
+# EF class: Maximum about one MTU sized packet allowed on the queue
+#
+print "$TC qdisc add $DEV parent 2:1 tbf rate $efrate burst $MTU limit 1.6kB\n";
+print "$TC filter add $DEV parent 2:0 protocol ip prio 1 ".
+	  "handle 0x2e tcindex classid 2:1 pass_on\n";
+#
+# BE class
+#
+print "#BE class(2:2) \n";
+print "$TC qdisc add $DEV parent 2:2 red limit 60KB ".
+	  "min 15KB max 45KB burst 20 avpkt 1000 bandwidth 10Mbit ".
+	  "probability 0.4\n";
+#
+print "$TC filter add $DEV parent 2:0 protocol ip prio 2 ".
+	  "handle 0 tcindex mask 0 classid 2:2 pass_on\n";
diff --git a/examples/diffserv/efcbq b/examples/diffserv/efcbq
new file mode 100644
index 0000000..bcc437b
--- /dev/null
+++ b/examples/diffserv/efcbq
@@ -0,0 +1,31 @@
+#!/usr/bin/perl
+#
+$TC = "/root/DS-6-beta/iproute2-990530-dsing/tc/tc";
+$DEV = "dev eth1";
+print "$TC qdisc add $DEV handle 1:0 root dsmark indices 64 set_tc_index\n";
+print "$TC filter add $DEV parent 1:0 protocol ip prio 1 tcindex ".
+  "mask 0xfc shift 2\n";
+print "$TC qdisc add $DEV parent 1:0 handle 2:0 cbq bandwidth ".
+	"10Mbit cell 8 avpkt 1000 mpu 64\n";
+#
+# EF class
+#
+print "$TC class add $DEV parent 2:0 classid 2:1 cbq bandwidth ". 
+	"10Mbit rate 1500Kbit avpkt 1000 prio 1 bounded isolated ".
+	"allot 1514 weight 1 maxburst 10 \n";
+# packet fifo for EF?
+print "$TC qdisc add $DEV parent 2:1 pfifo limit 5\n";
+print "$TC filter add $DEV parent 2:0 protocol ip prio 1 ".
+	  "handle 0x2e tcindex classid 2:1 pass_on\n";
+#
+# BE class
+#
+print "#BE class(2:2) \n";
+print "$TC class add $DEV parent 2:0 classid 2:2 cbq bandwidth ". 
+	"10Mbit rate 5Mbit avpkt 1000 prio 7 allot 1514 weight 1 ".
+	"maxburst 21 borrow split 2:0 defmap 0xffff \n";
+print "$TC qdisc add $DEV parent 2:2 red limit 60KB ".
+	  "min 15KB max 45KB burst 20 avpkt 1000 bandwidth 10Mbit ".
+	  "probability 0.4\n";
+print "$TC filter add $DEV parent 2:0 protocol ip prio 2 ".
+	  "handle 0 tcindex mask 0 classid 2:2 pass_on\n";
diff --git a/examples/diffserv/regression-testing b/examples/diffserv/regression-testing
new file mode 100644
index 0000000..0ec705c
--- /dev/null
+++ b/examples/diffserv/regression-testing
@@ -0,0 +1,125 @@
+
+These were the tests done to validate the Diffserv scripts.
+This document will be updated continously. If you do more
+thorough validation testing please post the details to the
+diffserv mailing list. 
+Nevertheless, these tests should serve for basic validation.
+
+AFCBQ, EFCBQ, EFPRIO
+----------------------
+
+generate all possible DSCPs and observe that they 
+get sent to the proper classes. In the case of AF also
+to the correct Virtual Queues.
+
+Edge1
+-----
+generate TOS values 0x0,0x10,0xbb each with IP addresses
+10.2.0.24 (mark 1), 10.2.0.3 (mark2) and 10.2.0.30 (mark 3)
+and observe that they get marked as expected.
+
+Edge2
+-----
+
+-Repeat the tests in Edge1
+-ftp with data direction from 10.2.0.2
+	*observe that the metering/policing works correctly (and the marking
+	as well). In this case the mark used will be 3
+
+Edge31-cb-chains
+----------------
+
+-ftp with data direction from 10.2.0.2
+
+	*observe that the metering/policing works correctly (and the marking
+	as well). In this case the mark used will be 1. 
+
+	Metering: The data throughput should not exceed 2*CIR1 + 2*CIR2
+	which is roughly: 5mbps
+
+	Marking: the should be a variation of marked packets:
+	AF41(TOS=0x88) AF42(0x90) AF43(0x98) and BE (0x0)
+
+More tests required to see the interaction of several sources (other
+than subnet 10.2.0.0/24).
+
+Edge31-ca-u32
+--------------
+
+Generate data using modified tcpblast from 10.2.0.2 (behind eth2) to the 
+discard port of 10.1.0.2 (behind eth1)
+
+1) generate with src tos = 0x88
+	Metering: Allocated throughput should not exceed 2*CIR1 + 2*CIR2
+	approximately 5mbps
+	Marking: Should vary between 0x88,0x90,0x98 and 0x0
+
+2) generate with src tos = 0x90
+	Metering: Allocated throughput should not exceed CIR1 + 2*CIR2
+	approximately 3.5mbps
+	Marking: Should vary between 0x90,0x98 and 0x0
+
+3) generate with src tos = 0x98
+	Metering: Allocated throughput should not exceed CIR1 + CIR2
+	approximately 2.5mbps
+	Marking: Should vary between 0x98 and 0x0
+
+4) generate with src tos any other than the above
+	Metering: Allocated throughput should not exceed CIR1 
+	approximately 1.5mbps
+	Marking: Should be consistent at 0x0
+
+TODO: Testing on how each color shares when all 4 types of packets
+are going through the edge device
+
+Edge32-cb-u32, Edge32-cb-chains
+-------------------------------
+
+-ftp with data direction from 10.2.0.2
+
+	*observe that the metering/policing works correctly (and the marking
+	as well). 
+
+	Metering: 
+        The data throughput should not exceed 2*CIR1 + 2*CIR2
+	+ 2*PIR2 + PIR1 for u32 which is roughly: 6mbps
+        The data throughput should not exceed 2*CIR1 + 5*CIR2
+	for chains which is roughly: 6mbps
+
+	Marking: the should be a variation of marked packets:
+	AF41(TOS=0x88) AF42(0x90) AF43(0x98) and BE (0x0)
+
+TODO:
+-More tests required to see the interaction of several sources (other
+than subnet 10.2.0.0/24).
+-More tests needed to capture stats on how many times the CIR was exceeded
+but the data was not remarked etc.
+
+Edge32-ca-u32
+--------------
+
+Generate data using modified tcpblast from 10.2.0.2 (behind eth2) to the 
+discard port of 10.1.0.2 (behind eth1)
+
+1) generate with src tos = 0x88
+	Metering: Allocated throughput should not exceed 2*CIR1 + 2*CIR2
+	+PIR1 -- approximately 4mbps
+	Marking: Should vary between 0x88,0x90,0x98 and 0x0
+
+2) generate with src tos = 0x90
+	Metering: Allocated throughput should not exceed CIR1 + 2*CIR2
+	+ 2* PIR2 approximately 3mbps
+	Marking: Should vary between 0x90,0x98 and 0x0
+
+3) generate with src tos = 0x98
+	Metering: Allocated throughput should not exceed PIR1+ CIR1 + CIR2
+	approximately 2.5mbps
+	Marking: Should vary between 0x98 and 0x0
+
+4) generate with src tos any other than the above
+	Metering: Allocated throughput should not exceed CIR1 
+	approximately 1mbps
+	Marking: Should be consistent at 0x0
+
+TODO: Testing on how each color shares when all 4 types of packets
+are going through the edge device
diff --git a/examples/gaiconf b/examples/gaiconf
new file mode 100644
index 0000000..d75292b
--- /dev/null
+++ b/examples/gaiconf
@@ -0,0 +1,134 @@
+#!/bin/sh
+
+#
+# Setup address label from /etc/gai.conf
+#
+# Written by YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>, 2010.
+#
+
+IP=ip
+DEFAULT_GAICONF=/etc/gai.conf
+verbose=
+debug=
+
+function run ()
+{
+	if [ x"$verbose" != x"" ]; then
+		echo "$@"
+	fi
+	if [ x"$debug" = x"" ]; then
+		"$@"
+	fi
+}
+
+function do_load_config ()
+{
+	file=$1; shift
+	flush=1
+	cat $file | while read command prefix label; do
+		if [ x"$command" = x"#label" ]; then
+			if [ ${flush} = 1 ]; then
+				run ${IP} -6 addrlabel flush
+				flush=0
+			fi
+			run ${IP} -6 addrlabel add prefix $prefix label $label
+		fi
+	done
+}
+
+function do_list_config ()
+{
+	${IP} -6 addrlabel list | while read p pfx l lbl; do
+		echo label ${pfx} ${lbl}
+	done
+}
+
+function help ()
+{
+	echo "Usage: $0 [-v] {--list | --config [ ${DEFAULT_GAICONF} ] | --default}"
+	exit 1
+}
+
+TEMP=`getopt -o c::dlv -l config::,default,list,verbose -n gaiconf -- "$@"`
+
+if [ $? != 0 ]; then
+	echo "Terminating..." >&2
+	exit 1
+fi
+
+TEMPFILE=`mktemp`
+
+eval set -- "$TEMP"
+
+while true ; do
+	case "$1" in
+		-c|--config)
+			if [ x"$cmd" != x"" ]; then
+				help
+			fi
+			case "$2" in
+			"")	gai_conf="${DEFAULT_GAICONF}"
+				shift 2
+				;;
+			*)	gai_conf="$2"
+				shift 2
+			esac
+			cmd=config
+			;;
+		-d|--default)
+			if [ x"$cmd" != x"" ]; then
+				help
+			fi
+			gai_conf=${TEMPFILE}
+			cmd=config
+			;;
+		-l|--list)
+			if [ x"$cmd" != x"" ]; then
+				help
+			fi
+			cmd=list
+			shift
+			;;
+		-v)
+			verbose=1
+			shift
+			;;
+		--)
+			shift;
+			break
+			;;
+		*)
+			echo "Internal error!" >&2
+			exit 1
+			;;
+	esac
+done
+
+case "$cmd" in
+	config)
+		if [ x"$gai_conf" = x"${TEMPFILE}" ]; then
+			sed -e 's/^[[:space:]]*//' <<END_OF_DEFAULT >${TEMPFILE}
+				label ::1/128       0
+				label ::/0          1
+				label 2002::/16     2
+				label ::/96         3
+				label ::ffff:0:0/96 4
+				label fec0::/10     5
+				label fc00::/7      6
+				label 2001:0::/32   7
+END_OF_DEFAULT
+		fi
+		do_load_config "$gai_conf"
+		;;
+	list)
+		do_list_config
+		;;
+	*)
+		help
+		;;
+esac
+
+rm -f "${TEMPFILE}"
+
+exit 0
+
diff --git a/genl/Makefile b/genl/Makefile
index 2b7a45b..03d1f26 100644
--- a/genl/Makefile
+++ b/genl/Makefile
@@ -1,7 +1,6 @@
-# SPDX-License-Identifier: GPL-2.0
 GENLOBJ=genl.o
 
-include ../config.mk
+include ../Config
 SHARED_LIBS ?= y
 
 CFLAGS += -fno-strict-aliasing
@@ -21,7 +20,6 @@
 all: genl
 
 genl: $(GENLOBJ) $(LIBNETLINK) $(LIBUTIL) $(GENLLIB)
-	$(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
 
 install: all
 	install -m 0755 genl $(DESTDIR)$(SBINDIR)
diff --git a/genl/ctrl.c b/genl/ctrl.c
index 0fb464b..b7a8878 100644
--- a/genl/ctrl.c
+++ b/genl/ctrl.c
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -38,6 +39,79 @@
 	return -1;
 }
 
+int genl_ctrl_resolve_family(const char *family)
+{
+	struct rtnl_handle rth;
+	struct nlmsghdr *nlh;
+	struct genlmsghdr *ghdr;
+	int ret = 0;
+	struct {
+		struct nlmsghdr         n;
+		char                    buf[4096];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+
+	nlh = &req.n;
+	nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	nlh->nlmsg_type = GENL_ID_CTRL;
+
+	ghdr = NLMSG_DATA(&req.n);
+	ghdr->cmd = CTRL_CMD_GETFAMILY;
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
+		fprintf(stderr, "Cannot open generic netlink socket\n");
+		exit(1);
+	}
+
+	addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME, family, strlen(family) + 1);
+
+	if (rtnl_talk(&rth, nlh, nlh, sizeof(req)) < 0) {
+		fprintf(stderr, "Error talking to the kernel\n");
+		goto errout;
+	}
+
+	{
+		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);
+			goto errout;
+		}
+
+		if (ghdr->cmd != CTRL_CMD_NEWFAMILY) {
+			fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
+			goto errout;
+		}
+
+		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");
+			goto errout;
+		}
+
+		ret = rta_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
+	}
+
+errout:
+	rtnl_close(&rth);
+	return ret;
+}
+
 static void print_ctrl_cmd_flags(FILE *fp, __u32 fl)
 {
 	fprintf(fp, "\n\t\tCapabilities (0x%x):\n ", fl);
@@ -58,7 +132,7 @@
 
 	fprintf(fp, "\n");
 }
-
+	
 static int print_ctrl_cmds(FILE *fp, struct rtattr *arg, __u32 ctrl_ver)
 {
 	struct rtattr *tb[CTRL_ATTR_OP_MAX + 1];
@@ -103,7 +177,8 @@
 /*
  * The controller sends one nlmsg per family
 */
-static int print_ctrl(struct rtnl_ctrl_data *ctrl,
+static int print_ctrl(const struct sockaddr_nl *who,
+		      struct rtnl_ctrl_data *ctrl,
 		      struct nlmsghdr *n, void *arg)
 {
 	struct rtattr *tb[CTRL_ATTR_MAX + 1];
@@ -207,28 +282,33 @@
 	return 0;
 }
 
-static int print_ctrl2(struct nlmsghdr *n, void *arg)
+static int print_ctrl2(const struct sockaddr_nl *who,
+		      struct nlmsghdr *n, void *arg)
 {
-	return print_ctrl(NULL, n, arg);
+	return print_ctrl(who, NULL, n, arg);
 }
 
 static int ctrl_list(int cmd, int argc, char **argv)
 {
 	struct rtnl_handle rth;
+	struct nlmsghdr *nlh;
+	struct genlmsghdr *ghdr;
 	int ret = -1;
 	char d[GENL_NAMSIZ];
 	struct {
 		struct nlmsghdr         n;
-		struct genlmsghdr	g;
 		char                    buf[4096];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN),
-		.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
-		.n.nlmsg_type = GENL_ID_CTRL,
-		.g.cmd = CTRL_CMD_GETFAMILY,
-	};
-	struct nlmsghdr *nlh = &req.n;
-	struct nlmsghdr *answer = NULL;
+	} req;
+
+	memset(&req, 0, sizeof(req));
+
+	nlh = &req.n;
+	nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	nlh->nlmsg_type = GENL_ID_CTRL;
+
+	ghdr = NLMSG_DATA(&req.n);
+	ghdr->cmd = CTRL_CMD_GETFAMILY;
 
 	if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
 		fprintf(stderr, "Cannot open generic netlink socket\n");
@@ -243,7 +323,7 @@
 
 		if (matches(*argv, "name") == 0) {
 			NEXT_ARG();
-			strlcpy(d, *argv, sizeof(d));
+			strncpy(d, *argv, sizeof (d) - 1);
 			addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME,
 				  d, strlen(d) + 1);
 		} else if (matches(*argv, "id") == 0) {
@@ -261,12 +341,12 @@
 			goto ctrl_done;
 		}
 
-		if (rtnl_talk(&rth, nlh, &answer) < 0) {
+		if (rtnl_talk(&rth, nlh, nlh, sizeof(req)) < 0) {
 			fprintf(stderr, "Error talking to the kernel\n");
 			goto ctrl_done;
 		}
 
-		if (print_ctrl2(answer, (void *) stdout) < 0) {
+		if (print_ctrl2(NULL, nlh, (void *) stdout) < 0) {
 			fprintf(stderr, "Dump terminated\n");
 			goto ctrl_done;
 		}
@@ -288,7 +368,6 @@
 
 	ret = 0;
 ctrl_done:
-	free(answer);
 	rtnl_close(&rth);
 	return ret;
 }
diff --git a/genl/genl.c b/genl/genl.c
index aba3c13..e33fafd 100644
--- a/genl/genl.c
+++ b/genl/genl.c
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <dlfcn.h>
 #include <sys/socket.h>
@@ -26,15 +27,17 @@
 #include "utils.h"
 #include "genl_utils.h"
 
-int show_stats;
-int show_details;
-int show_raw;
+int show_stats = 0;
+int show_details = 0;
+int show_raw = 0;
+int resolve_hosts = 0;
 
 static void *BODY;
-static struct genl_util *genl_list;
+static struct genl_util * genl_list;
 
 
-static int print_nofopt(struct nlmsghdr *n, void *arg)
+static int print_nofopt(const struct sockaddr_nl *who, struct nlmsghdr *n,
+			void *arg)
 {
 	fprintf((FILE *) arg, "unknown genl type ..\n");
 	return 0;
@@ -43,9 +46,8 @@
 static int parse_nofopt(struct genl_util *f, int argc, char **argv)
 {
 	if (argc) {
-		fprintf(stderr,
-			"Unknown genl \"%s\", hence option \"%s\" is unparsable\n",
-			f->name, *argv);
+		fprintf(stderr, "Unknown genl \"%s\", hence option \"%s\" "
+			"is unparsable\n", f->name, *argv);
 		return -1;
 	}
 
@@ -84,8 +86,9 @@
 	return f;
 
 noexist:
-	f = calloc(1, sizeof(*f));
+	f = malloc(sizeof(*f));
 	if (f) {
+		memset(f, 0, sizeof(*f));
 		strncpy(f->name, str, 15);
 		f->parse_genlopt = parse_nofopt;
 		f->print_genlopt = print_nofopt;
@@ -98,10 +101,9 @@
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: genl [ OPTIONS ] OBJECT [help] }\n"
-		"where  OBJECT := { ctrl etc }\n"
-		"       OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -V[ersion] | -h[elp] }\n");
+	fprintf(stderr, "Usage: genl [ OPTIONS ] OBJECT | help }\n"
+	                "where  OBJECT := { ctrl etc }\n"
+	                "       OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] }\n");
 	exit(-1);
 }
 
@@ -123,21 +125,19 @@
 		} else if (matches(argv[1], "-help") == 0) {
 			usage();
 		} else {
-			fprintf(stderr,
-				"Option \"%s\" is unknown, try \"genl -help\".\n",
-				argv[1]);
+			fprintf(stderr, "Option \"%s\" is unknown, try "
+				"\"genl -help\".\n", argv[1]);
 			exit(-1);
 		}
 		argc--;	argv++;
 	}
 
 	if (argc > 1) {
-		struct genl_util *a;
 		int ret;
-
+		struct genl_util *a = NULL;
 		a = get_genl_kind(argv[1]);
 		if (!a) {
-			fprintf(stderr, "bad genl %s\n", argv[1]);
+			fprintf(stderr,"bad genl %s\n", argv[1]);
 			exit(-1);
 		}
 
diff --git a/genl/genl_utils.h b/genl/genl_utils.h
index a8d433a..85b5183 100644
--- a/genl/genl_utils.h
+++ b/genl/genl_utils.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _TC_UTIL_H_
 #define _TC_UTIL_H_ 1
 
@@ -10,7 +9,9 @@
 	struct  genl_util *next;
 	char	name[16];
 	int	(*parse_genlopt)(struct genl_util *fu, int argc, char **argv);
-	int	(*print_genlopt)(struct nlmsghdr *n, void *arg);
+	int	(*print_genlopt)(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
 };
 
+extern int genl_ctrl_resolve_family(const char *family);
+
 #endif
diff --git a/genl/static-syms.c b/genl/static-syms.c
index 47c4092..0bc8074 100644
--- a/genl/static-syms.c
+++ b/genl/static-syms.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * This file creates a dummy version of dynamic loading
  * for environments where dynamic linking
diff --git a/include/SNAPSHOT.h b/include/SNAPSHOT.h
index b98ad50..58d3632 100644
--- a/include/SNAPSHOT.h
+++ b/include/SNAPSHOT.h
@@ -1 +1 @@
-static const char SNAPSHOT[] = "191125";
+static const char SNAPSHOT[] = "160111";
diff --git a/include/bpf_api.h b/include/bpf_api.h
index 89d3488..0666a31 100644
--- a/include/bpf_api.h
+++ b/include/bpf_api.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef __BPF_API__
 #define __BPF_API__
 
@@ -57,10 +56,6 @@
 # define ntohl(X)		__constant_ntohl((X))
 #endif
 
-#ifndef __inline__
-# define __inline__		__attribute__((always_inline))
-#endif
-
 /** Section helper macros. */
 
 #ifndef __section
@@ -73,11 +68,6 @@
 	__section(__stringify(ID) "/" __stringify(KEY))
 #endif
 
-#ifndef __section_xdp_entry
-# define __section_xdp_entry						\
-	__section(ELF_SECTION_PROG)
-#endif
-
 #ifndef __section_cls_entry
 # define __section_cls_entry						\
 	__section(ELF_SECTION_CLASSIFIER)
@@ -88,11 +78,6 @@
 	__section(ELF_SECTION_ACTION)
 #endif
 
-#ifndef __section_lwt_entry
-# define __section_lwt_entry						\
-	__section(ELF_SECTION_PROG)
-#endif
-
 #ifndef __section_license
 # define __section_license						\
 	__section(ELF_SECTION_LICENSE)
@@ -110,22 +95,62 @@
 	char ____license[] __section_license = NAME
 #endif
 
+#ifndef __BPF_MAP
+# define __BPF_MAP(NAME, TYPE, ID, SIZE_KEY, SIZE_VALUE, PIN, MAX_ELEM)	\
+	struct bpf_elf_map __section_maps NAME = {			\
+		.type		= (TYPE),				\
+		.id		= (ID),					\
+		.size_key	= (SIZE_KEY),				\
+		.size_value	= (SIZE_VALUE),				\
+		.pinning	= (PIN),				\
+		.max_elem	= (MAX_ELEM),				\
+	}
+#endif
+
+#ifndef BPF_HASH
+# define BPF_HASH(NAME, ID, SIZE_KEY, SIZE_VALUE, PIN, MAX_ELEM)	\
+	__BPF_MAP(NAME, BPF_MAP_TYPE_HASH, ID, SIZE_KEY, SIZE_VALUE,	\
+		  PIN, MAX_ELEM)
+#endif
+
+#ifndef BPF_ARRAY
+# define BPF_ARRAY(NAME, ID, SIZE_VALUE, PIN, MAX_ELEM)			\
+	__BPF_MAP(NAME, BPF_MAP_TYPE_ARRAY, ID, sizeof(uint32_t), 	\
+		  SIZE_VALUE, PIN, MAX_ELEM)
+#endif
+
+#ifndef BPF_ARRAY2
+# define BPF_ARRAY2(NAME, ID, PIN, MAX_ELEM)				\
+	BPF_ARRAY(NAME, ID, sizeof(uint16_t), PIN, MAX_ELEM)
+#endif
+
+#ifndef BPF_ARRAY4
+# define BPF_ARRAY4(NAME, ID, PIN, MAX_ELEM)				\
+	BPF_ARRAY(NAME, ID, sizeof(uint32_t), PIN, MAX_ELEM)
+#endif
+
+#ifndef BPF_ARRAY8
+# define BPF_ARRAY8(NAME, ID, PIN, MAX_ELEM)				\
+	BPF_ARRAY(NAME, ID, sizeof(uint64_t), PIN, MAX_ELEM)
+#endif
+
+#ifndef BPF_PROG_ARRAY
+# define BPF_PROG_ARRAY(NAME, ID, PIN, MAX_ELEM)			\
+	__BPF_MAP(NAME, BPF_MAP_TYPE_PROG_ARRAY, ID, sizeof(uint32_t),	\
+		  sizeof(uint32_t), PIN, MAX_ELEM)
+#endif
+
 /** Classifier helper */
 
 #ifndef BPF_H_DEFAULT
 # define BPF_H_DEFAULT	-1
 #endif
 
-/** BPF helper functions for tc. Individual flags are in linux/bpf.h */
-
-#ifndef __BPF_FUNC
-# define __BPF_FUNC(NAME, ...)						\
-	(* NAME)(__VA_ARGS__) __maybe_unused
-#endif
+/** BPF helper functions for tc. */
 
 #ifndef BPF_FUNC
 # define BPF_FUNC(NAME, ...)						\
-	__BPF_FUNC(NAME, __VA_ARGS__) = (void *) BPF_FUNC_##NAME
+	(* NAME)(__VA_ARGS__) __maybe_unused = (void *) BPF_FUNC_##NAME
 #endif
 
 /* Map access/manipulation */
@@ -138,22 +163,8 @@
 static uint64_t BPF_FUNC(ktime_get_ns);
 
 /* Debugging */
-
-/* FIXME: __attribute__ ((format(printf, 1, 3))) not possible unless
- * llvm bug https://llvm.org/bugs/show_bug.cgi?id=26243 gets resolved.
- * It would require ____fmt to be made const, which generates a reloc
- * entry (non-map).
- */
 static void BPF_FUNC(trace_printk, const char *fmt, int fmt_size, ...);
 
-#ifndef printt
-# define printt(fmt, ...)						\
-	({								\
-		char ____fmt[] = fmt;					\
-		trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__);	\
-	})
-#endif
-
 /* Random numbers */
 static uint32_t BPF_FUNC(get_prandom_u32);
 
@@ -163,15 +174,10 @@
 
 /* System helpers */
 static uint32_t BPF_FUNC(get_smp_processor_id);
-static uint32_t BPF_FUNC(get_numa_node_id);
 
 /* Packet misc meta data */
 static uint32_t BPF_FUNC(get_cgroup_classid, struct __sk_buff *skb);
-static int BPF_FUNC(skb_under_cgroup, void *map, uint32_t index);
-
 static uint32_t BPF_FUNC(get_route_realm, struct __sk_buff *skb);
-static uint32_t BPF_FUNC(get_hash_recalc, struct __sk_buff *skb);
-static uint32_t BPF_FUNC(set_hash_invalid, struct __sk_buff *skb);
 
 /* Packet redirection */
 static int BPF_FUNC(redirect, int ifindex, uint32_t flags);
@@ -179,31 +185,16 @@
 		    uint32_t flags);
 
 /* Packet manipulation */
-static int BPF_FUNC(skb_load_bytes, struct __sk_buff *skb, uint32_t off,
-		    void *to, uint32_t len);
-static int BPF_FUNC(skb_store_bytes, struct __sk_buff *skb, uint32_t off,
-		    const void *from, uint32_t len, uint32_t flags);
+#define BPF_PSEUDO_HDR			0x10
+#define BPF_HAS_PSEUDO_HDR(flags)	((flags) & BPF_PSEUDO_HDR)
+#define BPF_HDR_FIELD_SIZE(flags)	((flags) & 0x0f)
 
+static int BPF_FUNC(skb_store_bytes, struct __sk_buff *skb, uint32_t off,
+		    void *from, uint32_t len, uint32_t flags);
 static int BPF_FUNC(l3_csum_replace, struct __sk_buff *skb, uint32_t off,
 		    uint32_t from, uint32_t to, uint32_t flags);
 static int BPF_FUNC(l4_csum_replace, struct __sk_buff *skb, uint32_t off,
 		    uint32_t from, uint32_t to, uint32_t flags);
-static int BPF_FUNC(csum_diff, const void *from, uint32_t from_size,
-		    const void *to, uint32_t to_size, uint32_t seed);
-static int BPF_FUNC(csum_update, struct __sk_buff *skb, uint32_t wsum);
-
-static int BPF_FUNC(skb_change_type, struct __sk_buff *skb, uint32_t type);
-static int BPF_FUNC(skb_change_proto, struct __sk_buff *skb, uint32_t proto,
-		    uint32_t flags);
-static int BPF_FUNC(skb_change_tail, struct __sk_buff *skb, uint32_t nlen,
-		    uint32_t flags);
-
-static int BPF_FUNC(skb_pull_data, struct __sk_buff *skb, uint32_t len);
-
-/* Event notification */
-static int __BPF_FUNC(skb_event_output, struct __sk_buff *skb, void *map,
-		      uint64_t index, const void *data, uint32_t size) =
-		      (void *) BPF_FUNC_perf_event_output;
 
 /* Packet vlan encap/decap */
 static int BPF_FUNC(skb_vlan_push, struct __sk_buff *skb, uint16_t proto,
@@ -214,42 +205,14 @@
 static int BPF_FUNC(skb_get_tunnel_key, struct __sk_buff *skb,
 		    struct bpf_tunnel_key *to, uint32_t size, uint32_t flags);
 static int BPF_FUNC(skb_set_tunnel_key, struct __sk_buff *skb,
-		    const struct bpf_tunnel_key *from, uint32_t size,
-		    uint32_t flags);
+		    struct bpf_tunnel_key *from, uint32_t size, uint32_t flags);
 
-static int BPF_FUNC(skb_get_tunnel_opt, struct __sk_buff *skb,
-		    void *to, uint32_t size);
-static int BPF_FUNC(skb_set_tunnel_opt, struct __sk_buff *skb,
-		    const void *from, uint32_t size);
-
-/** LLVM built-ins, mem*() routines work for constant size */
+/** LLVM built-ins */
 
 #ifndef lock_xadd
 # define lock_xadd(ptr, val)	((void) __sync_fetch_and_add(ptr, val))
 #endif
 
-#ifndef memset
-# define memset(s, c, n)	__builtin_memset((s), (c), (n))
-#endif
-
-#ifndef memcpy
-# define memcpy(d, s, n)	__builtin_memcpy((d), (s), (n))
-#endif
-
-#ifndef memmove
-# define memmove(d, s, n)	__builtin_memmove((d), (s), (n))
-#endif
-
-/* FIXME: __builtin_memcmp() is not yet fully useable unless llvm bug
- * https://llvm.org/bugs/show_bug.cgi?id=26218 gets resolved. Also
- * this one would generate a reloc entry (non-map), otherwise.
- */
-#if 0
-#ifndef memcmp
-# define memcmp(a, b, n)	__builtin_memcmp((a), (b), (n))
-#endif
-#endif
-
 unsigned long long load_byte(void *skb, unsigned long long off)
 	asm ("llvm.bpf.load.byte");
 
diff --git a/include/bpf_elf.h b/include/bpf_elf.h
index 84e8ae0..31a8974 100644
--- a/include/bpf_elf.h
+++ b/include/bpf_elf.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef __BPF_ELF__
 #define __BPF_ELF__
 
@@ -16,7 +15,6 @@
 /* ELF section names, etc */
 #define ELF_SECTION_LICENSE	"license"
 #define ELF_SECTION_MAPS	"maps"
-#define ELF_SECTION_PROG	"prog"
 #define ELF_SECTION_CLASSIFIER	"classifier"
 #define ELF_SECTION_ACTION	"action"
 
@@ -34,20 +32,8 @@
 	__u32 size_key;
 	__u32 size_value;
 	__u32 max_elem;
-	__u32 flags;
 	__u32 id;
 	__u32 pinning;
-	__u32 inner_id;
-	__u32 inner_idx;
 };
 
-#define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val)		\
-	struct ____btf_map_##name {				\
-		type_key key;					\
-		type_val value;					\
-	};							\
-	struct ____btf_map_##name				\
-	    __attribute__ ((section(".maps." #name), used))	\
-	    ____btf_map_##name = { }
-
 #endif /* __BPF_ELF__ */
diff --git a/include/bpf_scm.h b/include/bpf_scm.h
index 669f053..35117d1 100644
--- a/include/bpf_scm.h
+++ b/include/bpf_scm.h
@@ -1,10 +1,8 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef __BPF_SCM__
 #define __BPF_SCM__
 
 #include <sys/types.h>
 #include <sys/socket.h>
-#include <sys/un.h>
 
 #include "utils.h"
 #include "bpf_elf.h"
diff --git a/include/bpf_util.h b/include/bpf_util.h
deleted file mode 100644
index 63db07c..0000000
--- a/include/bpf_util.h
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * bpf_util.h	BPF common code
- *
- *		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:	Daniel Borkmann <daniel@iogearbox.net>
- *		Jiri Pirko <jiri@resnulli.us>
- */
-
-#ifndef __BPF_UTIL__
-#define __BPF_UTIL__
-
-#include <linux/bpf.h>
-#include <linux/btf.h>
-#include <linux/filter.h>
-#include <linux/magic.h>
-#include <linux/elf-em.h>
-#include <linux/if_alg.h>
-
-#include "utils.h"
-#include "bpf_scm.h"
-
-#define BPF_ENV_UDS	"TC_BPF_UDS"
-#define BPF_ENV_MNT	"TC_BPF_MNT"
-
-#ifndef BPF_MAX_LOG
-# define BPF_MAX_LOG	4096
-#endif
-
-#define BPF_DIR_GLOBALS	"globals"
-
-#ifndef BPF_FS_MAGIC
-# define BPF_FS_MAGIC	0xcafe4a11
-#endif
-
-#define BPF_DIR_MNT	"/sys/fs/bpf"
-
-#ifndef TRACEFS_MAGIC
-# define TRACEFS_MAGIC	0x74726163
-#endif
-
-#define TRACE_DIR_MNT	"/sys/kernel/tracing"
-
-#ifndef AF_ALG
-# define AF_ALG		38
-#endif
-
-#ifndef EM_BPF
-# define EM_BPF		247
-#endif
-
-struct bpf_cfg_ops {
-	void (*cbpf_cb)(void *nl, const struct sock_filter *ops, int ops_len);
-	void (*ebpf_cb)(void *nl, int fd, const char *annotation);
-};
-
-enum bpf_mode {
-	CBPF_BYTECODE,
-	CBPF_FILE,
-	EBPF_OBJECT,
-	EBPF_PINNED,
-	BPF_MODE_MAX,
-};
-
-struct bpf_cfg_in {
-	const char *object;
-	const char *section;
-	const char *uds;
-	enum bpf_prog_type type;
-	enum bpf_mode mode;
-	__u32 ifindex;
-	bool verbose;
-	int argc;
-	char **argv;
-	struct sock_filter opcodes[BPF_MAXINSNS];
-	union {
-		int n_opcodes;
-		int prog_fd;
-	};
-};
-
-/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
-
-#define BPF_ALU64_REG(OP, DST, SRC)				\
-	((struct bpf_insn) {					\
-		.code  = BPF_ALU64 | BPF_OP(OP) | BPF_X,	\
-		.dst_reg = DST,					\
-		.src_reg = SRC,					\
-		.off   = 0,					\
-		.imm   = 0 })
-
-#define BPF_ALU32_REG(OP, DST, SRC)				\
-	((struct bpf_insn) {					\
-		.code  = BPF_ALU | BPF_OP(OP) | BPF_X,		\
-		.dst_reg = DST,					\
-		.src_reg = SRC,					\
-		.off   = 0,					\
-		.imm   = 0 })
-
-/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */
-
-#define BPF_ALU64_IMM(OP, DST, IMM)				\
-	((struct bpf_insn) {					\
-		.code  = BPF_ALU64 | BPF_OP(OP) | BPF_K,	\
-		.dst_reg = DST,					\
-		.src_reg = 0,					\
-		.off   = 0,					\
-		.imm   = IMM })
-
-#define BPF_ALU32_IMM(OP, DST, IMM)				\
-	((struct bpf_insn) {					\
-		.code  = BPF_ALU | BPF_OP(OP) | BPF_K,		\
-		.dst_reg = DST,					\
-		.src_reg = 0,					\
-		.off   = 0,					\
-		.imm   = IMM })
-
-/* Short form of mov, dst_reg = src_reg */
-
-#define BPF_MOV64_REG(DST, SRC)					\
-	((struct bpf_insn) {					\
-		.code  = BPF_ALU64 | BPF_MOV | BPF_X,		\
-		.dst_reg = DST,					\
-		.src_reg = SRC,					\
-		.off   = 0,					\
-		.imm   = 0 })
-
-#define BPF_MOV32_REG(DST, SRC)					\
-	((struct bpf_insn) {					\
-		.code  = BPF_ALU | BPF_MOV | BPF_X,		\
-		.dst_reg = DST,					\
-		.src_reg = SRC,					\
-		.off   = 0,					\
-		.imm   = 0 })
-
-/* Short form of mov, dst_reg = imm32 */
-
-#define BPF_MOV64_IMM(DST, IMM)					\
-	((struct bpf_insn) {					\
-		.code  = BPF_ALU64 | BPF_MOV | BPF_K,		\
-		.dst_reg = DST,					\
-		.src_reg = 0,					\
-		.off   = 0,					\
-		.imm   = IMM })
-
-#define BPF_MOV32_IMM(DST, IMM)					\
-	((struct bpf_insn) {					\
-		.code  = BPF_ALU | BPF_MOV | BPF_K,		\
-		.dst_reg = DST,					\
-		.src_reg = 0,					\
-		.off   = 0,					\
-		.imm   = IMM })
-
-/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */
-#define BPF_LD_IMM64(DST, IMM)					\
-	BPF_LD_IMM64_RAW(DST, 0, IMM)
-
-#define BPF_LD_IMM64_RAW(DST, SRC, IMM)				\
-	((struct bpf_insn) {					\
-		.code  = BPF_LD | BPF_DW | BPF_IMM,		\
-		.dst_reg = DST,					\
-		.src_reg = SRC,					\
-		.off   = 0,					\
-		.imm   = (__u32) (IMM) }),			\
-	((struct bpf_insn) {					\
-		.code  = 0, /* zero is reserved opcode */	\
-		.dst_reg = 0,					\
-		.src_reg = 0,					\
-		.off   = 0,					\
-		.imm   = ((__u64) (IMM)) >> 32 })
-
-#ifndef BPF_PSEUDO_MAP_FD
-# define BPF_PSEUDO_MAP_FD	1
-#endif
-
-/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */
-#define BPF_LD_MAP_FD(DST, MAP_FD)				\
-	BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
-
-
-/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */
-
-#define BPF_LD_ABS(SIZE, IMM)					\
-	((struct bpf_insn) {					\
-		.code  = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS,	\
-		.dst_reg = 0,					\
-		.src_reg = 0,					\
-		.off   = 0,					\
-		.imm   = IMM })
-
-/* Memory load, dst_reg = *(uint *) (src_reg + off16) */
-
-#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)			\
-	((struct bpf_insn) {					\
-		.code  = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM,	\
-		.dst_reg = DST,					\
-		.src_reg = SRC,					\
-		.off   = OFF,					\
-		.imm   = 0 })
-
-/* Memory store, *(uint *) (dst_reg + off16) = src_reg */
-
-#define BPF_STX_MEM(SIZE, DST, SRC, OFF)			\
-	((struct bpf_insn) {					\
-		.code  = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM,	\
-		.dst_reg = DST,					\
-		.src_reg = SRC,					\
-		.off   = OFF,					\
-		.imm   = 0 })
-
-/* Memory store, *(uint *) (dst_reg + off16) = imm32 */
-
-#define BPF_ST_MEM(SIZE, DST, OFF, IMM)				\
-	((struct bpf_insn) {					\
-		.code  = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM,	\
-		.dst_reg = DST,					\
-		.src_reg = 0,					\
-		.off   = OFF,					\
-		.imm   = IMM })
-
-/* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */
-
-#define BPF_JMP_REG(OP, DST, SRC, OFF)				\
-	((struct bpf_insn) {					\
-		.code  = BPF_JMP | BPF_OP(OP) | BPF_X,		\
-		.dst_reg = DST,					\
-		.src_reg = SRC,					\
-		.off   = OFF,					\
-		.imm   = 0 })
-
-/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */
-
-#define BPF_JMP_IMM(OP, DST, IMM, OFF)				\
-	((struct bpf_insn) {					\
-		.code  = BPF_JMP | BPF_OP(OP) | BPF_K,		\
-		.dst_reg = DST,					\
-		.src_reg = 0,					\
-		.off   = OFF,					\
-		.imm   = IMM })
-
-/* Raw code statement block */
-
-#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM)			\
-	((struct bpf_insn) {					\
-		.code  = CODE,					\
-		.dst_reg = DST,					\
-		.src_reg = SRC,					\
-		.off   = OFF,					\
-		.imm   = IMM })
-
-/* Program exit */
-
-#define BPF_EXIT_INSN()						\
-	((struct bpf_insn) {					\
-		.code  = BPF_JMP | BPF_EXIT,			\
-		.dst_reg = 0,					\
-		.src_reg = 0,					\
-		.off   = 0,					\
-		.imm   = 0 })
-
-int bpf_parse_common(struct bpf_cfg_in *cfg, const struct bpf_cfg_ops *ops);
-int bpf_load_common(struct bpf_cfg_in *cfg, const struct bpf_cfg_ops *ops,
-		    void *nl);
-int bpf_parse_and_load_common(struct bpf_cfg_in *cfg,
-			      const struct bpf_cfg_ops *ops, void *nl);
-
-const char *bpf_prog_to_default_section(enum bpf_prog_type type);
-
-int bpf_graft_map(const char *map_path, uint32_t *key, int argc, char **argv);
-int bpf_trace_pipe(void);
-
-void bpf_print_ops(struct rtattr *bpf_ops, __u16 len);
-
-int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns,
-		  size_t size_insns, const char *license, char *log,
-		  size_t size_log);
-
-int bpf_prog_attach_fd(int prog_fd, int target_fd, enum bpf_attach_type type);
-int bpf_prog_detach_fd(int target_fd, enum bpf_attach_type type);
-
-int bpf_dump_prog_info(FILE *f, uint32_t id);
-
-#ifdef HAVE_ELF
-int bpf_send_map_fds(const char *path, const char *obj);
-int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
-		     unsigned int entries);
-#else
-static inline int bpf_send_map_fds(const char *path, const char *obj)
-{
-	return 0;
-}
-
-static inline int bpf_recv_map_fds(const char *path, int *fds,
-				   struct bpf_map_aux *aux,
-				   unsigned int entries)
-{
-	return -1;
-}
-#endif /* HAVE_ELF */
-#endif /* __BPF_UTIL__ */
diff --git a/include/color.h b/include/color.h
index 17ec56f..b85003a 100644
--- a/include/color.h
+++ b/include/color.h
@@ -1,29 +1,16 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef __COLOR_H__
 #define __COLOR_H__ 1
 
-#include <stdbool.h>
-
 enum color_attr {
 	COLOR_IFNAME,
 	COLOR_MAC,
 	COLOR_INET,
 	COLOR_INET6,
 	COLOR_OPERSTATE_UP,
-	COLOR_OPERSTATE_DOWN,
-	COLOR_NONE
+	COLOR_OPERSTATE_DOWN
 };
 
-enum color_opt {
-	COLOR_OPT_NEVER = 0,
-	COLOR_OPT_AUTO = 1,
-	COLOR_OPT_ALWAYS = 2
-};
-
-bool check_enable_color(int color, int json);
-bool matches_color(const char *arg, int *val);
+void enable_color(void);
 int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...);
-enum color_attr ifa_family_color(__u8 ifa_family);
-enum color_attr oper_state_color(__u8 state);
 
 #endif
diff --git a/include/dlfcn.h b/include/dlfcn.h
index 1d8890a..f15bc2c 100644
--- a/include/dlfcn.h
+++ b/include/dlfcn.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Stub dlfcn implementation for systems that lack shared library support
  * but obviously can still reference compiled-in symbols.
diff --git a/include/hlist.h b/include/hlist.h
new file mode 100644
index 0000000..4e8de9e
--- /dev/null
+++ b/include/hlist.h
@@ -0,0 +1,56 @@
+#ifndef __HLIST_H__
+#define __HLIST_H__ 1
+/* Hash list stuff from kernel */
+
+#include <stddef.h>
+
+#define container_of(ptr, type, member) ({			\
+	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
+	(type *)( (char *)__mptr - offsetof(type,member) );})
+
+struct hlist_head {
+	struct hlist_node *first;
+};
+
+struct hlist_node {
+	struct hlist_node *next, **pprev;
+};
+
+static inline void hlist_del(struct hlist_node *n)
+{
+	struct hlist_node *next = n->next;
+	struct hlist_node **pprev = n->pprev;
+	*pprev = next;
+	if (next)
+		next->pprev = pprev;
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+	struct hlist_node *first = h->first;
+	n->next = first;
+	if (first)
+		first->pprev = &n->next;
+	h->first = n;
+	n->pprev = &h->first;
+}
+
+#define hlist_for_each(pos, head) \
+	for (pos = (head)->first; pos ; pos = pos->next)
+
+
+#define hlist_for_each_safe(pos, n, head) \
+	for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+	     pos = n)
+
+#define hlist_entry_safe(ptr, type, member) \
+	({ typeof(ptr) ____ptr = (ptr); \
+	   ____ptr ? hlist_entry(____ptr, type, member) : NULL; \
+	})
+
+#define hlist_for_each_entry(pos, head, member)				\
+	for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\
+	     pos;							\
+	     pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+
+#endif /* __HLIST_H__ */
diff --git a/include/ip6tables.h b/include/ip6tables.h
index bfb2868..5f1c5b6 100644
--- a/include/ip6tables.h
+++ b/include/ip6tables.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _IP6TABLES_USER_H
 #define _IP6TABLES_USER_H
 
diff --git a/include/iptables.h b/include/iptables.h
index 78bc378..78c10ab 100644
--- a/include/iptables.h
+++ b/include/iptables.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _IPTABLES_USER_H
 #define _IPTABLES_USER_H
 
diff --git a/include/iptables/internal.h b/include/iptables/internal.h
index 1fd1372..62a8ecb 100644
--- a/include/iptables/internal.h
+++ b/include/iptables/internal.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef IPTABLES_INTERNAL_H
 #define IPTABLES_INTERNAL_H 1
 
diff --git a/include/iptables_common.h b/include/iptables_common.h
new file mode 100644
index 0000000..9099667
--- /dev/null
+++ b/include/iptables_common.h
@@ -0,0 +1,51 @@
+#ifndef _IPTABLES_COMMON_H
+#define _IPTABLES_COMMON_H
+/* Shared definitions between ipv4 and ipv6. */
+
+enum exittype {
+	OTHER_PROBLEM = 1,
+	PARAMETER_PROBLEM,
+	VERSION_PROBLEM,
+	RESOURCE_PROBLEM
+};
+
+/* this is a special 64bit data type that is 8-byte aligned */
+#define aligned_u64 unsigned long long __attribute__((aligned(8)))
+
+extern void exit_printhelp(void) __attribute__((noreturn));
+extern void exit_tryhelp(int) __attribute__((noreturn));
+int check_inverse(const char option[], int *invert, int *optind, int argc);
+extern int string_to_number(const char *, 
+			    unsigned int, 
+			    unsigned int,
+			    unsigned int *);
+extern int string_to_number_l(const char *, 
+			    unsigned long int, 
+			    unsigned long int,
+			    unsigned long *);
+extern int string_to_number_ll(const char *, 
+			    unsigned long long int, 
+			    unsigned long long int,
+			    unsigned long long *);
+extern int iptables_insmod(const char *modname, const char *modprobe);
+extern int load_iptables_ko(const char *modprobe);
+void exit_error(enum exittype, char *, ...)__attribute__((noreturn,
+							  format(printf,2,3)));
+extern const char *program_name, *program_version;
+extern char *lib_dir;
+
+#define _init __attribute__((constructor)) my_init
+#ifdef NO_SHARED_LIBS
+# ifdef _INIT
+#  undef _init
+#  define _init _INIT
+# endif
+  extern void init_extensions(void);
+#endif
+
+#define __be32	u_int32_t
+#define __le32	u_int32_t
+#define __be16	u_int16_t
+#define __le16	u_int16_t
+
+#endif /*_IPTABLES_COMMON_H*/
diff --git a/include/json_print.h b/include/json_print.h
deleted file mode 100644
index fe92d14..0000000
--- a/include/json_print.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * json_print.h		"print regular or json output, based on json_writer".
- *
- *             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:    Julien Fortin, <julien@cumulusnetworks.com>
- */
-
-#ifndef _JSON_PRINT_H_
-#define _JSON_PRINT_H_
-
-#include "json_writer.h"
-#include "color.h"
-
-json_writer_t *get_json_writer(void);
-
-/*
- * use:
- *      - PRINT_ANY for context based output
- *      - PRINT_FP for non json specific output
- *      - PRINT_JSON for json specific output
- */
-enum output_type {
-	PRINT_FP = 1,
-	PRINT_JSON = 2,
-	PRINT_ANY = 4,
-};
-
-void new_json_obj(int json);
-void delete_json_obj(void);
-
-bool is_json_context(void);
-
-void fflush_fp(void);
-
-void open_json_object(const char *str);
-void close_json_object(void);
-void open_json_array(enum output_type type, const char *delim);
-void close_json_array(enum output_type type, const char *delim);
-
-void print_nl(void);
-
-#define _PRINT_FUNC(type_name, type)					\
-	void print_color_##type_name(enum output_type t,		\
-				     enum color_attr color,		\
-				     const char *key,			\
-				     const char *fmt,			\
-				     type value);			\
-									\
-	static inline void print_##type_name(enum output_type t,	\
-					     const char *key,		\
-					     const char *fmt,		\
-					     type value)		\
-	{								\
-		print_color_##type_name(t, COLOR_NONE, key, fmt, value);	\
-	}
-
-_PRINT_FUNC(int, int)
-_PRINT_FUNC(s64, int64_t)
-_PRINT_FUNC(bool, bool)
-_PRINT_FUNC(null, const char*)
-_PRINT_FUNC(string, const char*)
-_PRINT_FUNC(uint, unsigned int)
-_PRINT_FUNC(u64, uint64_t)
-_PRINT_FUNC(hhu, unsigned char)
-_PRINT_FUNC(hu, unsigned short)
-_PRINT_FUNC(hex, unsigned int)
-_PRINT_FUNC(0xhex, unsigned long long)
-_PRINT_FUNC(luint, unsigned long)
-_PRINT_FUNC(lluint, unsigned long long)
-_PRINT_FUNC(float, double)
-#undef _PRINT_FUNC
-
-#endif /* _JSON_PRINT_H_ */
diff --git a/include/json_writer.h b/include/json_writer.h
index b52dc2d..ab9a008 100644
--- a/include/json_writer.h
+++ b/include/json_writer.h
@@ -1,10 +1,14 @@
-/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
 /*
  * Simple streaming JSON writer
  *
  * This takes care of the annoying bits of JSON syntax like the commas
  * after elements
  *
+ * 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 <stephen@networkplumber.org>
  */
 
@@ -29,39 +33,20 @@
 void jsonw_name(json_writer_t *self, const char *name);
 
 /* Add value  */
-__attribute__((format(printf, 2, 3)))
-void jsonw_printf(json_writer_t *self, const char *fmt, ...);
 void jsonw_string(json_writer_t *self, const char *value);
 void jsonw_bool(json_writer_t *self, bool value);
 void jsonw_float(json_writer_t *self, double number);
-void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num);
-void jsonw_uint(json_writer_t *self, unsigned int number);
-void jsonw_u64(json_writer_t *self, uint64_t number);
-void jsonw_xint(json_writer_t *self, uint64_t number);
-void jsonw_hhu(json_writer_t *self, unsigned char num);
-void jsonw_hu(json_writer_t *self, unsigned short number);
-void jsonw_int(json_writer_t *self, int number);
-void jsonw_s64(json_writer_t *self, int64_t number);
+void jsonw_uint(json_writer_t *self, uint64_t number);
+void jsonw_int(json_writer_t *self, int64_t number);
 void jsonw_null(json_writer_t *self);
-void jsonw_luint(json_writer_t *self, unsigned long num);
-void jsonw_lluint(json_writer_t *self, unsigned long long num);
 
 /* Useful Combinations of name and value */
 void jsonw_string_field(json_writer_t *self, const char *prop, const char *val);
 void jsonw_bool_field(json_writer_t *self, const char *prop, bool value);
 void jsonw_float_field(json_writer_t *self, const char *prop, double num);
-void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num);
-void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num);
-void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num);
-void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num);
-void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num);
-void jsonw_int_field(json_writer_t *self, const char *prop, int num);
-void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num);
+void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num);
+void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num);
 void jsonw_null_field(json_writer_t *self, const char *prop);
-void jsonw_luint_field(json_writer_t *self, const char *prop,
-			unsigned long num);
-void jsonw_lluint_field(json_writer_t *self, const char *prop,
-			unsigned long long num);
 
 /* Collections */
 void jsonw_start_object(json_writer_t *self);
diff --git a/include/libgenl.h b/include/libgenl.h
index 656493a..9db4baf 100644
--- a/include/libgenl.h
+++ b/include/libgenl.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef __LIBGENL_H__
 #define __LIBGENL_H__
 
@@ -21,8 +20,6 @@
 	},								\
 }
 
-int genl_resolve_family(struct rtnl_handle *grth, const char *family);
-int genl_init_handle(struct rtnl_handle *grth, const char *family,
-		     int *genl_family);
+extern int genl_resolve_family(struct rtnl_handle *grth, const char *family);
 
 #endif /* __LIBGENL_H__ */
diff --git a/include/libiptc/ipt_kernel_headers.h b/include/libiptc/ipt_kernel_headers.h
index 3d2a2a3..a5963e9 100644
--- a/include/libiptc/ipt_kernel_headers.h
+++ b/include/libiptc/ipt_kernel_headers.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 /* This is the userspace/kernel interface for Generic IP Chains,
    required for libc6. */
 #ifndef _FWCHAINS_KERNEL_HEADERS_H
diff --git a/include/libiptc/libip6tc.h b/include/libiptc/libip6tc.h
index cd588de..9aed80a 100644
--- a/include/libiptc/libip6tc.h
+++ b/include/libiptc/libip6tc.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _LIBIP6TC_H
 #define _LIBIP6TC_H
 /* Library which manipulates firewall rules. Version 0.2. */
diff --git a/include/libiptc/libiptc.h b/include/libiptc/libiptc.h
index 1bfe4e1..24cdbdb 100644
--- a/include/libiptc/libiptc.h
+++ b/include/libiptc/libiptc.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _LIBIPTC_H
 #define _LIBIPTC_H
 /* Library which manipulates filtering rules. */
diff --git a/include/libiptc/libxtc.h b/include/libiptc/libxtc.h
index 1e9596a..3701018 100644
--- a/include/libiptc/libxtc.h
+++ b/include/libiptc/libxtc.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _LIBXTC_H
 #define _LIBXTC_H
 /* Library which manipulates filtering rules. */
diff --git a/include/libiptc/xtcshared.h b/include/libiptc/xtcshared.h
index 278a58f..773ebc4 100644
--- a/include/libiptc/xtcshared.h
+++ b/include/libiptc/xtcshared.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _LIBXTC_SHARED_H
 #define _LIBXTC_SHARED_H 1
 
diff --git a/include/libnetlink.h b/include/libnetlink.h
index 8ebdc6d..431189e 100644
--- a/include/libnetlink.h
+++ b/include/libnetlink.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef __LIBNETLINK_H__
 #define __LIBNETLINK_H__ 1
 
@@ -11,9 +10,9 @@
 #include <linux/if_addr.h>
 #include <linux/neighbour.h>
 #include <linux/netconf.h>
-#include <arpa/inet.h>
 
-struct rtnl_handle {
+struct rtnl_handle
+{
 	int			fd;
 	struct sockaddr_nl	local;
 	struct sockaddr_nl	peer;
@@ -22,71 +21,23 @@
 	int			proto;
 	FILE		       *dump_fp;
 #define RTNL_HANDLE_F_LISTEN_ALL_NSID		0x01
-#define RTNL_HANDLE_F_SUPPRESS_NLERR		0x02
-#define RTNL_HANDLE_F_STRICT_CHK		0x04
 	int			flags;
 };
 
-struct nlmsg_list {
-	struct nlmsg_list *next;
-	struct nlmsghdr   h;
-};
-
-struct nlmsg_chain {
-	struct nlmsg_list *head;
-	struct nlmsg_list *tail;
-};
-
 extern int rcvbuf;
 
-int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions)
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
 	__attribute__((warn_unused_result));
 
-int rtnl_open_byproto(struct rtnl_handle *rth, unsigned int subscriptions,
+int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
 			     int protocol)
 	__attribute__((warn_unused_result));
-int rtnl_add_nl_group(struct rtnl_handle *rth, unsigned int group)
-	__attribute__((warn_unused_result));
+
 void rtnl_close(struct rtnl_handle *rth);
-void rtnl_set_strict_dump(struct rtnl_handle *rth);
-
-typedef int (*req_filter_fn_t)(struct nlmsghdr *nlh, int reqlen);
-
-int rtnl_addrdump_req(struct rtnl_handle *rth, int family,
-		      req_filter_fn_t filter_fn)
+int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type)
 	__attribute__((warn_unused_result));
-int rtnl_addrlbldump_req(struct rtnl_handle *rth, int family)
-	__attribute__((warn_unused_result));
-int rtnl_routedump_req(struct rtnl_handle *rth, int family,
-		       req_filter_fn_t filter_fn)
-	__attribute__((warn_unused_result));
-int rtnl_ruledump_req(struct rtnl_handle *rth, int family)
-	__attribute__((warn_unused_result));
-int rtnl_neighdump_req(struct rtnl_handle *rth, int family,
-		       req_filter_fn_t filter_fn)
-	__attribute__((warn_unused_result));
-int rtnl_neightbldump_req(struct rtnl_handle *rth, int family)
-	__attribute__((warn_unused_result));
-int rtnl_mdbdump_req(struct rtnl_handle *rth, int family)
-	__attribute__((warn_unused_result));
-int rtnl_netconfdump_req(struct rtnl_handle *rth, int family)
-	__attribute__((warn_unused_result));
-
-int rtnl_linkdump_req(struct rtnl_handle *rth, int fam)
-	__attribute__((warn_unused_result));
-int rtnl_linkdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask)
-	__attribute__((warn_unused_result));
-
-int rtnl_linkdump_req_filter_fn(struct rtnl_handle *rth, int fam,
-				req_filter_fn_t fn)
-	__attribute__((warn_unused_result));
-int rtnl_fdb_linkdump_req_filter_fn(struct rtnl_handle *rth,
-				    req_filter_fn_t filter_fn)
-	__attribute__((warn_unused_result));
-int rtnl_nsiddump_req_filter_fn(struct rtnl_handle *rth, int family,
-				req_filter_fn_t filter_fn)
-	__attribute__((warn_unused_result));
-int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask)
+int rtnl_wilddump_req_filter(struct rtnl_handle *rth, int fam, int type,
+				    __u32 filt_mask)
 	__attribute__((warn_unused_result));
 int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req,
 			     int len)
@@ -94,48 +45,38 @@
 int rtnl_dump_request_n(struct rtnl_handle *rth, struct nlmsghdr *n)
 	__attribute__((warn_unused_result));
 
-int rtnl_nexthopdump_req(struct rtnl_handle *rth, int family,
-			 req_filter_fn_t filter_fn)
-	__attribute__((warn_unused_result));
-
 struct rtnl_ctrl_data {
 	int	nsid;
 };
 
-typedef int (*rtnl_filter_t)(struct nlmsghdr *n, void *);
+typedef int (*rtnl_filter_t)(const struct sockaddr_nl *,
+			     struct nlmsghdr *n, void *);
 
-typedef int (*rtnl_listen_filter_t)(struct rtnl_ctrl_data *,
+typedef int (*rtnl_listen_filter_t)(const struct sockaddr_nl *,
+				    struct rtnl_ctrl_data *,
 				    struct nlmsghdr *n, void *);
 
-typedef int (*nl_ext_ack_fn_t)(const char *errmsg, uint32_t off,
-			       const struct nlmsghdr *inner_nlh);
-
-struct rtnl_dump_filter_arg {
+struct rtnl_dump_filter_arg
+{
 	rtnl_filter_t filter;
 	void *arg1;
 	__u16 nc_flags;
 };
 
+int rtnl_dump_filter_l(struct rtnl_handle *rth,
+			      const struct rtnl_dump_filter_arg *arg);
 int rtnl_dump_filter_nc(struct rtnl_handle *rth,
 			rtnl_filter_t filter,
 			void *arg, __u16 nc_flags);
 #define rtnl_dump_filter(rth, filter, arg) \
 	rtnl_dump_filter_nc(rth, filter, arg, 0)
 int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
-	      struct nlmsghdr **answer)
-	__attribute__((warn_unused_result));
-int rtnl_talk_iov(struct rtnl_handle *rtnl, struct iovec *iovec, size_t iovlen,
-		  struct nlmsghdr **answer)
-	__attribute__((warn_unused_result));
-int rtnl_talk_suppress_rtnl_errmsg(struct rtnl_handle *rtnl, struct nlmsghdr *n,
-				   struct nlmsghdr **answer)
+	      struct nlmsghdr *answer, size_t len)
 	__attribute__((warn_unused_result));
 int rtnl_send(struct rtnl_handle *rth, const void *buf, int)
 	__attribute__((warn_unused_result));
 int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int)
 	__attribute__((warn_unused_result));
-int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn);
-int nl_dump_ext_ack_done(const struct nlmsghdr *nlh, int error);
 
 int addattr(struct nlmsghdr *n, int maxlen, int type);
 int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data);
@@ -162,6 +103,8 @@
 int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
 int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta,
 			      int len, unsigned short flags);
+int parse_rtattr_byindex(struct rtattr *tb[], int max,
+			 struct rtattr *rta, int len);
 struct rtattr *parse_rtattr_one(int type, struct rtattr *rta, int len);
 int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, int len);
 
@@ -190,36 +133,16 @@
 {
 	return *(__u16 *)RTA_DATA(rta);
 }
-static inline __be16 rta_getattr_be16(const struct rtattr *rta)
-{
-	return ntohs(rta_getattr_u16(rta));
-}
 static inline __u32 rta_getattr_u32(const struct rtattr *rta)
 {
 	return *(__u32 *)RTA_DATA(rta);
 }
-static inline __be32 rta_getattr_be32(const struct rtattr *rta)
-{
-	return ntohl(rta_getattr_u32(rta));
-}
 static inline __u64 rta_getattr_u64(const struct rtattr *rta)
 {
 	__u64 tmp;
-
 	memcpy(&tmp, RTA_DATA(rta), sizeof(__u64));
 	return tmp;
 }
-static inline __s32 rta_getattr_s32(const struct rtattr *rta)
-{
-	return *(__s32 *)RTA_DATA(rta);
-}
-static inline __s64 rta_getattr_s64(const struct rtattr *rta)
-{
-	__s64 tmp;
-
-	memcpy(&tmp, RTA_DATA(rta), sizeof(tmp));
-	return tmp;
-}
 static inline const char *rta_getattr_str(const struct rtattr *rta)
 {
 	return (const char *)RTA_DATA(rta);
@@ -236,47 +159,42 @@
 
 #ifndef IFA_RTA
 #define IFA_RTA(r) \
-	((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
 #endif
 #ifndef IFA_PAYLOAD
-#define IFA_PAYLOAD(n)	NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg))
+#define IFA_PAYLOAD(n)	NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
 #endif
 
 #ifndef IFLA_RTA
 #define IFLA_RTA(r) \
-	((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
 #endif
 #ifndef IFLA_PAYLOAD
-#define IFLA_PAYLOAD(n)	NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg))
+#define IFLA_PAYLOAD(n)	NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
 #endif
 
 #ifndef NDA_RTA
 #define NDA_RTA(r) \
-	((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
 #endif
 #ifndef NDA_PAYLOAD
-#define NDA_PAYLOAD(n)	NLMSG_PAYLOAD(n, sizeof(struct ndmsg))
+#define NDA_PAYLOAD(n)	NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
 #endif
 
 #ifndef NDTA_RTA
 #define NDTA_RTA(r) \
-	((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg))))
+	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg))))
 #endif
 #ifndef NDTA_PAYLOAD
-#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct ndtmsg))
+#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg))
 #endif
 
 #ifndef NETNS_RTA
 #define NETNS_RTA(r) \
-	((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))))
+	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))))
 #endif
 #ifndef NETNS_PAYLOAD
-#define NETNS_PAYLOAD(n)	NLMSG_PAYLOAD(n, sizeof(struct rtgenmsg))
-#endif
-
-#ifndef IFLA_STATS_RTA
-#define IFLA_STATS_RTA(r) \
-	((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct if_stats_msg))))
+#define NETNS_PAYLOAD(n)	NLMSG_PAYLOAD(n,sizeof(struct rtgenmsg))
 #endif
 
 /* User defined nlmsg_type which is used mostly for logging netlink
@@ -284,3 +202,4 @@
 #define NLMSG_TSTAMP	15
 
 #endif /* __LIBNETLINK_H__ */
+
diff --git a/include/uapi/linux/atm.h b/include/linux/atm.h
similarity index 98%
rename from include/uapi/linux/atm.h
rename to include/linux/atm.h
index e33ff6b..08e27be 100644
--- a/include/uapi/linux/atm.h
+++ b/include/linux/atm.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /* atm.h - general ATM declarations */
  
 /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
diff --git a/include/uapi/linux/atmapi.h b/include/linux/atmapi.h
similarity index 93%
rename from include/uapi/linux/atmapi.h
rename to include/linux/atmapi.h
index c9bf5c2..8fe54d9 100644
--- a/include/uapi/linux/atmapi.h
+++ b/include/linux/atmapi.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /* atmapi.h - ATM API user space/kernel compatibility */
  
 /* Written 1999,2000 by Werner Almesberger, EPFL ICA */
diff --git a/include/uapi/linux/atmioc.h b/include/linux/atmioc.h
similarity index 95%
rename from include/uapi/linux/atmioc.h
rename to include/linux/atmioc.h
index cd7655e..37f67aa 100644
--- a/include/uapi/linux/atmioc.h
+++ b/include/linux/atmioc.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /* atmioc.h - ranges for ATM-related ioctl numbers */
  
 /* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
diff --git a/include/uapi/linux/atmsap.h b/include/linux/atmsap.h
similarity index 98%
rename from include/uapi/linux/atmsap.h
rename to include/linux/atmsap.h
index fc05248..799b104 100644
--- a/include/uapi/linux/atmsap.h
+++ b/include/linux/atmsap.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /* atmsap.h - ATM Service Access Point addressing definitions */
 
 /* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
new file mode 100644
index 0000000..f970f9d
--- /dev/null
+++ b/include/linux/bpf.h
@@ -0,0 +1,326 @@
+/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+#ifndef __LINUX_BPF_H__
+#define __LINUX_BPF_H__
+
+#include <linux/types.h>
+#include <linux/bpf_common.h>
+
+/* Extended instruction set based on top of classic BPF */
+
+/* instruction classes */
+#define BPF_ALU64	0x07	/* alu mode in double word width */
+
+/* ld/ldx fields */
+#define BPF_DW		0x18	/* double word */
+#define BPF_XADD	0xc0	/* exclusive add */
+
+/* alu/jmp fields */
+#define BPF_MOV		0xb0	/* mov reg to reg */
+#define BPF_ARSH	0xc0	/* sign extending arithmetic shift right */
+
+/* change endianness of a register */
+#define BPF_END		0xd0	/* flags for endianness conversion: */
+#define BPF_TO_LE	0x00	/* convert to little-endian */
+#define BPF_TO_BE	0x08	/* convert to big-endian */
+#define BPF_FROM_LE	BPF_TO_LE
+#define BPF_FROM_BE	BPF_TO_BE
+
+#define BPF_JNE		0x50	/* jump != */
+#define BPF_JSGT	0x60	/* SGT is signed '>', GT in x86 */
+#define BPF_JSGE	0x70	/* SGE is signed '>=', GE in x86 */
+#define BPF_CALL	0x80	/* function call */
+#define BPF_EXIT	0x90	/* function return */
+
+/* Register numbers */
+enum {
+	BPF_REG_0 = 0,
+	BPF_REG_1,
+	BPF_REG_2,
+	BPF_REG_3,
+	BPF_REG_4,
+	BPF_REG_5,
+	BPF_REG_6,
+	BPF_REG_7,
+	BPF_REG_8,
+	BPF_REG_9,
+	BPF_REG_10,
+	__MAX_BPF_REG,
+};
+
+/* BPF has 10 general purpose 64-bit registers and stack frame. */
+#define MAX_BPF_REG	__MAX_BPF_REG
+
+struct bpf_insn {
+	__u8	code;		/* opcode */
+	__u8	dst_reg:4;	/* dest register */
+	__u8	src_reg:4;	/* source register */
+	__s16	off;		/* signed offset */
+	__s32	imm;		/* signed immediate constant */
+};
+
+/* BPF syscall commands, see bpf(2) man-page for details. */
+enum bpf_cmd {
+	BPF_MAP_CREATE,
+	BPF_MAP_LOOKUP_ELEM,
+	BPF_MAP_UPDATE_ELEM,
+	BPF_MAP_DELETE_ELEM,
+	BPF_MAP_GET_NEXT_KEY,
+	BPF_PROG_LOAD,
+	BPF_OBJ_PIN,
+	BPF_OBJ_GET,
+};
+
+enum bpf_map_type {
+	BPF_MAP_TYPE_UNSPEC,
+	BPF_MAP_TYPE_HASH,
+	BPF_MAP_TYPE_ARRAY,
+	BPF_MAP_TYPE_PROG_ARRAY,
+	BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+};
+
+enum bpf_prog_type {
+	BPF_PROG_TYPE_UNSPEC,
+	BPF_PROG_TYPE_SOCKET_FILTER,
+	BPF_PROG_TYPE_KPROBE,
+	BPF_PROG_TYPE_SCHED_CLS,
+	BPF_PROG_TYPE_SCHED_ACT,
+};
+
+#define BPF_PSEUDO_MAP_FD	1
+
+/* flags for BPF_MAP_UPDATE_ELEM command */
+#define BPF_ANY		0 /* create new element or update existing */
+#define BPF_NOEXIST	1 /* create new element if it didn't exist */
+#define BPF_EXIST	2 /* update existing element */
+
+union bpf_attr {
+	struct { /* anonymous struct used by BPF_MAP_CREATE command */
+		__u32	map_type;	/* one of enum bpf_map_type */
+		__u32	key_size;	/* size of key in bytes */
+		__u32	value_size;	/* size of value in bytes */
+		__u32	max_entries;	/* max number of entries in a map */
+	};
+
+	struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
+		__u32		map_fd;
+		__aligned_u64	key;
+		union {
+			__aligned_u64 value;
+			__aligned_u64 next_key;
+		};
+		__u64		flags;
+	};
+
+	struct { /* anonymous struct used by BPF_PROG_LOAD command */
+		__u32		prog_type;	/* one of enum bpf_prog_type */
+		__u32		insn_cnt;
+		__aligned_u64	insns;
+		__aligned_u64	license;
+		__u32		log_level;	/* verbosity level of verifier */
+		__u32		log_size;	/* size of user buffer */
+		__aligned_u64	log_buf;	/* user supplied buffer */
+		__u32		kern_version;	/* checked when prog_type=kprobe */
+	};
+
+	struct { /* anonymous struct used by BPF_OBJ_* commands */
+		__aligned_u64	pathname;
+		__u32		bpf_fd;
+	};
+} __attribute__((aligned(8)));
+
+/* integer value in 'imm' field of BPF_CALL instruction selects which helper
+ * function eBPF program intends to call
+ */
+enum bpf_func_id {
+	BPF_FUNC_unspec,
+	BPF_FUNC_map_lookup_elem, /* void *map_lookup_elem(&map, &key) */
+	BPF_FUNC_map_update_elem, /* int map_update_elem(&map, &key, &value, flags) */
+	BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */
+	BPF_FUNC_probe_read,      /* int bpf_probe_read(void *dst, int size, void *src) */
+	BPF_FUNC_ktime_get_ns,    /* u64 bpf_ktime_get_ns(void) */
+	BPF_FUNC_trace_printk,    /* int bpf_trace_printk(const char *fmt, int fmt_size, ...) */
+	BPF_FUNC_get_prandom_u32, /* u32 prandom_u32(void) */
+	BPF_FUNC_get_smp_processor_id, /* u32 raw_smp_processor_id(void) */
+
+	/**
+	 * skb_store_bytes(skb, offset, from, len, flags) - store bytes into packet
+	 * @skb: pointer to skb
+	 * @offset: offset within packet from skb->mac_header
+	 * @from: pointer where to copy bytes from
+	 * @len: number of bytes to store into packet
+	 * @flags: bit 0 - if true, recompute skb->csum
+	 *         other bits - reserved
+	 * Return: 0 on success
+	 */
+	BPF_FUNC_skb_store_bytes,
+
+	/**
+	 * l3_csum_replace(skb, offset, from, to, flags) - recompute IP checksum
+	 * @skb: pointer to skb
+	 * @offset: offset within packet where IP checksum is located
+	 * @from: old value of header field
+	 * @to: new value of header field
+	 * @flags: bits 0-3 - size of header field
+	 *         other bits - reserved
+	 * Return: 0 on success
+	 */
+	BPF_FUNC_l3_csum_replace,
+
+	/**
+	 * l4_csum_replace(skb, offset, from, to, flags) - recompute TCP/UDP checksum
+	 * @skb: pointer to skb
+	 * @offset: offset within packet where TCP/UDP checksum is located
+	 * @from: old value of header field
+	 * @to: new value of header field
+	 * @flags: bits 0-3 - size of header field
+	 *         bit 4 - is pseudo header
+	 *         other bits - reserved
+	 * Return: 0 on success
+	 */
+	BPF_FUNC_l4_csum_replace,
+
+	/**
+	 * bpf_tail_call(ctx, prog_array_map, index) - jump into another BPF program
+	 * @ctx: context pointer passed to next program
+	 * @prog_array_map: pointer to map which type is BPF_MAP_TYPE_PROG_ARRAY
+	 * @index: index inside array that selects specific program to run
+	 * Return: 0 on success
+	 */
+	BPF_FUNC_tail_call,
+
+	/**
+	 * bpf_clone_redirect(skb, ifindex, flags) - redirect to another netdev
+	 * @skb: pointer to skb
+	 * @ifindex: ifindex of the net device
+	 * @flags: bit 0 - if set, redirect to ingress instead of egress
+	 *         other bits - reserved
+	 * Return: 0 on success
+	 */
+	BPF_FUNC_clone_redirect,
+
+	/**
+	 * u64 bpf_get_current_pid_tgid(void)
+	 * Return: current->tgid << 32 | current->pid
+	 */
+	BPF_FUNC_get_current_pid_tgid,
+
+	/**
+	 * u64 bpf_get_current_uid_gid(void)
+	 * Return: current_gid << 32 | current_uid
+	 */
+	BPF_FUNC_get_current_uid_gid,
+
+	/**
+	 * bpf_get_current_comm(char *buf, int size_of_buf)
+	 * stores current->comm into buf
+	 * Return: 0 on success
+	 */
+	BPF_FUNC_get_current_comm,
+
+	/**
+	 * bpf_get_cgroup_classid(skb) - retrieve a proc's classid
+	 * @skb: pointer to skb
+	 * Return: classid if != 0
+	 */
+	BPF_FUNC_get_cgroup_classid,
+	BPF_FUNC_skb_vlan_push, /* bpf_skb_vlan_push(skb, vlan_proto, vlan_tci) */
+	BPF_FUNC_skb_vlan_pop,  /* bpf_skb_vlan_pop(skb) */
+
+	/**
+	 * bpf_skb_[gs]et_tunnel_key(skb, key, size, flags)
+	 * retrieve or populate tunnel metadata
+	 * @skb: pointer to skb
+	 * @key: pointer to 'struct bpf_tunnel_key'
+	 * @size: size of 'struct bpf_tunnel_key'
+	 * @flags: room for future extensions
+	 * Retrun: 0 on success
+	 */
+	BPF_FUNC_skb_get_tunnel_key,
+	BPF_FUNC_skb_set_tunnel_key,
+	BPF_FUNC_perf_event_read,	/* u64 bpf_perf_event_read(&map, index) */
+	/**
+	 * bpf_redirect(ifindex, flags) - redirect to another netdev
+	 * @ifindex: ifindex of the net device
+	 * @flags: bit 0 - if set, redirect to ingress instead of egress
+	 *         other bits - reserved
+	 * Return: TC_ACT_REDIRECT
+	 */
+	BPF_FUNC_redirect,
+
+	/**
+	 * bpf_get_route_realm(skb) - retrieve a dst's tclassid
+	 * @skb: pointer to skb
+	 * Return: realm if != 0
+	 */
+	BPF_FUNC_get_route_realm,
+
+	/**
+	 * bpf_perf_event_output(ctx, map, index, data, size) - output perf raw sample
+	 * @ctx: struct pt_regs*
+	 * @map: pointer to perf_event_array map
+	 * @index: index of event in the map
+	 * @data: data on stack to be output as raw data
+	 * @size: size of data
+	 * Return: 0 on success
+	 */
+	BPF_FUNC_perf_event_output,
+	BPF_FUNC_skb_load_bytes,
+	__BPF_FUNC_MAX_ID,
+};
+
+/* All flags used by eBPF helper functions, placed here. */
+
+/* BPF_FUNC_skb_store_bytes flags. */
+#define BPF_F_RECOMPUTE_CSUM		(1ULL << 0)
+
+/* BPF_FUNC_l3_csum_replace and BPF_FUNC_l4_csum_replace flags.
+ * First 4 bits are for passing the header field size.
+ */
+#define BPF_F_HDR_FIELD_MASK		0xfULL
+
+/* BPF_FUNC_l4_csum_replace flags. */
+#define BPF_F_PSEUDO_HDR		(1ULL << 4)
+
+/* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */
+#define BPF_F_INGRESS			(1ULL << 0)
+
+/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */
+#define BPF_F_TUNINFO_IPV6		(1ULL << 0)
+
+/* user accessible mirror of in-kernel sk_buff.
+ * new fields can only be added to the end of this structure
+ */
+struct __sk_buff {
+	__u32 len;
+	__u32 pkt_type;
+	__u32 mark;
+	__u32 queue_mapping;
+	__u32 protocol;
+	__u32 vlan_present;
+	__u32 vlan_tci;
+	__u32 vlan_proto;
+	__u32 priority;
+	__u32 ingress_ifindex;
+	__u32 ifindex;
+	__u32 tc_index;
+	__u32 cb[5];
+	__u32 hash;
+	__u32 tc_classid;
+};
+
+struct bpf_tunnel_key {
+	__u32 tunnel_id;
+	union {
+		__u32 remote_ipv4;
+		__u32 remote_ipv6[4];
+	};
+	__u8 tunnel_tos;
+	__u8 tunnel_ttl;
+};
+
+#endif /* __LINUX_BPF_H__ */
diff --git a/include/uapi/linux/bpf_common.h b/include/linux/bpf_common.h
similarity index 85%
rename from include/uapi/linux/bpf_common.h
rename to include/linux/bpf_common.h
index f0fe139..afe7433 100644
--- a/include/uapi/linux/bpf_common.h
+++ b/include/linux/bpf_common.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_BPF_COMMON_H__
 #define __LINUX_BPF_COMMON_H__
 
@@ -15,10 +14,9 @@
 
 /* ld/ldx fields */
 #define BPF_SIZE(code)  ((code) & 0x18)
-#define		BPF_W		0x00 /* 32-bit */
-#define		BPF_H		0x08 /* 16-bit */
-#define		BPF_B		0x10 /*  8-bit */
-/* eBPF		BPF_DW		0x18    64-bit */
+#define		BPF_W		0x00
+#define		BPF_H		0x08
+#define		BPF_B		0x10
 #define BPF_MODE(code)  ((code) & 0xe0)
 #define		BPF_IMM		0x00
 #define		BPF_ABS		0x20
diff --git a/include/uapi/linux/can.h b/include/linux/can.h
similarity index 92%
rename from include/uapi/linux/can.h
rename to include/linux/can.h
index 55b6660..4af39b0 100644
--- a/include/uapi/linux/can.h
+++ b/include/linux/can.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: ((GPL-2.0-only WITH Linux-syscall-note) OR BSD-3-Clause) */
 /*
  * linux/can.h
  *
@@ -77,7 +76,7 @@
 /*
  * Controller Area Network Error Message Frame Mask structure
  *
- * bit 0-28	: error class mask (see include/uapi/linux/can/error.h)
+ * bit 0-28	: error class mask (see include/linux/can/error.h)
  * bit 29-31	: set to zero
  */
 typedef __u32 can_err_mask_t;
@@ -157,8 +156,7 @@
 #define CAN_TP20	4 /* VAG Transport Protocol v2.0 */
 #define CAN_MCNET	5 /* Bosch MCNet */
 #define CAN_ISOTP	6 /* ISO 15765-2 Transport Protocol */
-#define CAN_J1939	7 /* SAE J1939 */
-#define CAN_NPROTO	8
+#define CAN_NPROTO	7
 
 #define SOL_CAN_BASE 100
 
@@ -175,23 +173,6 @@
 		/* transport protocol class address information (e.g. ISOTP) */
 		struct { canid_t rx_id, tx_id; } tp;
 
-		/* J1939 address information */
-		struct {
-			/* 8 byte name when using dynamic addressing */
-			__u64 name;
-
-			/* pgn:
-			 * 8 bit: PS in PDU2 case, else 0
-			 * 8 bit: PF
-			 * 1 bit: DP
-			 * 1 bit: reserved
-			 */
-			__u32 pgn;
-
-			/* 1 byte address */
-			__u8 addr;
-		} j1939;
-
 		/* reserved for future CAN protocols address information */
 	} can_addr;
 };
@@ -215,6 +196,5 @@
 };
 
 #define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
-#define CAN_RAW_FILTER_MAX 512 /* maximum number of can_filter set via setsockopt() */
 
 #endif /* !_UAPI_CAN_H */
diff --git a/include/uapi/linux/can/netlink.h b/include/linux/can/netlink.h
similarity index 88%
rename from include/uapi/linux/can/netlink.h
rename to include/linux/can/netlink.h
index 7159dc6..6d4ec2a 100644
--- a/include/uapi/linux/can/netlink.h
+++ b/include/linux/can/netlink.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
 /*
  * linux/can/netlink.h
  *
@@ -40,15 +39,15 @@
 };
 
 /*
- * CAN hardware-dependent bit-timing constant
+ * CAN harware-dependent bit-timing constant
  *
  * Used for calculating and checking bit-timing parameters
  */
 struct can_bittiming_const {
 	char name[16];		/* Name of the CAN controller hardware */
-	__u32 tseg1_min;	/* Time segment 1 = prop_seg + phase_seg1 */
+	__u32 tseg1_min;	/* Time segement 1 = prop_seg + phase_seg1 */
 	__u32 tseg1_max;
-	__u32 tseg2_min;	/* Time segment 2 = phase_seg2 */
+	__u32 tseg2_min;	/* Time segement 2 = phase_seg2 */
 	__u32 tseg2_max;
 	__u32 sjw_max;		/* Synchronisation jump width */
 	__u32 brp_min;		/* Bit-rate prescaler */
@@ -128,17 +127,9 @@
 	IFLA_CAN_BERR_COUNTER,
 	IFLA_CAN_DATA_BITTIMING,
 	IFLA_CAN_DATA_BITTIMING_CONST,
-	IFLA_CAN_TERMINATION,
-	IFLA_CAN_TERMINATION_CONST,
-	IFLA_CAN_BITRATE_CONST,
-	IFLA_CAN_DATA_BITRATE_CONST,
-	IFLA_CAN_BITRATE_MAX,
 	__IFLA_CAN_MAX
 };
 
 #define IFLA_CAN_MAX	(__IFLA_CAN_MAX - 1)
 
-/* u16 termination range: 1..65535 Ohms */
-#define CAN_TERMINATION_DISABLED 0
-
 #endif /* !_UAPI_CAN_NETLINK_H */
diff --git a/include/uapi/linux/fib_rules.h b/include/linux/fib_rules.h
similarity index 85%
rename from include/uapi/linux/fib_rules.h
rename to include/linux/fib_rules.h
index 232df14..bbf02a6 100644
--- a/include/uapi/linux/fib_rules.h
+++ b/include/linux/fib_rules.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_FIB_RULES_H
 #define __LINUX_FIB_RULES_H
 
@@ -23,7 +22,7 @@
 	__u8		tos;
 
 	__u8		table;
-	__u8		res1;   /* reserved */
+	__u8		res1;	/* reserved */
 	__u8		res2;	/* reserved */
 	__u8		action;
 
@@ -35,11 +34,6 @@
 	__u32		end;
 };
 
-struct fib_rule_port_range {
-	__u16		start;
-	__u16		end;
-};
-
 enum {
 	FRA_UNSPEC,
 	FRA_DST,	/* destination address */
@@ -63,10 +57,6 @@
 	FRA_PAD,
 	FRA_L3MDEV,	/* iif or oif is l3mdev goto its table */
 	FRA_UID_RANGE,	/* UID range */
-	FRA_PROTOCOL,   /* Originator of the rule */
-	FRA_IP_PROTO,	/* ip proto */
-	FRA_SPORT_RANGE, /* sport */
-	FRA_DPORT_RANGE, /* dport */
 	__FRA_MAX
 };
 
diff --git a/include/uapi/linux/filter.h b/include/linux/filter.h
similarity index 96%
rename from include/uapi/linux/filter.h
rename to include/linux/filter.h
index eaef459..e4f2f74 100644
--- a/include/uapi/linux/filter.h
+++ b/include/linux/filter.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /*
  * Linux Socket Filter Data Structures
  */
diff --git a/include/uapi/linux/fou.h b/include/linux/fou.h
similarity index 70%
rename from include/uapi/linux/fou.h
rename to include/linux/fou.h
index 9f91511..744c323 100644
--- a/include/uapi/linux/fou.h
+++ b/include/linux/fou.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /* fou.h - FOU Interface */
 
 #ifndef _LINUX_FOU_H
@@ -16,12 +15,6 @@
 	FOU_ATTR_IPPROTO,			/* u8 */
 	FOU_ATTR_TYPE,				/* u8 */
 	FOU_ATTR_REMCSUM_NOPARTIAL,		/* flag */
-	FOU_ATTR_LOCAL_V4,			/* u32 */
-	FOU_ATTR_LOCAL_V6,			/* in6_addr */
-	FOU_ATTR_PEER_V4,			/* u32 */
-	FOU_ATTR_PEER_V6,			/* in6_addr */
-	FOU_ATTR_PEER_PORT,			/* u16 */
-	FOU_ATTR_IFINDEX,			/* s32 */
 
 	__FOU_ATTR_MAX,
 };
diff --git a/include/uapi/linux/gen_stats.h b/include/linux/gen_stats.h
similarity index 93%
rename from include/uapi/linux/gen_stats.h
rename to include/linux/gen_stats.h
index 065408e..6487317 100644
--- a/include/uapi/linux/gen_stats.h
+++ b/include/linux/gen_stats.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_GEN_STATS_H
 #define __LINUX_GEN_STATS_H
 
@@ -11,8 +10,6 @@
 	TCA_STATS_QUEUE,
 	TCA_STATS_APP,
 	TCA_STATS_RATE_EST64,
-	TCA_STATS_PAD,
-	TCA_STATS_BASIC_HW,
 	__TCA_STATS_MAX,
 };
 #define TCA_STATS_MAX (__TCA_STATS_MAX - 1)
diff --git a/include/uapi/linux/genetlink.h b/include/linux/genetlink.h
similarity index 90%
rename from include/uapi/linux/genetlink.h
rename to include/linux/genetlink.h
index 1317119..8a1d500 100644
--- a/include/uapi/linux/genetlink.h
+++ b/include/linux/genetlink.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_GENERIC_NETLINK_H
 #define __LINUX_GENERIC_NETLINK_H
 
@@ -22,16 +21,14 @@
 #define GENL_CMD_CAP_DO		0x02
 #define GENL_CMD_CAP_DUMP	0x04
 #define GENL_CMD_CAP_HASPOL	0x08
-#define GENL_UNS_ADMIN_PERM	0x10
 
 /*
  * List of reserved static generic netlink identifiers:
  */
+#define GENL_ID_GENERATE	0
 #define GENL_ID_CTRL		NLMSG_MIN_TYPE
 #define GENL_ID_VFS_DQUOT	(NLMSG_MIN_TYPE + 1)
 #define GENL_ID_PMCRAID		(NLMSG_MIN_TYPE + 2)
-/* must be last reserved + 1 */
-#define GENL_START_ALLOC	(NLMSG_MIN_TYPE + 3)
 
 /**************************************************************************
  * Controller
diff --git a/include/uapi/linux/hdlc/ioctl.h b/include/linux/hdlc/ioctl.h
similarity index 97%
rename from include/uapi/linux/hdlc/ioctl.h
rename to include/linux/hdlc/ioctl.h
index 0fe4238..04bc027 100644
--- a/include/uapi/linux/hdlc/ioctl.h
+++ b/include/linux/hdlc/ioctl.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __HDLC_IOCTL_H__
 #define __HDLC_IOCTL_H__
 
diff --git a/include/uapi/linux/if.h b/include/linux/if.h
similarity index 86%
rename from include/uapi/linux/if.h
rename to include/linux/if.h
index 495cdd2..a55a9e0 100644
--- a/include/uapi/linux/if.h
+++ b/include/linux/if.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
 /*
  * INET		An implementation of the TCP/IP protocol suite for the LINUX
  *		operating system.  INET is implemented using the  BSD Socket
@@ -20,22 +19,14 @@
 #ifndef _LINUX_IF_H
 #define _LINUX_IF_H
 
-#include <linux/libc-compat.h>          /* for compatibility with glibc */
 #include <linux/types.h>		/* for "__kernel_caddr_t" et al	*/
 #include <linux/socket.h>		/* for "struct sockaddr" et al	*/
 		/* for "__user" et al           */
 
-#include <sys/socket.h>			/* for struct sockaddr.		*/
-
-#if __UAPI_DEF_IF_IFNAMSIZ
 #define	IFNAMSIZ	16
-#endif /* __UAPI_DEF_IF_IFNAMSIZ */
 #define	IFALIASZ	256
 #include <linux/hdlc/ioctl.h>
 
-/* For glibc compatibility. An empty enum does not compile. */
-#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || \
-    __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0
 /**
  * enum net_device_flags - &struct net_device flags
  *
@@ -46,7 +37,7 @@
  * are shared for all types of net_devices. The sysfs entries are available
  * via /sys/class/net/<dev>/flags. Flags which can be toggled through sysfs
  * are annotated below, note that only a few flags can be toggled and some
- * other flags are always preserved from the original net_device flags
+ * other flags are always always preserved from the original net_device flags
  * even if you try to set them via sysfs. Flags which are always preserved
  * are kept under the flag grouping @IFF_VOLATILE. Flags which are __volatile__
  * are annotated below as such.
@@ -77,8 +68,6 @@
  * @IFF_ECHO: echo sent packets. Volatile.
  */
 enum net_device_flags {
-/* for compatibility with glibc net/if.h */
-#if __UAPI_DEF_IF_NET_DEVICE_FLAGS
 	IFF_UP				= 1<<0,  /* sysfs */
 	IFF_BROADCAST			= 1<<1,  /* __volatile__ */
 	IFF_DEBUG			= 1<<2,  /* sysfs */
@@ -95,17 +84,11 @@
 	IFF_PORTSEL			= 1<<13, /* sysfs */
 	IFF_AUTOMEDIA			= 1<<14, /* sysfs */
 	IFF_DYNAMIC			= 1<<15, /* sysfs */
-#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */
-#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO
 	IFF_LOWER_UP			= 1<<16, /* __volatile__ */
 	IFF_DORMANT			= 1<<17, /* __volatile__ */
 	IFF_ECHO			= 1<<18, /* __volatile__ */
-#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */
 };
-#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 */
 
-/* for compatibility with glibc net/if.h */
-#if __UAPI_DEF_IF_NET_DEVICE_FLAGS
 #define IFF_UP				IFF_UP
 #define IFF_BROADCAST			IFF_BROADCAST
 #define IFF_DEBUG			IFF_DEBUG
@@ -122,13 +105,9 @@
 #define IFF_PORTSEL			IFF_PORTSEL
 #define IFF_AUTOMEDIA			IFF_AUTOMEDIA
 #define IFF_DYNAMIC			IFF_DYNAMIC
-#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */
-
-#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO
 #define IFF_LOWER_UP			IFF_LOWER_UP
 #define IFF_DORMANT			IFF_DORMANT
 #define IFF_ECHO			IFF_ECHO
-#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */
 
 #define IFF_VOLATILE	(IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\
 		IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT)
@@ -187,8 +166,6 @@
  *	being very small might be worth keeping for clean configuration.
  */
 
-/* for compatibility with glibc net/if.h */
-#if __UAPI_DEF_IF_IFMAP
 struct ifmap {
 	unsigned long mem_start;
 	unsigned long mem_end;
@@ -198,7 +175,6 @@
 	unsigned char port;
 	/* 3 bytes spare */
 };
-#endif /* __UAPI_DEF_IF_IFMAP */
 
 struct if_settings {
 	unsigned int type;	/* Type of physical device or protocol */
@@ -224,8 +200,6 @@
  * remainder may be interface specific.
  */
 
-/* for compatibility with glibc net/if.h */
-#if __UAPI_DEF_IF_IFREQ
 struct ifreq {
 #define IFHWADDRLEN	6
 	union
@@ -249,7 +223,6 @@
 		struct	if_settings ifru_settings;
 	} ifr_ifru;
 };
-#endif /* __UAPI_DEF_IF_IFREQ */
 
 #define ifr_name	ifr_ifrn.ifrn_name	/* interface name 	*/
 #define ifr_hwaddr	ifr_ifru.ifru_hwaddr	/* MAC address 		*/
@@ -276,8 +249,6 @@
  * must know all networks accessible).
  */
 
-/* for compatibility with glibc net/if.h */
-#if __UAPI_DEF_IF_IFCONF
 struct ifconf  {
 	int	ifc_len;			/* size of buffer	*/
 	union {
@@ -285,8 +256,6 @@
 		struct ifreq *ifcu_req;
 	} ifc_ifcu;
 };
-#endif /* __UAPI_DEF_IF_IFCONF */
-
 #define	ifc_buf	ifc_ifcu.ifcu_buf		/* buffer address	*/
 #define	ifc_req	ifc_ifcu.ifcu_req		/* array of structures	*/
 
diff --git a/include/uapi/linux/if_addr.h b/include/linux/if_addr.h
similarity index 91%
rename from include/uapi/linux/if_addr.h
rename to include/linux/if_addr.h
index c4dd87f..26f0ecf 100644
--- a/include/uapi/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_IF_ADDR_H
 #define __LINUX_IF_ADDR_H
 
@@ -33,8 +32,6 @@
 	IFA_CACHEINFO,
 	IFA_MULTICAST,
 	IFA_FLAGS,
-	IFA_RT_PRIORITY,  /* u32, priority/metric for prefix route */
-	IFA_TARGET_NETNSID,
 	__IFA_MAX,
 };
 
diff --git a/include/uapi/linux/if_addrlabel.h b/include/linux/if_addrlabel.h
similarity index 91%
rename from include/uapi/linux/if_addrlabel.h
rename to include/linux/if_addrlabel.h
index d1f5974..54580c2 100644
--- a/include/uapi/linux/if_addrlabel.h
+++ b/include/linux/if_addrlabel.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /*
  * if_addrlabel.h - netlink interface for address labels
  *
diff --git a/include/uapi/linux/if_arp.h b/include/linux/if_arp.h
similarity index 90%
rename from include/uapi/linux/if_arp.h
rename to include/linux/if_arp.h
index dbfbc22..d001bdb 100644
--- a/include/uapi/linux/if_arp.h
+++ b/include/linux/if_arp.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
 /*
  * INET		An implementation of the TCP/IP protocol suite for the LINUX
  *		operating system.  INET is implemented using the  BSD Socket
@@ -60,7 +59,6 @@
 #define ARPHRD_LAPB	516		/* LAPB				*/
 #define ARPHRD_DDCMP    517		/* Digital's DDCMP protocol     */
 #define ARPHRD_RAWHDLC	518		/* Raw HDLC			*/
-#define ARPHRD_RAWIP    519		/* Raw IP                       */
 
 #define ARPHRD_TUNNEL	768		/* IPIP tunnel			*/
 #define ARPHRD_TUNNEL6	769		/* IP6IP6 tunnel       		*/
@@ -97,7 +95,6 @@
 #define ARPHRD_IP6GRE	823		/* GRE over IPv6		*/
 #define ARPHRD_NETLINK	824		/* Netlink header		*/
 #define ARPHRD_6LOWPAN	825		/* IPv6 over LoWPAN             */
-#define ARPHRD_VSOCKMON	826		/* Vsock monitor header		*/
 
 #define ARPHRD_VOID	  0xFFFF	/* Void type, nothing is known */
 #define ARPHRD_NONE	  0xFFFE	/* zero header length */
@@ -114,18 +111,18 @@
 
 /* ARP ioctl request. */
 struct arpreq {
-	struct sockaddr	arp_pa;		/* protocol address		 */
-	struct sockaddr	arp_ha;		/* hardware address		 */
-	int		arp_flags;	/* flags			 */
-	struct sockaddr arp_netmask;    /* netmask (only for proxy arps) */
-	char		arp_dev[IFNAMSIZ];
+  struct sockaddr	arp_pa;		/* protocol address		*/
+  struct sockaddr	arp_ha;		/* hardware address		*/
+  int			arp_flags;	/* flags			*/
+  struct sockaddr       arp_netmask;    /* netmask (only for proxy arps) */
+  char			arp_dev[16];
 };
 
 struct arpreq_old {
-	struct sockaddr	arp_pa;		/* protocol address		 */
-	struct sockaddr	arp_ha;		/* hardware address		 */
-	int		arp_flags;	/* flags			 */
-	struct sockaddr	arp_netmask;    /* netmask (only for proxy arps) */
+  struct sockaddr	arp_pa;		/* protocol address		*/
+  struct sockaddr	arp_ha;		/* hardware address		*/
+  int			arp_flags;	/* flags			*/
+  struct sockaddr       arp_netmask;    /* netmask (only for proxy arps) */
 };
 
 /* ARP Flag values. */
diff --git a/include/uapi/linux/if_bridge.h b/include/linux/if_bridge.h
similarity index 62%
rename from include/uapi/linux/if_bridge.h
rename to include/linux/if_bridge.h
index 31fc51b..ee197a3 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
 /*
  *	Linux ethernet bridge
  *
@@ -119,7 +118,6 @@
 	IFLA_BRIDGE_FLAGS,
 	IFLA_BRIDGE_MODE,
 	IFLA_BRIDGE_VLAN_INFO,
-	IFLA_BRIDGE_VLAN_TUNNEL_INFO,
 	__IFLA_BRIDGE_MAX,
 };
 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
@@ -136,40 +134,14 @@
 	__u16 vid;
 };
 
-enum {
-	IFLA_BRIDGE_VLAN_TUNNEL_UNSPEC,
-	IFLA_BRIDGE_VLAN_TUNNEL_ID,
-	IFLA_BRIDGE_VLAN_TUNNEL_VID,
-	IFLA_BRIDGE_VLAN_TUNNEL_FLAGS,
-	__IFLA_BRIDGE_VLAN_TUNNEL_MAX,
-};
-
-#define IFLA_BRIDGE_VLAN_TUNNEL_MAX (__IFLA_BRIDGE_VLAN_TUNNEL_MAX - 1)
-
-struct bridge_vlan_xstats {
-	__u64 rx_bytes;
-	__u64 rx_packets;
-	__u64 tx_bytes;
-	__u64 tx_packets;
-	__u16 vid;
-	__u16 flags;
-	__u32 pad2;
-};
-
 /* Bridge multicast database attributes
  * [MDBA_MDB] = {
  *     [MDBA_MDB_ENTRY] = {
- *         [MDBA_MDB_ENTRY_INFO] {
- *		struct br_mdb_entry
- *		[MDBA_MDB_EATTR attributes]
- *         }
+ *         [MDBA_MDB_ENTRY_INFO]
  *     }
  * }
  * [MDBA_ROUTER] = {
- *    [MDBA_ROUTER_PORT] = {
- *        u32 ifindex
- *        [MDBA_ROUTER_PATTR attributes]
- *    }
+ *    [MDBA_ROUTER_PORT]
  * }
  */
 enum {
@@ -194,22 +166,6 @@
 };
 #define MDBA_MDB_ENTRY_MAX (__MDBA_MDB_ENTRY_MAX - 1)
 
-/* per mdb entry additional attributes */
-enum {
-	MDBA_MDB_EATTR_UNSPEC,
-	MDBA_MDB_EATTR_TIMER,
-	__MDBA_MDB_EATTR_MAX
-};
-#define MDBA_MDB_EATTR_MAX (__MDBA_MDB_EATTR_MAX - 1)
-
-/* multicast router types */
-enum {
-	MDB_RTR_TYPE_DISABLED,
-	MDB_RTR_TYPE_TEMP_QUERY,
-	MDB_RTR_TYPE_PERM,
-	MDB_RTR_TYPE_TEMP
-};
-
 enum {
 	MDBA_ROUTER_UNSPEC,
 	MDBA_ROUTER_PORT,
@@ -217,15 +173,6 @@
 };
 #define MDBA_ROUTER_MAX (__MDBA_ROUTER_MAX - 1)
 
-/* router port attributes */
-enum {
-	MDBA_ROUTER_PATTR_UNSPEC,
-	MDBA_ROUTER_PATTR_TIMER,
-	MDBA_ROUTER_PATTR_TYPE,
-	__MDBA_ROUTER_PATTR_MAX
-};
-#define MDBA_ROUTER_PATTR_MAX (__MDBA_ROUTER_PATTR_MAX - 1)
-
 struct br_port_msg {
 	__u8  family;
 	__u32 ifindex;
@@ -236,9 +183,6 @@
 #define MDB_TEMPORARY 0
 #define MDB_PERMANENT 1
 	__u8 state;
-#define MDB_FLAGS_OFFLOAD	(1 << 0)
-#define MDB_FLAGS_FAST_LEAVE	(1 << 1)
-	__u8 flags;
 	__u16 vid;
 	struct {
 		union {
@@ -256,62 +200,4 @@
 };
 #define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1)
 
-/* Embedded inside LINK_XSTATS_TYPE_BRIDGE */
-enum {
-	BRIDGE_XSTATS_UNSPEC,
-	BRIDGE_XSTATS_VLAN,
-	BRIDGE_XSTATS_MCAST,
-	BRIDGE_XSTATS_PAD,
-	__BRIDGE_XSTATS_MAX
-};
-#define BRIDGE_XSTATS_MAX (__BRIDGE_XSTATS_MAX - 1)
-
-enum {
-	BR_MCAST_DIR_RX,
-	BR_MCAST_DIR_TX,
-	BR_MCAST_DIR_SIZE
-};
-
-/* IGMP/MLD statistics */
-struct br_mcast_stats {
-	__u64 igmp_v1queries[BR_MCAST_DIR_SIZE];
-	__u64 igmp_v2queries[BR_MCAST_DIR_SIZE];
-	__u64 igmp_v3queries[BR_MCAST_DIR_SIZE];
-	__u64 igmp_leaves[BR_MCAST_DIR_SIZE];
-	__u64 igmp_v1reports[BR_MCAST_DIR_SIZE];
-	__u64 igmp_v2reports[BR_MCAST_DIR_SIZE];
-	__u64 igmp_v3reports[BR_MCAST_DIR_SIZE];
-	__u64 igmp_parse_errors;
-
-	__u64 mld_v1queries[BR_MCAST_DIR_SIZE];
-	__u64 mld_v2queries[BR_MCAST_DIR_SIZE];
-	__u64 mld_leaves[BR_MCAST_DIR_SIZE];
-	__u64 mld_v1reports[BR_MCAST_DIR_SIZE];
-	__u64 mld_v2reports[BR_MCAST_DIR_SIZE];
-	__u64 mld_parse_errors;
-
-	__u64 mcast_bytes[BR_MCAST_DIR_SIZE];
-	__u64 mcast_packets[BR_MCAST_DIR_SIZE];
-};
-
-/* bridge boolean options
- * BR_BOOLOPT_NO_LL_LEARN - disable learning from link-local packets
- *
- * IMPORTANT: if adding a new option do not forget to handle
- *            it in br_boolopt_toggle/get and bridge sysfs
- */
-enum br_boolopt_id {
-	BR_BOOLOPT_NO_LL_LEARN,
-	BR_BOOLOPT_MAX
-};
-
-/* struct br_boolopt_multi - change multiple bridge boolean options
- *
- * @optval: new option values (bit per option)
- * @optmask: options to change (bit per option)
- */
-struct br_boolopt_multi {
-	__u32 optval;
-	__u32 optmask;
-};
 #endif /* _LINUX_IF_BRIDGE_H */
diff --git a/include/uapi/linux/if_ether.h b/include/linux/if_ether.h
similarity index 85%
rename from include/uapi/linux/if_ether.h
rename to include/linux/if_ether.h
index 728c42d..bf278d6 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/linux/if_ether.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
 /*
  * INET		An implementation of the TCP/IP protocol suite for the LINUX
  *		operating system.  INET is implemented using the  BSD Socket
@@ -30,16 +29,12 @@
  */
 
 #define ETH_ALEN	6		/* Octets in one ethernet addr	 */
-#define ETH_TLEN	2		/* Octets in ethernet type field */
 #define ETH_HLEN	14		/* Total octets in header.	 */
 #define ETH_ZLEN	60		/* Min. octets in frame sans FCS */
 #define ETH_DATA_LEN	1500		/* Max. octets in payload	 */
 #define ETH_FRAME_LEN	1514		/* Max. octets in frame sans FCS */
 #define ETH_FCS_LEN	4		/* Octets in the FCS		 */
 
-#define ETH_MIN_MTU	68		/* Min IPv4 MTU per RFC791	*/
-#define ETH_MAX_MTU	0xFFFFU		/* 65535, same as IP_MAX_MTU	*/
-
 /*
  *	These are the defined Ethernet Protocol ID's.
  */
@@ -48,7 +43,6 @@
 #define ETH_P_PUP	0x0200		/* Xerox PUP packet		*/
 #define ETH_P_PUPAT	0x0201		/* Xerox PUP Addr Trans packet	*/
 #define ETH_P_TSN	0x22F0		/* TSN (IEEE 1722) packet	*/
-#define ETH_P_ERSPAN2	0x22EB		/* ERSPAN version 2 (type III)	*/
 #define ETH_P_IP	0x0800		/* Internet Protocol packet	*/
 #define ETH_P_X25	0x0805		/* CCITT X.25			*/
 #define ETH_P_ARP	0x0806		/* Address Resolution packet	*/
@@ -69,7 +63,6 @@
 #define ETH_P_ATALK	0x809B		/* Appletalk DDP		*/
 #define ETH_P_AARP	0x80F3		/* Appletalk AARP		*/
 #define ETH_P_8021Q	0x8100          /* 802.1Q VLAN Extended Header  */
-#define ETH_P_ERSPAN	0x88BE		/* ERSPAN type II		*/
 #define ETH_P_IPX	0x8137		/* IPX over DIX			*/
 #define ETH_P_IPV6	0x86DD		/* IPv6 over bluebook		*/
 #define ETH_P_PAUSE	0x8808		/* IEEE Pause frames. See 802.3 31B */
@@ -89,29 +82,20 @@
 #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_PREAUTH	0x88C7		/* 802.11 Preauthentication */
 #define ETH_P_TIPC	0x88CA		/* TIPC 			*/
-#define ETH_P_LLDP	0x88CC		/* Link Layer Discovery Protocol */
-#define ETH_P_MACSEC	0x88E5		/* 802.1ae MACsec */
 #define ETH_P_8021AH	0x88E7          /* 802.1ah Backbone Service Tag */
 #define ETH_P_MVRP	0x88F5          /* 802.1Q MVRP                  */
 #define ETH_P_1588	0x88F7		/* IEEE 1588 Timesync */
-#define ETH_P_NCSI	0x88F8		/* NCSI protocol		*/
 #define ETH_P_PRP	0x88FB		/* IEC 62439-3 PRP/HSRv0	*/
 #define ETH_P_FCOE	0x8906		/* Fibre Channel over Ethernet  */
-#define ETH_P_IBOE	0x8915		/* Infiniband over Ethernet	*/
 #define ETH_P_TDLS	0x890D          /* TDLS */
 #define ETH_P_FIP	0x8914		/* FCoE Initialization Protocol */
 #define ETH_P_80221	0x8917		/* IEEE 802.21 Media Independent Handover Protocol */
-#define ETH_P_HSR	0x892F		/* IEC 62439-3 HSRv1	*/
-#define ETH_P_NSH	0x894F		/* Network Service Header */
 #define ETH_P_LOOPBACK	0x9000		/* Ethernet loopback packet, per IEEE 802.3 */
 #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_DSA_8021Q	0xDADB		/* Fake VLAN Header for DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
-#define ETH_P_IFE	0xED3E		/* ForCES inter-FE LFB type */
 #define ETH_P_AF_IUCV   0xFBFB		/* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */
 
 #define ETH_P_802_3_MIN	0x0600		/* If the value in the ethernet type is less than this value
@@ -146,26 +130,16 @@
 #define ETH_P_IEEE802154 0x00F6		/* IEEE802.15.4 frame		*/
 #define ETH_P_CAIF	0x00F7		/* ST-Ericsson CAIF protocol	*/
 #define ETH_P_XDSA	0x00F8		/* Multiplexed DSA protocol	*/
-#define ETH_P_MAP	0x00F9		/* Qualcomm multiplexing and
-					 * aggregation protocol
-					 */
 
 /*
  *	This is an Ethernet frame header.
  */
 
-/* allow libcs like musl to deactivate this, glibc does not implement this. */
-#ifndef __UAPI_DEF_ETHHDR
-#define __UAPI_DEF_ETHHDR		1
-#endif
-
-#if __UAPI_DEF_ETHHDR
 struct ethhdr {
 	unsigned char	h_dest[ETH_ALEN];	/* destination eth addr	*/
 	unsigned char	h_source[ETH_ALEN];	/* source ether addr	*/
 	__be16		h_proto;		/* packet type ID field	*/
 } __attribute__((packed));
-#endif
 
 
 #endif /* _LINUX_IF_ETHER_H */
diff --git a/include/uapi/linux/if_link.h b/include/linux/if_link.h
similarity index 72%
rename from include/uapi/linux/if_link.h
rename to include/linux/if_link.h
index d36919f..d91f2c9 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _LINUX_IF_LINK_H
 #define _LINUX_IF_LINK_H
 
@@ -36,8 +35,6 @@
 	/* for cslip etc */
 	__u32	rx_compressed;
 	__u32	tx_compressed;
-
-	__u32	rx_nohandler;		/* dropped, no handler found	*/
 };
 
 /* The main device statistics structure */
@@ -71,8 +68,6 @@
 	/* for cslip etc */
 	__u64	rx_compressed;
 	__u64	tx_compressed;
-
-	__u64	rx_nohandler;		/* dropped, no handler found	*/
 };
 
 /* The struct should be in sync with struct ifmap */
@@ -154,19 +149,6 @@
 	IFLA_LINK_NETNSID,
 	IFLA_PHYS_PORT_NAME,
 	IFLA_PROTO_DOWN,
-	IFLA_GSO_MAX_SEGS,
-	IFLA_GSO_MAX_SIZE,
-	IFLA_PAD,
-	IFLA_XDP,
-	IFLA_EVENT,
-	IFLA_NEW_NETNSID,
-	IFLA_IF_NETNSID,
-	IFLA_TARGET_NETNSID = IFLA_IF_NETNSID, /* new alias */
-	IFLA_CARRIER_UP_COUNT,
-	IFLA_CARRIER_DOWN_COUNT,
-	IFLA_NEW_IFINDEX,
-	IFLA_MIN_MTU,
-	IFLA_MAX_MTU,
 	__IFLA_MAX
 };
 
@@ -280,13 +262,6 @@
 	IFLA_BR_NF_CALL_IP6TABLES,
 	IFLA_BR_NF_CALL_ARPTABLES,
 	IFLA_BR_VLAN_DEFAULT_PVID,
-	IFLA_BR_PAD,
-	IFLA_BR_VLAN_STATS_ENABLED,
-	IFLA_BR_MCAST_STATS_ENABLED,
-	IFLA_BR_MCAST_IGMP_VERSION,
-	IFLA_BR_MCAST_MLD_VERSION,
-	IFLA_BR_VLAN_STATS_PER_PORT,
-	IFLA_BR_MULTI_BOOLOPT,
 	__IFLA_BR_MAX,
 };
 
@@ -329,15 +304,6 @@
 	IFLA_BRPORT_HOLD_TIMER,
 	IFLA_BRPORT_FLUSH,
 	IFLA_BRPORT_MULTICAST_ROUTER,
-	IFLA_BRPORT_PAD,
-	IFLA_BRPORT_MCAST_FLOOD,
-	IFLA_BRPORT_MCAST_TO_UCAST,
-	IFLA_BRPORT_VLAN_TUNNEL,
-	IFLA_BRPORT_BCAST_FLOOD,
-	IFLA_BRPORT_GROUP_FWD_MASK,
-	IFLA_BRPORT_NEIGH_SUPPRESS,
-	IFLA_BRPORT_ISOLATED,
-	IFLA_BRPORT_BACKUP_PORT,
 	__IFLA_BRPORT_MAX
 };
 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
@@ -433,59 +399,10 @@
 
 #define IFLA_VRF_MAX (__IFLA_VRF_MAX - 1)
 
-enum {
-	IFLA_VRF_PORT_UNSPEC,
-	IFLA_VRF_PORT_TABLE,
-	__IFLA_VRF_PORT_MAX
-};
-
-#define IFLA_VRF_PORT_MAX (__IFLA_VRF_PORT_MAX - 1)
-
-/* MACSEC section */
-enum {
-	IFLA_MACSEC_UNSPEC,
-	IFLA_MACSEC_SCI,
-	IFLA_MACSEC_PORT,
-	IFLA_MACSEC_ICV_LEN,
-	IFLA_MACSEC_CIPHER_SUITE,
-	IFLA_MACSEC_WINDOW,
-	IFLA_MACSEC_ENCODING_SA,
-	IFLA_MACSEC_ENCRYPT,
-	IFLA_MACSEC_PROTECT,
-	IFLA_MACSEC_INC_SCI,
-	IFLA_MACSEC_ES,
-	IFLA_MACSEC_SCB,
-	IFLA_MACSEC_REPLAY_PROTECT,
-	IFLA_MACSEC_VALIDATION,
-	IFLA_MACSEC_PAD,
-	__IFLA_MACSEC_MAX,
-};
-
-#define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1)
-
-/* XFRM section */
-enum {
-	IFLA_XFRM_UNSPEC,
-	IFLA_XFRM_LINK,
-	IFLA_XFRM_IF_ID,
-	__IFLA_XFRM_MAX
-};
-
-#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1)
-
-enum macsec_validation_type {
-	MACSEC_VALIDATE_DISABLED = 0,
-	MACSEC_VALIDATE_CHECK = 1,
-	MACSEC_VALIDATE_STRICT = 2,
-	__MACSEC_VALIDATE_END,
-	MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1,
-};
-
 /* IPVLAN section */
 enum {
 	IFLA_IPVLAN_UNSPEC,
 	IFLA_IPVLAN_MODE,
-	IFLA_IPVLAN_FLAGS,
 	__IFLA_IPVLAN_MAX
 };
 
@@ -494,13 +411,9 @@
 enum ipvlan_mode {
 	IPVLAN_MODE_L2 = 0,
 	IPVLAN_MODE_L3,
-	IPVLAN_MODE_L3S,
 	IPVLAN_MODE_MAX
 };
 
-#define IPVLAN_F_PRIVATE	0x01
-#define IPVLAN_F_VEPA		0x02
-
 /* VXLAN section */
 enum {
 	IFLA_VXLAN_UNSPEC,
@@ -529,10 +442,6 @@
 	IFLA_VXLAN_GBP,
 	IFLA_VXLAN_REMCSUM_NOPARTIAL,
 	IFLA_VXLAN_COLLECT_METADATA,
-	IFLA_VXLAN_LABEL,
-	IFLA_VXLAN_GPE,
-	IFLA_VXLAN_TTL_INHERIT,
-	IFLA_VXLAN_DF,
 	__IFLA_VXLAN_MAX
 };
 #define IFLA_VXLAN_MAX	(__IFLA_VXLAN_MAX - 1)
@@ -542,14 +451,6 @@
 	__be16	high;
 };
 
-enum ifla_vxlan_df {
-	VXLAN_DF_UNSET = 0,
-	VXLAN_DF_SET,
-	VXLAN_DF_INHERIT,
-	__VXLAN_DF_END,
-	VXLAN_DF_MAX = __VXLAN_DF_END - 1,
-};
-
 /* GENEVE section */
 enum {
 	IFLA_GENEVE_UNSPEC,
@@ -563,46 +464,10 @@
 	IFLA_GENEVE_UDP_CSUM,
 	IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
 	IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
-	IFLA_GENEVE_LABEL,
-	IFLA_GENEVE_TTL_INHERIT,
-	IFLA_GENEVE_DF,
 	__IFLA_GENEVE_MAX
 };
 #define IFLA_GENEVE_MAX	(__IFLA_GENEVE_MAX - 1)
 
-enum ifla_geneve_df {
-	GENEVE_DF_UNSET = 0,
-	GENEVE_DF_SET,
-	GENEVE_DF_INHERIT,
-	__GENEVE_DF_END,
-	GENEVE_DF_MAX = __GENEVE_DF_END - 1,
-};
-
-/* PPP section */
-enum {
-	IFLA_PPP_UNSPEC,
-	IFLA_PPP_DEV_FD,
-	__IFLA_PPP_MAX
-};
-#define IFLA_PPP_MAX (__IFLA_PPP_MAX - 1)
-
-/* GTP section */
-
-enum ifla_gtp_role {
-	GTP_ROLE_GGSN = 0,
-	GTP_ROLE_SGSN,
-};
-
-enum {
-	IFLA_GTP_UNSPEC,
-	IFLA_GTP_FD0,
-	IFLA_GTP_FD1,
-	IFLA_GTP_PDP_HASHSIZE,
-	IFLA_GTP_ROLE,
-	__IFLA_GTP_MAX,
-};
-#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)
-
 /* Bonding section */
 
 enum {
@@ -634,7 +499,6 @@
 	IFLA_BOND_AD_USER_PORT_KEY,
 	IFLA_BOND_AD_ACTOR_SYSTEM,
 	IFLA_BOND_TLB_DYNAMIC_LB,
-	IFLA_BOND_PEER_NOTIF_DELAY,
 	__IFLA_BOND_MAX,
 };
 
@@ -680,7 +544,7 @@
 enum {
 	IFLA_VF_UNSPEC,
 	IFLA_VF_MAC,		/* Hardware queue specific attributes */
-	IFLA_VF_VLAN,		/* VLAN ID and QoS */
+	IFLA_VF_VLAN,
 	IFLA_VF_TX_RATE,	/* Max TX Bandwidth Allocation */
 	IFLA_VF_SPOOFCHK,	/* Spoof Checking on/off switch */
 	IFLA_VF_LINK_STATE,	/* link state enable/disable/auto switch */
@@ -690,10 +554,6 @@
 				 */
 	IFLA_VF_STATS,		/* network device statistics */
 	IFLA_VF_TRUST,		/* Trust VF */
-	IFLA_VF_IB_NODE_GUID,	/* VF Infiniband node GUID */
-	IFLA_VF_IB_PORT_GUID,	/* VF Infiniband port GUID */
-	IFLA_VF_VLAN_LIST,	/* nested list of vlans, option for QinQ */
-	IFLA_VF_BROADCAST,	/* VF broadcast */
 	__IFLA_VF_MAX,
 };
 
@@ -704,32 +564,12 @@
 	__u8 mac[32]; /* MAX_ADDR_LEN */
 };
 
-struct ifla_vf_broadcast {
-	__u8 broadcast[32];
-};
-
 struct ifla_vf_vlan {
 	__u32 vf;
 	__u32 vlan; /* 0 - 4095, 0 disables VLAN filter */
 	__u32 qos;
 };
 
-enum {
-	IFLA_VF_VLAN_INFO_UNSPEC,
-	IFLA_VF_VLAN_INFO,	/* VLAN ID, QoS and VLAN protocol */
-	__IFLA_VF_VLAN_INFO_MAX,
-};
-
-#define IFLA_VF_VLAN_INFO_MAX (__IFLA_VF_VLAN_INFO_MAX - 1)
-#define MAX_VLAN_LIST_LEN 1
-
-struct ifla_vf_vlan_info {
-	__u32 vf;
-	__u32 vlan; /* 0 - 4095, 0 disables VLAN filter */
-	__u32 qos;
-	__be16 vlan_proto; /* VLAN protocol either 802.1Q or 802.1ad */
-};
-
 struct ifla_vf_tx_rate {
 	__u32 vf;
 	__u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */
@@ -746,11 +586,6 @@
 	__u32 setting;
 };
 
-struct ifla_vf_guid {
-	__u32 vf;
-	__u64 guid;
-};
-
 enum {
 	IFLA_VF_LINK_STATE_AUTO,	/* link state of the uplink */
 	IFLA_VF_LINK_STATE_ENABLE,	/* link always up */
@@ -775,9 +610,6 @@
 	IFLA_VF_STATS_TX_BYTES,
 	IFLA_VF_STATS_BROADCAST,
 	IFLA_VF_STATS_MULTICAST,
-	IFLA_VF_STATS_PAD,
-	IFLA_VF_STATS_RX_DROPPED,
-	IFLA_VF_STATS_TX_DROPPED,
 	__IFLA_VF_STATS_MAX,
 };
 
@@ -888,142 +720,9 @@
 	IFLA_HSR_MULTICAST_SPEC,	/* Last byte of supervision addr */
 	IFLA_HSR_SUPERVISION_ADDR,	/* Supervision frame multicast addr */
 	IFLA_HSR_SEQ_NR,
-	IFLA_HSR_VERSION,		/* HSR version */
 	__IFLA_HSR_MAX,
 };
 
 #define IFLA_HSR_MAX (__IFLA_HSR_MAX - 1)
 
-/* STATS section */
-
-struct if_stats_msg {
-	__u8  family;
-	__u8  pad1;
-	__u16 pad2;
-	__u32 ifindex;
-	__u32 filter_mask;
-};
-
-/* A stats attribute can be netdev specific or a global stat.
- * For netdev stats, lets use the prefix IFLA_STATS_LINK_*
- */
-enum {
-	IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */
-	IFLA_STATS_LINK_64,
-	IFLA_STATS_LINK_XSTATS,
-	IFLA_STATS_LINK_XSTATS_SLAVE,
-	IFLA_STATS_LINK_OFFLOAD_XSTATS,
-	IFLA_STATS_AF_SPEC,
-	__IFLA_STATS_MAX,
-};
-
-#define IFLA_STATS_MAX (__IFLA_STATS_MAX - 1)
-
-#define IFLA_STATS_FILTER_BIT(ATTR)	(1 << (ATTR - 1))
-
-/* These are embedded into IFLA_STATS_LINK_XSTATS:
- * [IFLA_STATS_LINK_XSTATS]
- * -> [LINK_XSTATS_TYPE_xxx]
- *    -> [rtnl link type specific attributes]
- */
-enum {
-	LINK_XSTATS_TYPE_UNSPEC,
-	LINK_XSTATS_TYPE_BRIDGE,
-	LINK_XSTATS_TYPE_BOND,
-	__LINK_XSTATS_TYPE_MAX
-};
-#define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1)
-
-/* These are stats embedded into IFLA_STATS_LINK_OFFLOAD_XSTATS */
-enum {
-	IFLA_OFFLOAD_XSTATS_UNSPEC,
-	IFLA_OFFLOAD_XSTATS_CPU_HIT, /* struct rtnl_link_stats64 */
-	__IFLA_OFFLOAD_XSTATS_MAX
-};
-#define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1)
-
-/* XDP section */
-
-#define XDP_FLAGS_UPDATE_IF_NOEXIST	(1U << 0)
-#define XDP_FLAGS_SKB_MODE		(1U << 1)
-#define XDP_FLAGS_DRV_MODE		(1U << 2)
-#define XDP_FLAGS_HW_MODE		(1U << 3)
-#define XDP_FLAGS_MODES			(XDP_FLAGS_SKB_MODE | \
-					 XDP_FLAGS_DRV_MODE | \
-					 XDP_FLAGS_HW_MODE)
-#define XDP_FLAGS_MASK			(XDP_FLAGS_UPDATE_IF_NOEXIST | \
-					 XDP_FLAGS_MODES)
-
-/* These are stored into IFLA_XDP_ATTACHED on dump. */
-enum {
-	XDP_ATTACHED_NONE = 0,
-	XDP_ATTACHED_DRV,
-	XDP_ATTACHED_SKB,
-	XDP_ATTACHED_HW,
-	XDP_ATTACHED_MULTI,
-};
-
-enum {
-	IFLA_XDP_UNSPEC,
-	IFLA_XDP_FD,
-	IFLA_XDP_ATTACHED,
-	IFLA_XDP_FLAGS,
-	IFLA_XDP_PROG_ID,
-	IFLA_XDP_DRV_PROG_ID,
-	IFLA_XDP_SKB_PROG_ID,
-	IFLA_XDP_HW_PROG_ID,
-	__IFLA_XDP_MAX,
-};
-
-#define IFLA_XDP_MAX (__IFLA_XDP_MAX - 1)
-
-enum {
-	IFLA_EVENT_NONE,
-	IFLA_EVENT_REBOOT,		/* internal reset / reboot */
-	IFLA_EVENT_FEATURES,		/* change in offload features */
-	IFLA_EVENT_BONDING_FAILOVER,	/* change in active slave */
-	IFLA_EVENT_NOTIFY_PEERS,	/* re-sent grat. arp/ndisc */
-	IFLA_EVENT_IGMP_RESEND,		/* re-sent IGMP JOIN */
-	IFLA_EVENT_BONDING_OPTIONS,	/* change in bonding options */
-};
-
-/* tun section */
-
-enum {
-	IFLA_TUN_UNSPEC,
-	IFLA_TUN_OWNER,
-	IFLA_TUN_GROUP,
-	IFLA_TUN_TYPE,
-	IFLA_TUN_PI,
-	IFLA_TUN_VNET_HDR,
-	IFLA_TUN_PERSIST,
-	IFLA_TUN_MULTI_QUEUE,
-	IFLA_TUN_NUM_QUEUES,
-	IFLA_TUN_NUM_DISABLED_QUEUES,
-	__IFLA_TUN_MAX,
-};
-
-#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1)
-
-/* rmnet section */
-
-#define RMNET_FLAGS_INGRESS_DEAGGREGATION         (1U << 0)
-#define RMNET_FLAGS_INGRESS_MAP_COMMANDS          (1U << 1)
-#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4           (1U << 2)
-#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4            (1U << 3)
-
-enum {
-	IFLA_RMNET_UNSPEC,
-	IFLA_RMNET_MUX_ID,
-	IFLA_RMNET_FLAGS,
-	__IFLA_RMNET_MAX,
-};
-
-#define IFLA_RMNET_MAX	(__IFLA_RMNET_MAX - 1)
-
-struct ifla_rmnet_flags {
-	__u32	flags;
-	__u32	mask;
-};
-
 #endif /* _LINUX_IF_LINK_H */
diff --git a/include/uapi/linux/if_tun.h b/include/linux/if_tun.h
similarity index 92%
rename from include/uapi/linux/if_tun.h
rename to include/linux/if_tun.h
index 8489ae0..d5ecb42 100644
--- a/include/uapi/linux/if_tun.h
+++ b/include/linux/if_tun.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
 /*
  *  Universal TUN/TAP device driver.
  *  Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
@@ -57,16 +56,10 @@
  */
 #define TUNSETVNETBE _IOW('T', 222, int)
 #define TUNGETVNETBE _IOR('T', 223, int)
-#define TUNSETSTEERINGEBPF _IOR('T', 224, int)
-#define TUNSETFILTEREBPF _IOR('T', 225, int)
-#define TUNSETCARRIER _IOW('T', 226, int)
-#define TUNGETDEVNETNS _IO('T', 227)
 
 /* TUNSETIFF ifr flags */
 #define IFF_TUN		0x0001
 #define IFF_TAP		0x0002
-#define IFF_NAPI	0x0010
-#define IFF_NAPI_FRAGS	0x0020
 #define IFF_NO_PI	0x1000
 /* This flag has no real effect */
 #define IFF_ONE_QUEUE	0x2000
diff --git a/include/uapi/linux/if_tunnel.h b/include/linux/if_tunnel.h
similarity index 63%
rename from include/uapi/linux/if_tunnel.h
rename to include/linux/if_tunnel.h
index c7f0a5e..f0201ca 100644
--- a/include/uapi/linux/if_tunnel.h
+++ b/include/linux/if_tunnel.h
@@ -1,11 +1,7 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _IF_TUNNEL_H_
 #define _IF_TUNNEL_H_
 
 #include <linux/types.h>
-#include <linux/if.h>
-#include <linux/ip.h>
-#include <linux/in6.h>
 #include <asm/byteorder.h>
 
 
@@ -28,23 +24,9 @@
 #define GRE_SEQ		__cpu_to_be16(0x1000)
 #define GRE_STRICT	__cpu_to_be16(0x0800)
 #define GRE_REC		__cpu_to_be16(0x0700)
-#define GRE_ACK		__cpu_to_be16(0x0080)
-#define GRE_FLAGS	__cpu_to_be16(0x0078)
+#define GRE_FLAGS	__cpu_to_be16(0x00F8)
 #define GRE_VERSION	__cpu_to_be16(0x0007)
 
-#define GRE_IS_CSUM(f)		((f) & GRE_CSUM)
-#define GRE_IS_ROUTING(f)	((f) & GRE_ROUTING)
-#define GRE_IS_KEY(f)		((f) & GRE_KEY)
-#define GRE_IS_SEQ(f)		((f) & GRE_SEQ)
-#define GRE_IS_STRICT(f)	((f) & GRE_STRICT)
-#define GRE_IS_REC(f)		((f) & GRE_REC)
-#define GRE_IS_ACK(f)		((f) & GRE_ACK)
-
-#define GRE_VERSION_0		__cpu_to_be16(0x0000)
-#define GRE_VERSION_1		__cpu_to_be16(0x0001)
-#define GRE_PROTO_PPP		__cpu_to_be16(0x880b)
-#define GRE_PPTP_KEY_MASK	__cpu_to_be32(0xffff)
-
 struct ip_tunnel_parm {
 	char			name[IFNAMSIZ];
 	int			link;
@@ -75,8 +57,6 @@
 	IFLA_IPTUN_ENCAP_FLAGS,
 	IFLA_IPTUN_ENCAP_SPORT,
 	IFLA_IPTUN_ENCAP_DPORT,
-	IFLA_IPTUN_COLLECT_METADATA,
-	IFLA_IPTUN_FWMARK,
 	__IFLA_IPTUN_MAX,
 };
 #define IFLA_IPTUN_MAX	(__IFLA_IPTUN_MAX - 1)
@@ -85,7 +65,6 @@
 	TUNNEL_ENCAP_NONE,
 	TUNNEL_ENCAP_FOU,
 	TUNNEL_ENCAP_GUE,
-	TUNNEL_ENCAP_MPLS,
 };
 
 #define TUNNEL_ENCAP_FLAG_CSUM		(1<<0)
@@ -134,12 +113,6 @@
 	IFLA_GRE_ENCAP_SPORT,
 	IFLA_GRE_ENCAP_DPORT,
 	IFLA_GRE_COLLECT_METADATA,
-	IFLA_GRE_IGNORE_DF,
-	IFLA_GRE_FWMARK,
-	IFLA_GRE_ERSPAN_INDEX,
-	IFLA_GRE_ERSPAN_VER,
-	IFLA_GRE_ERSPAN_DIR,
-	IFLA_GRE_ERSPAN_HWID,
 	__IFLA_GRE_MAX,
 };
 
@@ -155,29 +128,8 @@
 	IFLA_VTI_OKEY,
 	IFLA_VTI_LOCAL,
 	IFLA_VTI_REMOTE,
-	IFLA_VTI_FWMARK,
 	__IFLA_VTI_MAX,
 };
 
 #define IFLA_VTI_MAX	(__IFLA_VTI_MAX - 1)
-
-#define TUNNEL_CSUM		__cpu_to_be16(0x01)
-#define TUNNEL_ROUTING		__cpu_to_be16(0x02)
-#define TUNNEL_KEY		__cpu_to_be16(0x04)
-#define TUNNEL_SEQ		__cpu_to_be16(0x08)
-#define TUNNEL_STRICT		__cpu_to_be16(0x10)
-#define TUNNEL_REC		__cpu_to_be16(0x20)
-#define TUNNEL_VERSION		__cpu_to_be16(0x40)
-#define TUNNEL_NO_KEY		__cpu_to_be16(0x80)
-#define TUNNEL_DONT_FRAGMENT    __cpu_to_be16(0x0100)
-#define TUNNEL_OAM		__cpu_to_be16(0x0200)
-#define TUNNEL_CRIT_OPT		__cpu_to_be16(0x0400)
-#define TUNNEL_GENEVE_OPT	__cpu_to_be16(0x0800)
-#define TUNNEL_VXLAN_OPT	__cpu_to_be16(0x1000)
-#define TUNNEL_NOCACHE		__cpu_to_be16(0x2000)
-#define TUNNEL_ERSPAN_OPT	__cpu_to_be16(0x4000)
-
-#define TUNNEL_OPTIONS_PRESENT \
-		(TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT | TUNNEL_ERSPAN_OPT)
-
 #endif /* _IF_TUNNEL_H_ */
diff --git a/include/uapi/linux/if_vlan.h b/include/linux/if_vlan.h
similarity index 88%
rename from include/uapi/linux/if_vlan.h
rename to include/linux/if_vlan.h
index 04bca79..24ae007 100644
--- a/include/uapi/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
 /*
  * VLAN		An implementation of 802.1Q VLAN tagging.
  *
@@ -32,11 +31,10 @@
 };
 
 enum vlan_flags {
-	VLAN_FLAG_REORDER_HDR		= 0x1,
-	VLAN_FLAG_GVRP			= 0x2,
-	VLAN_FLAG_LOOSE_BINDING		= 0x4,
-	VLAN_FLAG_MVRP			= 0x8,
-	VLAN_FLAG_BRIDGE_BINDING	= 0x10,
+	VLAN_FLAG_REORDER_HDR	= 0x1,
+	VLAN_FLAG_GVRP		= 0x2,
+	VLAN_FLAG_LOOSE_BINDING	= 0x4,
+	VLAN_FLAG_MVRP		= 0x8,
 };
 
 enum vlan_name_types {
diff --git a/include/linux/ila.h b/include/linux/ila.h
new file mode 100644
index 0000000..4f9e1de
--- /dev/null
+++ b/include/linux/ila.h
@@ -0,0 +1,37 @@
+/* ila.h - ILA Interface */
+
+#ifndef _LINUX_ILA_H
+#define _LINUX_ILA_H
+
+/* NETLINK_GENERIC related info */
+#define ILA_GENL_NAME		"ila"
+#define ILA_GENL_VERSION	0x1
+
+enum {
+	ILA_ATTR_UNSPEC,
+	ILA_ATTR_LOCATOR,			/* u64 */
+	ILA_ATTR_IDENTIFIER,			/* u64 */
+	ILA_ATTR_LOCATOR_MATCH,			/* u64 */
+	ILA_ATTR_IFINDEX,			/* s32 */
+	ILA_ATTR_DIR,				/* u32 */
+
+	__ILA_ATTR_MAX,
+};
+
+#define ILA_ATTR_MAX		(__ILA_ATTR_MAX - 1)
+
+enum {
+	ILA_CMD_UNSPEC,
+	ILA_CMD_ADD,
+	ILA_CMD_DEL,
+	ILA_CMD_GET,
+
+	__ILA_CMD_MAX,
+};
+
+#define ILA_CMD_MAX	(__ILA_CMD_MAX - 1)
+
+#define ILA_DIR_IN	(1 << 0)
+#define ILA_DIR_OUT	(1 << 1)
+
+#endif /* _LINUX_ILA_H */
diff --git a/include/uapi/linux/in.h b/include/linux/in.h
similarity index 92%
rename from include/uapi/linux/in.h
rename to include/linux/in.h
index ac079ab..194b43b 100644
--- a/include/uapi/linux/in.h
+++ b/include/linux/in.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
 /*
  * INET		An implementation of the TCP/IP protocol suite for the LINUX
  *		operating system.  INET is implemented using the  BSD Socket
@@ -118,7 +117,6 @@
 #define IP_NODEFRAG     22
 #define IP_CHECKSUM	23
 #define IP_BIND_ADDRESS_NO_PORT	24
-#define IP_RECVFRAGSIZE	25
 
 /* IP_MTU_DISCOVER values */
 #define IP_PMTUDISC_DONT		0	/* Never send DF frames */
@@ -266,14 +264,10 @@
 
 #define	IN_CLASSD(a)		((((long int) (a)) & 0xf0000000) == 0xe0000000)
 #define	IN_MULTICAST(a)		IN_CLASSD(a)
-#define	IN_MULTICAST_NET	0xe0000000
+#define IN_MULTICAST_NET	0xF0000000
 
-#define	IN_BADCLASS(a)		(((long int) (a) ) == (long int)0xffffffff)
-#define	IN_EXPERIMENTAL(a)	IN_BADCLASS((a))
-
-#define	IN_CLASSE(a)		((((long int) (a)) & 0xf0000000) == 0xf0000000)
-#define	IN_CLASSE_NET		0xffffffff
-#define	IN_CLASSE_NSHIFT	0
+#define	IN_EXPERIMENTAL(a)	((((long int) (a)) & 0xf0000000) == 0xf0000000)
+#define	IN_BADCLASS(a)		IN_EXPERIMENTAL((a))
 
 /* Address to accept any incoming messages. */
 #define	INADDR_ANY		((unsigned long int) 0x00000000)
@@ -292,11 +286,10 @@
 #define	IN_LOOPBACK(a)		((((long int) (a)) & 0xff000000) == 0x7f000000)
 
 /* Defines for Multicast INADDR */
-#define INADDR_UNSPEC_GROUP		0xe0000000U	/* 224.0.0.0   */
-#define INADDR_ALLHOSTS_GROUP		0xe0000001U	/* 224.0.0.1   */
-#define INADDR_ALLRTRS_GROUP		0xe0000002U	/* 224.0.0.2 */
-#define INADDR_ALLSNOOPERS_GROUP	0xe000006aU	/* 224.0.0.106 */
-#define INADDR_MAX_LOCAL_GROUP		0xe00000ffU	/* 224.0.0.255 */
+#define INADDR_UNSPEC_GROUP   	0xe0000000U	/* 224.0.0.0   */
+#define INADDR_ALLHOSTS_GROUP 	0xe0000001U	/* 224.0.0.1   */
+#define INADDR_ALLRTRS_GROUP    0xe0000002U	/* 224.0.0.2 */
+#define INADDR_MAX_LOCAL_GROUP  0xe00000ffU	/* 224.0.0.255 */
 #endif
 
 /* <asm/byteorder.h> contains the htonl type stuff.. */
diff --git a/include/uapi/linux/in6.h b/include/linux/in6.h
similarity index 96%
rename from include/uapi/linux/in6.h
rename to include/linux/in6.h
index d016ac9..aa5b66d 100644
--- a/include/uapi/linux/in6.h
+++ b/include/linux/in6.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
 /*
  *	Types and definitions for AF_INET6 
  *	Linux INET6 implementation 
@@ -144,7 +143,6 @@
 #define IPV6_TLV_PAD1		0
 #define IPV6_TLV_PADN		1
 #define IPV6_TLV_ROUTERALERT	5
-#define IPV6_TLV_CALIPSO	7	/* RFC 5570 */
 #define IPV6_TLV_JUMBO		194
 #define IPV6_TLV_HAO		201	/* home address option */
 
@@ -177,8 +175,6 @@
 #define IPV6_V6ONLY		26
 #define IPV6_JOIN_ANYCAST	27
 #define IPV6_LEAVE_ANYCAST	28
-#define IPV6_MULTICAST_ALL	29
-#define IPV6_ROUTER_ALERT_ISOLATE	30
 
 /* IPV6_MTU_DISCOVER values */
 #define IPV6_PMTUDISC_DONT		0
@@ -286,8 +282,6 @@
 #define IPV6_RECVORIGDSTADDR    IPV6_ORIGDSTADDR
 #define IPV6_TRANSPARENT        75
 #define IPV6_UNICAST_IF         76
-#define IPV6_RECVFRAGSIZE	77
-#define IPV6_FREEBIND		78
 
 /*
  * Multicast Routing:
diff --git a/include/uapi/linux/inet_diag.h b/include/linux/inet_diag.h
similarity index 65%
rename from include/uapi/linux/inet_diag.h
rename to include/linux/inet_diag.h
index 3dff684..016de88 100644
--- a/include/uapi/linux/inet_diag.h
+++ b/include/linux/inet_diag.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _INET_DIAG_H_
 #define _INET_DIAG_H_
 
@@ -44,23 +43,6 @@
 	struct inet_diag_sockid id;
 };
 
-/*
- * SOCK_RAW sockets require the underlied protocol to be
- * additionally specified so we can use @pad member for
- * this, but we can't rename it because userspace programs
- * still may depend on this name. Instead lets use another
- * structure definition as an alias for struct
- * @inet_diag_req_v2.
- */
-struct inet_diag_req_raw {
-	__u8	sdiag_family;
-	__u8	sdiag_protocol;
-	__u8	idiag_ext;
-	__u8	sdiag_raw_protocol;
-	__u32	idiag_states;
-	struct inet_diag_sockid id;
-};
-
 enum {
 	INET_DIAG_REQ_NONE,
 	INET_DIAG_REQ_BYTECODE,
@@ -92,8 +74,6 @@
 	INET_DIAG_BC_D_COND,
 	INET_DIAG_BC_DEV_COND,   /* u32 ifindex */
 	INET_DIAG_BC_MARK_COND,
-	INET_DIAG_BC_S_EQ,
-	INET_DIAG_BC_D_EQ,
 };
 
 struct inet_diag_hostcond {
@@ -137,36 +117,18 @@
 	INET_DIAG_TCLASS,
 	INET_DIAG_SKMEMINFO,
 	INET_DIAG_SHUTDOWN,
-
-	/*
-	 * Next extenstions cannot be requested in struct inet_diag_req_v2:
-	 * its field idiag_ext has only 8 bits.
-	 */
-
-	INET_DIAG_DCTCPINFO,	/* request as INET_DIAG_VEGASINFO */
-	INET_DIAG_PROTOCOL,	/* response attribute only */
+	INET_DIAG_DCTCPINFO,
+	INET_DIAG_PROTOCOL,  /* response attribute only */
 	INET_DIAG_SKV6ONLY,
 	INET_DIAG_LOCALS,
 	INET_DIAG_PEERS,
 	INET_DIAG_PAD,
-	INET_DIAG_MARK,		/* only with CAP_NET_ADMIN */
-	INET_DIAG_BBRINFO,	/* request as INET_DIAG_VEGASINFO */
-	INET_DIAG_CLASS_ID,	/* request as INET_DIAG_TCLASS */
-	INET_DIAG_MD5SIG,
-	INET_DIAG_ULP_INFO,
+	INET_DIAG_MARK,
 	__INET_DIAG_MAX,
 };
 
 #define INET_DIAG_MAX (__INET_DIAG_MAX - 1)
 
-enum {
-	INET_ULP_INFO_UNSPEC,
-	INET_ULP_INFO_NAME,
-	INET_ULP_INFO_TLS,
-	__INET_ULP_INFO_MAX,
-};
-#define INET_ULP_INFO_MAX (__INET_ULP_INFO_MAX - 1)
-
 /* INET_DIAG_MEM */
 
 struct inet_diag_meminfo {
@@ -195,20 +157,8 @@
 	__u32	dctcp_ab_tot;
 };
 
-/* INET_DIAG_BBRINFO */
-
-struct tcp_bbr_info {
-	/* u64 bw: max-filtered BW (app throughput) estimate in Byte per sec: */
-	__u32	bbr_bw_lo;		/* lower 32 bits of bw */
-	__u32	bbr_bw_hi;		/* upper 32 bits of bw */
-	__u32	bbr_min_rtt;		/* min-filtered RTT in uSec */
-	__u32	bbr_pacing_gain;	/* pacing gain shifted left 8 bits */
-	__u32	bbr_cwnd_gain;		/* cwnd gain shifted left 8 bits */
-};
-
 union tcp_cc_info {
 	struct tcpvegas_info	vegas;
 	struct tcp_dctcp_info	dctcp;
-	struct tcp_bbr_info	bbr;
 };
 #endif /* _INET_DIAG_H_ */
diff --git a/include/uapi/linux/ip6_tunnel.h b/include/linux/ip6_tunnel.h
similarity index 87%
rename from include/uapi/linux/ip6_tunnel.h
rename to include/linux/ip6_tunnel.h
index 0245269..48af63c 100644
--- a/include/uapi/linux/ip6_tunnel.h
+++ b/include/linux/ip6_tunnel.h
@@ -1,10 +1,7 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _IP6_TUNNEL_H
 #define _IP6_TUNNEL_H
 
 #include <linux/types.h>
-#include <linux/if.h>		/* For IFNAMSIZ. */
-#include <linux/in6.h>		/* For struct in6_addr. */
 
 #define IPV6_TLV_TNL_ENCAP_LIMIT 4
 #define IPV6_DEFAULT_TNL_ENCAP_LIMIT 4
@@ -21,8 +18,6 @@
 #define IP6_TNL_F_RCV_DSCP_COPY 0x10
 /* copy fwmark from inner packet */
 #define IP6_TNL_F_USE_ORIG_FWMARK 0x20
-/* allow remote endpoint on the local node */
-#define IP6_TNL_F_ALLOW_LOCAL_REMOTE 0x40
 
 struct ip6_tnl_parm {
 	char name[IFNAMSIZ];	/* name of tunnel device */
diff --git a/include/uapi/linux/l2tp.h b/include/linux/l2tp.h
similarity index 80%
rename from include/uapi/linux/l2tp.h
rename to include/linux/l2tp.h
index 131c3a2..5b0e36d 100644
--- a/include/uapi/linux/l2tp.h
+++ b/include/linux/l2tp.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /*
  * L2TP-over-IP socket for L2TPv3.
  *
@@ -10,8 +9,7 @@
 
 #include <linux/types.h>
 #include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/in6.h>
+#include <netinet/in.h>
 
 #define IPPROTO_L2TP		115
 
@@ -31,7 +29,7 @@
 	__u32		l2tp_conn_id;	/* Connection ID of tunnel */
 
 	/* Pad to size of `struct sockaddr'. */
-	unsigned char	__pad[__SOCK_SIZE__ -
+	unsigned char	__pad[sizeof(struct sockaddr) -
 			      sizeof(__kernel_sa_family_t) -
 			      sizeof(__be16) - sizeof(struct in_addr) -
 			      sizeof(__u32)];
@@ -60,14 +58,14 @@
 /*
  * Commands.
  * Valid TLVs of each command are:-
- * TUNNEL_CREATE	- CONN_ID, pw_type, netns, ifname, ipinfo, udpinfo, udpcsum
+ * 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, cookie, peer_cookie, l2spec
+ * SESSION_CREATE	- SESSION_ID, PW_TYPE, offset, data_seq, cookie, peer_cookie, offset, l2spec
  * SESSION_DELETE	- SESSION_ID
- * SESSION_MODIFY	- SESSION_ID
+ * SESSION_MODIFY	- SESSION_ID, data_seq
  * SESSION_GET		- SESSION_ID, (...)
  * SESSION_GETSTATS	- SESSION_ID, (stats)
  *
@@ -94,10 +92,10 @@
 	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 (not used) */
-	L2TP_ATTR_DATA_SEQ,		/* u16 (not used) */
+	L2TP_ATTR_OFFSET,		/* u16 */
+	L2TP_ATTR_DATA_SEQ,		/* u16 */
 	L2TP_ATTR_L2SPEC_TYPE,		/* u8, enum l2tp_l2spec_type */
-	L2TP_ATTR_L2SPEC_LEN,		/* u8 (not used) */
+	L2TP_ATTR_L2SPEC_LEN,		/* u8, enum l2tp_l2spec_type */
 	L2TP_ATTR_PROTO_VERSION,	/* u8 */
 	L2TP_ATTR_IFNAME,		/* string */
 	L2TP_ATTR_CONN_ID,		/* u32 */
@@ -105,10 +103,10 @@
 	L2TP_ATTR_SESSION_ID,		/* u32 */
 	L2TP_ATTR_PEER_SESSION_ID,	/* u32 */
 	L2TP_ATTR_UDP_CSUM,		/* u8 */
-	L2TP_ATTR_VLAN_ID,		/* u16 (not used) */
+	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, enum l2tp_debug_flags */
+	L2TP_ATTR_DEBUG,		/* u32 */
 	L2TP_ATTR_RECV_SEQ,		/* u8 */
 	L2TP_ATTR_SEND_SEQ,		/* u8 */
 	L2TP_ATTR_LNS_MODE,		/* u8 */
@@ -119,14 +117,13 @@
 	L2TP_ATTR_IP_DADDR,		/* u32 */
 	L2TP_ATTR_UDP_SPORT,		/* u16 */
 	L2TP_ATTR_UDP_DPORT,		/* u16 */
-	L2TP_ATTR_MTU,			/* u16 (not used) */
-	L2TP_ATTR_MRU,			/* u16 (not used) */
+	L2TP_ATTR_MTU,			/* u16 */
+	L2TP_ATTR_MRU,			/* u16 */
 	L2TP_ATTR_STATS,		/* nested */
 	L2TP_ATTR_IP6_SADDR,		/* struct in6_addr */
 	L2TP_ATTR_IP6_DADDR,		/* struct in6_addr */
-	L2TP_ATTR_UDP_ZERO_CSUM6_TX,	/* flag */
-	L2TP_ATTR_UDP_ZERO_CSUM6_RX,	/* flag */
-	L2TP_ATTR_PAD,
+	L2TP_ATTR_UDP_ZERO_CSUM6_TX,	/* u8 */
+	L2TP_ATTR_UDP_ZERO_CSUM6_RX,	/* u8 */
 	__L2TP_ATTR_MAX,
 };
 
@@ -143,7 +140,6 @@
 	L2TP_ATTR_RX_SEQ_DISCARDS,	/* u64 */
 	L2TP_ATTR_RX_OOS_PACKETS,	/* u64 */
 	L2TP_ATTR_RX_ERRORS,		/* u64 */
-	L2TP_ATTR_STATS_PAD,
 	__L2TP_ATTR_STATS_MAX,
 };
 
@@ -169,28 +165,12 @@
 	L2TP_ENCAPTYPE_IP,
 };
 
-/* For L2TP_ATTR_DATA_SEQ. Unused. */
 enum l2tp_seqmode {
 	L2TP_SEQ_NONE = 0,
 	L2TP_SEQ_IP = 1,
 	L2TP_SEQ_ALL = 2,
 };
 
-/**
- * enum l2tp_debug_flags - debug message categories for L2TP tunnels/sessions
- *
- * @L2TP_MSG_DEBUG: verbose debug (if compiled in)
- * @L2TP_MSG_CONTROL: userspace - kernel interface
- * @L2TP_MSG_SEQ: sequence numbers
- * @L2TP_MSG_DATA: data packets
- */
-enum l2tp_debug_flags {
-	L2TP_MSG_DEBUG		= (1 << 0),
-	L2TP_MSG_CONTROL	= (1 << 1),
-	L2TP_MSG_SEQ		= (1 << 2),
-	L2TP_MSG_DATA		= (1 << 3),
-};
-
 /*
  * NETLINK_GENERIC related info
  */
diff --git a/include/linux/libc-compat.h b/include/linux/libc-compat.h
new file mode 100644
index 0000000..9bed5b6
--- /dev/null
+++ b/include/linux/libc-compat.h
@@ -0,0 +1,143 @@
+/*
+ * Compatibility interface for userspace libc header coordination:
+ *
+ * Define compatibility macros that are used to control the inclusion or
+ * exclusion of UAPI structures and definitions in coordination with another
+ * userspace C library.
+ *
+ * This header is intended to solve the problem of UAPI definitions that
+ * conflict with userspace definitions. If a UAPI header has such conflicting
+ * definitions then the solution is as follows:
+ *
+ * * Synchronize the UAPI header and the libc headers so either one can be
+ *   used and such that the ABI is preserved. If this is not possible then
+ *   no simple compatibility interface exists (you need to write translating
+ *   wrappers and rename things) and you can't use this interface.
+ *
+ * Then follow this process:
+ *
+ * (a) Include libc-compat.h in the UAPI header.
+ *      e.g. #include <linux/libc-compat.h>
+ *     This include must be as early as possible.
+ *
+ * (b) In libc-compat.h add enough code to detect that the comflicting
+ *     userspace libc header has been included first.
+ *
+ * (c) If the userspace libc header has been included first define a set of
+ *     guard macros of the form __UAPI_DEF_FOO and set their values to 1, else
+ *     set their values to 0.
+ *
+ * (d) Back in the UAPI header with the conflicting definitions, guard the
+ *     definitions with:
+ *     #if __UAPI_DEF_FOO
+ *       ...
+ *     #endif
+ *
+ * This fixes the situation where the linux headers are included *after* the
+ * libc headers. To fix the problem with the inclusion in the other order the
+ * userspace libc headers must be fixed like this:
+ *
+ * * For all definitions that conflict with kernel definitions wrap those
+ *   defines in the following:
+ *   #if !__UAPI_DEF_FOO
+ *     ...
+ *   #endif
+ *
+ * This prevents the redefinition of a construct already defined by the kernel.
+ */
+#ifndef _LIBC_COMPAT_H
+#define _LIBC_COMPAT_H
+
+/* We have included glibc headers... */
+#if defined(__GLIBC__)
+
+/* Coordinate with glibc netinet/in.h header. */
+#if defined(_NETINET_IN_H)
+
+/* GLIBC headers included first so don't define anything
+ * that would already be defined. */
+#define __UAPI_DEF_IN_ADDR		0
+#define __UAPI_DEF_IN_IPPROTO		0
+#define __UAPI_DEF_IN_PKTINFO		0
+#define __UAPI_DEF_IP_MREQ		0
+#define __UAPI_DEF_SOCKADDR_IN		0
+#define __UAPI_DEF_IN_CLASS		0
+
+#define __UAPI_DEF_IN6_ADDR		0
+/* The exception is the in6_addr macros which must be defined
+ * if the glibc code didn't define them. This guard matches
+ * the guard in glibc/inet/netinet/in.h which defines the
+ * additional in6_addr macros e.g. s6_addr16, and s6_addr32. */
+#if defined(__USE_MISC) || defined (__USE_GNU)
+#define __UAPI_DEF_IN6_ADDR_ALT		0
+#else
+#define __UAPI_DEF_IN6_ADDR_ALT		1
+#endif
+#define __UAPI_DEF_SOCKADDR_IN6		0
+#define __UAPI_DEF_IPV6_MREQ		0
+#define __UAPI_DEF_IPPROTO_V6		0
+#define __UAPI_DEF_IPV6_OPTIONS		0
+#define __UAPI_DEF_IN6_PKTINFO		0
+#define __UAPI_DEF_IP6_MTUINFO		0
+
+#else
+
+/* Linux headers included first, and we must define everything
+ * we need. The expectation is that glibc will check the
+ * __UAPI_DEF_* defines and adjust appropriately. */
+#define __UAPI_DEF_IN_ADDR		1
+#define __UAPI_DEF_IN_IPPROTO		1
+#define __UAPI_DEF_IN_PKTINFO		1
+#define __UAPI_DEF_IP_MREQ		1
+#define __UAPI_DEF_SOCKADDR_IN		1
+#define __UAPI_DEF_IN_CLASS		1
+
+#define __UAPI_DEF_IN6_ADDR		1
+/* We unconditionally define the in6_addr macros and glibc must
+ * coordinate. */
+#define __UAPI_DEF_IN6_ADDR_ALT		1
+#define __UAPI_DEF_SOCKADDR_IN6		1
+#define __UAPI_DEF_IPV6_MREQ		1
+#define __UAPI_DEF_IPPROTO_V6		1
+#define __UAPI_DEF_IPV6_OPTIONS		1
+#define __UAPI_DEF_IN6_PKTINFO		1
+#define __UAPI_DEF_IP6_MTUINFO		1
+
+#endif /* _NETINET_IN_H */
+
+/* Definitions for xattr.h */
+#if defined(_SYS_XATTR_H)
+#define __UAPI_DEF_XATTR		0
+#else
+#define __UAPI_DEF_XATTR		1
+#endif
+
+/* If we did not see any headers from any supported C libraries,
+ * or we are being included in the kernel, then define everything
+ * that we need. */
+#else /* !defined(__GLIBC__) */
+
+/* Definitions for in.h */
+#define __UAPI_DEF_IN_ADDR		1
+#define __UAPI_DEF_IN_IPPROTO		1
+#define __UAPI_DEF_IN_PKTINFO		1
+#define __UAPI_DEF_IP_MREQ		1
+#define __UAPI_DEF_SOCKADDR_IN		1
+#define __UAPI_DEF_IN_CLASS		1
+
+/* Definitions for in6.h */
+#define __UAPI_DEF_IN6_ADDR		1
+#define __UAPI_DEF_IN6_ADDR_ALT		1
+#define __UAPI_DEF_SOCKADDR_IN6		1
+#define __UAPI_DEF_IPV6_MREQ		1
+#define __UAPI_DEF_IPPROTO_V6		1
+#define __UAPI_DEF_IPV6_OPTIONS		1
+#define __UAPI_DEF_IN6_PKTINFO		1
+#define __UAPI_DEF_IP6_MTUINFO		1
+
+/* Definitions for xattr.h */
+#define __UAPI_DEF_XATTR		1
+
+#endif /* __GLIBC__ */
+
+#endif /* _LIBC_COMPAT_H */
diff --git a/include/uapi/linux/lwtunnel.h b/include/linux/lwtunnel.h
similarity index 60%
rename from include/uapi/linux/lwtunnel.h
rename to include/linux/lwtunnel.h
index 3f3fe6f..1d2f4f6 100644
--- a/include/uapi/linux/lwtunnel.h
+++ b/include/linux/lwtunnel.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _LWTUNNEL_H_
 #define _LWTUNNEL_H_
 
@@ -10,9 +9,6 @@
 	LWTUNNEL_ENCAP_IP,
 	LWTUNNEL_ENCAP_ILA,
 	LWTUNNEL_ENCAP_IP6,
-	LWTUNNEL_ENCAP_SEG6,
-	LWTUNNEL_ENCAP_BPF,
-	LWTUNNEL_ENCAP_SEG6_LOCAL,
 	__LWTUNNEL_ENCAP_MAX,
 };
 
@@ -26,7 +22,6 @@
 	LWTUNNEL_IP_TTL,
 	LWTUNNEL_IP_TOS,
 	LWTUNNEL_IP_FLAGS,
-	LWTUNNEL_IP_PAD,
 	__LWTUNNEL_IP_MAX,
 };
 
@@ -40,32 +35,9 @@
 	LWTUNNEL_IP6_HOPLIMIT,
 	LWTUNNEL_IP6_TC,
 	LWTUNNEL_IP6_FLAGS,
-	LWTUNNEL_IP6_PAD,
 	__LWTUNNEL_IP6_MAX,
 };
 
 #define LWTUNNEL_IP6_MAX (__LWTUNNEL_IP6_MAX - 1)
 
-enum {
-	LWT_BPF_PROG_UNSPEC,
-	LWT_BPF_PROG_FD,
-	LWT_BPF_PROG_NAME,
-	__LWT_BPF_PROG_MAX,
-};
-
-#define LWT_BPF_PROG_MAX (__LWT_BPF_PROG_MAX - 1)
-
-enum {
-	LWT_BPF_UNSPEC,
-	LWT_BPF_IN,
-	LWT_BPF_OUT,
-	LWT_BPF_XMIT,
-	LWT_BPF_XMIT_HEADROOM,
-	__LWT_BPF_MAX,
-};
-
-#define LWT_BPF_MAX (__LWT_BPF_MAX - 1)
-
-#define LWT_BPF_MAX_HEADROOM 256
-
 #endif /* _LWTUNNEL_H_ */
diff --git a/include/uapi/linux/mpls.h b/include/linux/mpls.h
similarity index 61%
rename from include/uapi/linux/mpls.h
rename to include/linux/mpls.h
index 9effbf9..a14b54b 100644
--- a/include/uapi/linux/mpls.h
+++ b/include/linux/mpls.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _MPLS_H
 #define _MPLS_H
 
@@ -44,34 +43,4 @@
 
 #define MPLS_LABEL_FIRST_UNRESERVED	16 /* RFC3032 */
 
-/* These are embedded into IFLA_STATS_AF_SPEC:
- * [IFLA_STATS_AF_SPEC]
- * -> [AF_MPLS]
- *    -> [MPLS_STATS_xxx]
- *
- * Attributes:
- * [MPLS_STATS_LINK] = {
- *     struct mpls_link_stats
- * }
- */
-enum {
-	MPLS_STATS_UNSPEC, /* also used as 64bit pad attribute */
-	MPLS_STATS_LINK,
-	__MPLS_STATS_MAX,
-};
-
-#define MPLS_STATS_MAX (__MPLS_STATS_MAX - 1)
-
-struct mpls_link_stats {
-	__u64	rx_packets;		/* total packets received	*/
-	__u64	tx_packets;		/* total packets transmitted	*/
-	__u64	rx_bytes;		/* total bytes received		*/
-	__u64	tx_bytes;		/* total bytes transmitted	*/
-	__u64	rx_errors;		/* bad packets received		*/
-	__u64	tx_errors;		/* packet transmit problems	*/
-	__u64	rx_dropped;		/* packet dropped on receive	*/
-	__u64	tx_dropped;		/* packet dropped on transmit	*/
-	__u64	rx_noroute;		/* no route for packet dest	*/
-};
-
 #endif /* _MPLS_H */
diff --git a/include/uapi/linux/mpls_iptunnel.h b/include/linux/mpls_iptunnel.h
similarity index 85%
rename from include/uapi/linux/mpls_iptunnel.h
rename to include/linux/mpls_iptunnel.h
index 2c69b7d..4132c3c 100644
--- a/include/uapi/linux/mpls_iptunnel.h
+++ b/include/linux/mpls_iptunnel.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
 /*
  *	mpls tunnel api
  *
@@ -17,13 +16,11 @@
 /* MPLS tunnel attributes
  * [RTA_ENCAP] = {
  *     [MPLS_IPTUNNEL_DST]
- *     [MPLS_IPTUNNEL_TTL]
  * }
  */
 enum {
 	MPLS_IPTUNNEL_UNSPEC,
 	MPLS_IPTUNNEL_DST,
-	MPLS_IPTUNNEL_TTL,
 	__MPLS_IPTUNNEL_MAX,
 };
 #define MPLS_IPTUNNEL_MAX (__MPLS_IPTUNNEL_MAX - 1)
diff --git a/include/uapi/linux/neighbour.h b/include/linux/neighbour.h
similarity index 95%
rename from include/uapi/linux/neighbour.h
rename to include/linux/neighbour.h
index cd144e3..788655b 100644
--- a/include/uapi/linux/neighbour.h
+++ b/include/linux/neighbour.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_NEIGHBOUR_H
 #define __LINUX_NEIGHBOUR_H
 
@@ -27,8 +26,6 @@
 	NDA_IFINDEX,
 	NDA_MASTER,
 	NDA_LINK_NETNSID,
-	NDA_SRC_VNI,
-	NDA_PROTOCOL,  /* Originator of entry */
 	__NDA_MAX
 };
 
@@ -43,8 +40,6 @@
 #define NTF_MASTER	0x04
 #define NTF_PROXY	0x08	/* == ATF_PUBL */
 #define NTF_EXT_LEARNED	0x10
-#define NTF_OFFLOADED   0x20
-#define NTF_STICKY	0x40
 #define NTF_ROUTER	0x80
 
 /*
@@ -133,7 +128,6 @@
 	NDTPA_LOCKTIME,			/* u64, msecs */
 	NDTPA_QUEUE_LENBYTES,		/* u32 */
 	NDTPA_MCAST_REPROBES,		/* u32 */
-	NDTPA_PAD,
 	__NDTPA_MAX
 };
 #define NDTPA_MAX (__NDTPA_MAX - 1)
@@ -166,7 +160,6 @@
 	NDTA_PARMS,			/* nested TLV NDTPA_* */
 	NDTA_STATS,			/* struct ndt_stats, read-only */
 	NDTA_GC_INTERVAL,		/* u64, msecs */
-	NDTA_PAD,
 	__NDTA_MAX
 };
 #define NDTA_MAX (__NDTA_MAX - 1)
diff --git a/include/uapi/linux/net_namespace.h b/include/linux/net_namespace.h
similarity index 85%
rename from include/uapi/linux/net_namespace.h
rename to include/linux/net_namespace.h
index fa81f1e..9a92b7e 100644
--- a/include/uapi/linux/net_namespace.h
+++ b/include/linux/net_namespace.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /* Copyright (c) 2015 6WIND S.A.
  * Author: Nicolas Dichtel <nicolas.dichtel@6wind.com>
  *
@@ -16,8 +15,6 @@
 	NETNSA_NSID,
 	NETNSA_PID,
 	NETNSA_FD,
-	NETNSA_TARGET_NSID,
-	NETNSA_CURRENT_NSID,
 	__NETNSA_MAX,
 };
 
diff --git a/include/uapi/linux/netconf.h b/include/linux/netconf.h
similarity index 78%
rename from include/uapi/linux/netconf.h
rename to include/linux/netconf.h
index 229e885..7210fe4 100644
--- a/include/uapi/linux/netconf.h
+++ b/include/linux/netconf.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _LINUX_NETCONF_H_
 #define _LINUX_NETCONF_H_
 
@@ -17,12 +16,9 @@
 	NETCONFA_MC_FORWARDING,
 	NETCONFA_PROXY_NEIGH,
 	NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
-	NETCONFA_INPUT,
-	NETCONFA_BC_FORWARDING,
 	__NETCONFA_MAX
 };
 #define NETCONFA_MAX	(__NETCONFA_MAX - 1)
-#define NETCONFA_ALL	-1
 
 #define NETCONFA_IFINDEX_ALL		-1
 #define NETCONFA_IFINDEX_DEFAULT	-2
diff --git a/include/uapi/linux/netdevice.h b/include/linux/netdevice.h
similarity index 96%
rename from include/uapi/linux/netdevice.h
rename to include/linux/netdevice.h
index 86d961c..66fceb4 100644
--- a/include/uapi/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
 /*
  * INET		An implementation of the TCP/IP protocol suite for the LINUX
  *		operating system.  INET is implemented using the  BSD Socket
diff --git a/include/uapi/linux/netfilter.h b/include/linux/netfilter.h
similarity index 88%
rename from include/uapi/linux/netfilter.h
rename to include/linux/netfilter.h
index 899be98..b71b4c9 100644
--- a/include/uapi/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -1,9 +1,9 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_NETFILTER_H
 #define __LINUX_NETFILTER_H
 
 #include <linux/types.h>
 
+#include <linux/sysctl.h>
 #include <linux/in.h>
 #include <linux/in6.h>
 
@@ -13,7 +13,7 @@
 #define NF_STOLEN 2
 #define NF_QUEUE 3
 #define NF_REPEAT 4
-#define NF_STOP 5	/* Deprecated, for userspace nf_queue compatibility. */
+#define NF_STOP 5
 #define NF_MAX_VERDICT NF_STOP
 
 /* we overload the higher bits for encoding auxiliary data such as the queue
@@ -33,6 +33,10 @@
 #define NF_DROP_ERR(x) (((-x) << 16) | NF_DROP)
 
 /* only for userspace compatibility */
+/* Generic cache responses from hook functions.
+   <= 0x2000 is used for protocol-flags. */
+#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
diff --git a/include/uapi/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
similarity index 98%
rename from include/uapi/linux/netfilter/x_tables.h
rename to include/linux/netfilter/x_tables.h
index ae2fd12..4120970 100644
--- a/include/uapi/linux/netfilter/x_tables.h
+++ b/include/linux/netfilter/x_tables.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _X_TABLES_H
 #define _X_TABLES_H
 #include <linux/kernel.h>
diff --git a/include/uapi/linux/netfilter/xt_tcpudp.h b/include/linux/netfilter/xt_tcpudp.h
similarity index 94%
rename from include/uapi/linux/netfilter/xt_tcpudp.h
rename to include/linux/netfilter/xt_tcpudp.h
index 658c169..38aa7b3 100644
--- a/include/uapi/linux/netfilter/xt_tcpudp.h
+++ b/include/linux/netfilter/xt_tcpudp.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _XT_TCPUDP_H
 #define _XT_TCPUDP_H
 
diff --git a/include/uapi/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h
similarity index 67%
rename from include/uapi/linux/netfilter_ipv4.h
rename to include/linux/netfilter_ipv4.h
index 96979e3..a5f4dc7 100644
--- a/include/uapi/linux/netfilter_ipv4.h
+++ b/include/linux/netfilter_ipv4.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /* IPv4-specific defines for netfilter. 
  * (C)1998 Rusty Russell -- This code is GPL.
  */
@@ -12,6 +11,34 @@
 
 #include <limits.h> /* for INT_MIN, INT_MAX */
 
+/* IP Cache bits. */
+/* Src IP address. */
+#define NFC_IP_SRC		0x0001
+/* Dest IP address. */
+#define NFC_IP_DST		0x0002
+/* Input device. */
+#define NFC_IP_IF_IN		0x0004
+/* Output device. */
+#define NFC_IP_IF_OUT		0x0008
+/* TOS. */
+#define NFC_IP_TOS		0x0010
+/* Protocol. */
+#define NFC_IP_PROTO		0x0020
+/* IP options. */
+#define NFC_IP_OPTIONS		0x0040
+/* Frag & flags. */
+#define NFC_IP_FRAG		0x0080
+
+/* Per-protocol information: only matters if proto match. */
+/* TCP flags. */
+#define NFC_IP_TCPFLAGS		0x0100
+/* Source port. */
+#define NFC_IP_SRC_PT		0x0200
+/* Dest port. */
+#define NFC_IP_DST_PT		0x0400
+/* Something else about the proto */
+#define NFC_IP_PROTO_UNKNOWN	0x2000
+
 /* IP Hooks */
 /* After promisc drops, checksum checks. */
 #define NF_IP_PRE_ROUTING	0
@@ -27,7 +54,6 @@
 
 enum nf_ip_hook_priorities {
 	NF_IP_PRI_FIRST = INT_MIN,
-	NF_IP_PRI_RAW_BEFORE_DEFRAG = -450,
 	NF_IP_PRI_CONNTRACK_DEFRAG = -400,
 	NF_IP_PRI_RAW = -300,
 	NF_IP_PRI_SELINUX_FIRST = -225,
diff --git a/include/uapi/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h
similarity index 98%
rename from include/uapi/linux/netfilter_ipv4/ip_tables.h
rename to include/linux/netfilter_ipv4/ip_tables.h
index 409cff7..38542b4 100644
--- a/include/uapi/linux/netfilter_ipv4/ip_tables.h
+++ b/include/linux/netfilter_ipv4/ip_tables.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /*
  * 25-Jul-1998 Major changes to allow for ip chain table
  *
@@ -18,7 +17,6 @@
 
 #include <linux/types.h>
 
-#include <linux/if.h>
 #include <linux/netfilter_ipv4.h>
 
 #include <linux/netfilter/x_tables.h>
diff --git a/include/uapi/linux/netlink.h b/include/linux/netlink.h
similarity index 70%
rename from include/uapi/linux/netlink.h
rename to include/linux/netlink.h
index 2c28d32..8a7ca5c 100644
--- a/include/uapi/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_NETLINK_H
 #define __LINUX_NETLINK_H
 
@@ -28,7 +27,6 @@
 #define NETLINK_ECRYPTFS	19
 #define NETLINK_RDMA		20
 #define NETLINK_CRYPTO		21	/* Crypto layer */
-#define NETLINK_SMC		22	/* SMC monitoring */
 
 #define NETLINK_INET_DIAG	NETLINK_SOCK_DIAG
 
@@ -51,12 +49,12 @@
 
 /* Flags values */
 
-#define NLM_F_REQUEST		0x01	/* It is request message. 	*/
-#define NLM_F_MULTI		0x02	/* Multipart message, terminated by NLMSG_DONE */
-#define NLM_F_ACK		0x04	/* Reply with ack, with zero or error code */
-#define NLM_F_ECHO		0x08	/* Echo this request 		*/
-#define NLM_F_DUMP_INTR		0x10	/* Dump was inconsistent due to sequence change */
-#define NLM_F_DUMP_FILTERED	0x20	/* Dump was filtered as requested */
+#define NLM_F_REQUEST		1	/* It is request message. 	*/
+#define NLM_F_MULTI		2	/* Multipart message, terminated by NLMSG_DONE */
+#define NLM_F_ACK		4	/* Reply with ack, with zero or error code */
+#define NLM_F_ECHO		8	/* Echo this request 		*/
+#define NLM_F_DUMP_INTR		16	/* Dump was inconsistent due to sequence change */
+#define NLM_F_DUMP_FILTERED	32	/* Dump was filtered as requested */
 
 /* Modifiers to GET request */
 #define NLM_F_ROOT	0x100	/* specify tree	root	*/
@@ -70,13 +68,6 @@
 #define NLM_F_CREATE	0x400	/* Create, if it does not exist	*/
 #define NLM_F_APPEND	0x800	/* Add to end of list		*/
 
-/* Modifiers to DELETE request */
-#define NLM_F_NONREC	0x100	/* Do not delete recursively	*/
-
-/* Flags for ACK message */
-#define NLM_F_CAPPED	0x100	/* request was capped */
-#define NLM_F_ACK_TLVS	0x200	/* extended ACK TVLs were included */
-
 /*
    4.4BSD ADD		NLM_F_CREATE|NLM_F_EXCL
    4.4BSD CHANGE	NLM_F_REPLACE
@@ -109,37 +100,6 @@
 struct nlmsgerr {
 	int		error;
 	struct nlmsghdr msg;
-	/*
-	 * followed by the message contents unless NETLINK_CAP_ACK was set
-	 * or the ACK indicates success (error == 0)
-	 * message length is aligned with NLMSG_ALIGN()
-	 */
-	/*
-	 * followed by TLVs defined in enum nlmsgerr_attrs
-	 * if NETLINK_EXT_ACK was set
-	 */
-};
-
-/**
- * enum nlmsgerr_attrs - nlmsgerr attributes
- * @NLMSGERR_ATTR_UNUSED: unused
- * @NLMSGERR_ATTR_MSG: error message string (string)
- * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original
- *	 message, counting from the beginning of the header (u32)
- * @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to
- *	be used - in the success case - to identify a created
- *	object or operation or similar (binary)
- * @__NLMSGERR_ATTR_MAX: number of attributes
- * @NLMSGERR_ATTR_MAX: highest attribute number
- */
-enum nlmsgerr_attrs {
-	NLMSGERR_ATTR_UNUSED,
-	NLMSGERR_ATTR_MSG,
-	NLMSGERR_ATTR_OFFS,
-	NLMSGERR_ATTR_COOKIE,
-
-	__NLMSGERR_ATTR_MAX,
-	NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
 };
 
 #define NETLINK_ADD_MEMBERSHIP		1
@@ -152,8 +112,6 @@
 #define NETLINK_LISTEN_ALL_NSID		8
 #define NETLINK_LIST_MEMBERSHIPS	9
 #define NETLINK_CAP_ACK			10
-#define NETLINK_EXT_ACK			11
-#define NETLINK_GET_STRICT_CHK		12
 
 struct nl_pktinfo {
 	__u32	group;
@@ -227,22 +185,5 @@
 #define NLA_ALIGN(len)		(((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
 #define NLA_HDRLEN		((int) NLA_ALIGN(sizeof(struct nlattr)))
 
-/* Generic 32 bitflags attribute content sent to the kernel.
- *
- * The value is a bitmap that defines the values being set
- * The selector is a bitmask that defines which value is legit
- *
- * Examples:
- *  value = 0x0, and selector = 0x1
- *  implies we are selecting bit 1 and we want to set its value to 0.
- *
- *  value = 0x2, and selector = 0x2
- *  implies we are selecting bit 2 and we want to set its value to 1.
- *
- */
-struct nla_bitfield32 {
-	__u32 value;
-	__u32 selector;
-};
 
 #endif /* __LINUX_NETLINK_H */
diff --git a/include/uapi/linux/netlink_diag.h b/include/linux/netlink_diag.h
similarity index 70%
rename from include/uapi/linux/netlink_diag.h
rename to include/linux/netlink_diag.h
index 4cd0657..f2159d3 100644
--- a/include/uapi/linux/netlink_diag.h
+++ b/include/linux/netlink_diag.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __NETLINK_DIAG_H__
 #define __NETLINK_DIAG_H__
 
@@ -39,7 +38,6 @@
 	NETLINK_DIAG_GROUPS,
 	NETLINK_DIAG_RX_RING,
 	NETLINK_DIAG_TX_RING,
-	NETLINK_DIAG_FLAGS,
 
 	__NETLINK_DIAG_MAX,
 };
@@ -50,16 +48,6 @@
 
 #define NDIAG_SHOW_MEMINFO	0x00000001 /* show memory info of a socket */
 #define NDIAG_SHOW_GROUPS	0x00000002 /* show groups of a netlink socket */
-/* deprecated since 4.6 */
 #define NDIAG_SHOW_RING_CFG	0x00000004 /* show ring configuration */
-#define NDIAG_SHOW_FLAGS	0x00000008 /* show flags of a netlink socket */
-
-/* flags */
-#define NDIAG_FLAG_CB_RUNNING		0x00000001
-#define NDIAG_FLAG_PKTINFO		0x00000002
-#define NDIAG_FLAG_BROADCAST_ERROR	0x00000004
-#define NDIAG_FLAG_NO_ENOBUFS		0x00000008
-#define NDIAG_FLAG_LISTEN_ALL_NSID	0x00000010
-#define NDIAG_FLAG_CAP_ACK		0x00000020
 
 #endif
diff --git a/include/uapi/linux/packet_diag.h b/include/linux/packet_diag.h
similarity index 93%
rename from include/uapi/linux/packet_diag.h
rename to include/linux/packet_diag.h
index 349ddf0..d08c63f 100644
--- a/include/uapi/linux/packet_diag.h
+++ b/include/linux/packet_diag.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __PACKET_DIAG_H__
 #define __PACKET_DIAG_H__
 
@@ -65,7 +64,7 @@
 	__u32	pdmc_count;
 	__u16	pdmc_type;
 	__u16	pdmc_alen;
-	__u8	pdmc_addr[32]; /* MAX_ADDR_LEN */
+	__u8	pdmc_addr[MAX_ADDR_LEN];
 };
 
 struct packet_diag_ring {
diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h
new file mode 100644
index 0000000..a323146
--- /dev/null
+++ b/include/linux/pkt_cls.h
@@ -0,0 +1,448 @@
+#ifndef __LINUX_PKT_CLS_H
+#define __LINUX_PKT_CLS_H
+
+#include <linux/types.h>
+#include <linux/pkt_sched.h>
+
+
+/* Action attributes */
+enum {
+	TCA_ACT_UNSPEC,
+	TCA_ACT_KIND,
+	TCA_ACT_OPTIONS,
+	TCA_ACT_INDEX,
+	TCA_ACT_STATS,
+	__TCA_ACT_MAX
+};
+
+#define TCA_ACT_MAX __TCA_ACT_MAX
+#define TCA_OLD_COMPAT (TCA_ACT_MAX+1)
+#define TCA_ACT_MAX_PRIO 32
+#define TCA_ACT_BIND	1
+#define TCA_ACT_NOBIND	0
+#define TCA_ACT_UNBIND	1
+#define TCA_ACT_NOUNBIND	0
+#define TCA_ACT_REPLACE		1
+#define TCA_ACT_NOREPLACE	0
+
+#define TC_ACT_UNSPEC	(-1)
+#define TC_ACT_OK		0
+#define TC_ACT_RECLASSIFY	1
+#define TC_ACT_SHOT		2
+#define TC_ACT_PIPE		3
+#define TC_ACT_STOLEN		4
+#define TC_ACT_QUEUED		5
+#define TC_ACT_REPEAT		6
+#define TC_ACT_REDIRECT		7
+#define TC_ACT_JUMP		0x10000000
+
+/* Action type identifiers*/
+enum {
+	TCA_ID_UNSPEC=0,
+	TCA_ID_POLICE=1,
+	/* other actions go here */
+	__TCA_ID_MAX=255
+};
+
+#define TCA_ID_MAX __TCA_ID_MAX
+
+struct tc_police {
+	__u32			index;
+	int			action;
+#define TC_POLICE_UNSPEC	TC_ACT_UNSPEC
+#define TC_POLICE_OK		TC_ACT_OK
+#define TC_POLICE_RECLASSIFY	TC_ACT_RECLASSIFY
+#define TC_POLICE_SHOT		TC_ACT_SHOT
+#define TC_POLICE_PIPE		TC_ACT_PIPE
+
+	__u32			limit;
+	__u32			burst;
+	__u32			mtu;
+	struct tc_ratespec	rate;
+	struct tc_ratespec	peakrate;
+	int 			refcnt;
+	int 			bindcnt;
+	__u32			capab;
+};
+
+struct tcf_t {
+	__u64   install;
+	__u64   lastuse;
+	__u64   expires;
+};
+
+struct tc_cnt {
+	int                   refcnt; 
+	int                   bindcnt;
+};
+
+#define tc_gen \
+	__u32                 index; \
+	__u32                 capab; \
+	int                   action; \
+	int                   refcnt; \
+	int                   bindcnt
+
+enum {
+	TCA_POLICE_UNSPEC,
+	TCA_POLICE_TBF,
+	TCA_POLICE_RATE,
+	TCA_POLICE_PEAKRATE,
+	TCA_POLICE_AVRATE,
+	TCA_POLICE_RESULT,
+	__TCA_POLICE_MAX
+#define TCA_POLICE_RESULT TCA_POLICE_RESULT
+};
+
+#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1)
+
+/* U32 filters */
+
+#define TC_U32_HTID(h) ((h)&0xFFF00000)
+#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20)
+#define TC_U32_HASH(h) (((h)>>12)&0xFF)
+#define TC_U32_NODE(h) ((h)&0xFFF)
+#define TC_U32_KEY(h) ((h)&0xFFFFF)
+#define TC_U32_UNSPEC	0
+#define TC_U32_ROOT	(0xFFF00000)
+
+enum {
+	TCA_U32_UNSPEC,
+	TCA_U32_CLASSID,
+	TCA_U32_HASH,
+	TCA_U32_LINK,
+	TCA_U32_DIVISOR,
+	TCA_U32_SEL,
+	TCA_U32_POLICE,
+	TCA_U32_ACT,   
+	TCA_U32_INDEV,
+	TCA_U32_PCNT,
+	TCA_U32_MARK,
+	__TCA_U32_MAX
+};
+
+#define TCA_U32_MAX (__TCA_U32_MAX - 1)
+
+struct tc_u32_key {
+	__be32		mask;
+	__be32		val;
+	int		off;
+	int		offmask;
+};
+
+struct tc_u32_sel {
+	unsigned char		flags;
+	unsigned char		offshift;
+	unsigned char		nkeys;
+
+	__be16			offmask;
+	__u16			off;
+	short			offoff;
+
+	short			hoff;
+	__be32			hmask;
+	struct tc_u32_key	keys[0];
+};
+
+struct tc_u32_mark {
+	__u32		val;
+	__u32		mask;
+	__u32		success;
+};
+
+struct tc_u32_pcnt {
+	__u64 rcnt;
+	__u64 rhit;
+	__u64 kcnts[0];
+};
+
+/* Flags */
+
+#define TC_U32_TERMINAL		1
+#define TC_U32_OFFSET		2
+#define TC_U32_VAROFFSET	4
+#define TC_U32_EAT		8
+
+#define TC_U32_MAXDEPTH 8
+
+
+/* RSVP filter */
+
+enum {
+	TCA_RSVP_UNSPEC,
+	TCA_RSVP_CLASSID,
+	TCA_RSVP_DST,
+	TCA_RSVP_SRC,
+	TCA_RSVP_PINFO,
+	TCA_RSVP_POLICE,
+	TCA_RSVP_ACT,
+	__TCA_RSVP_MAX
+};
+
+#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 )
+
+struct tc_rsvp_gpi {
+	__u32	key;
+	__u32	mask;
+	int	offset;
+};
+
+struct tc_rsvp_pinfo {
+	struct tc_rsvp_gpi dpi;
+	struct tc_rsvp_gpi spi;
+	__u8	protocol;
+	__u8	tunnelid;
+	__u8	tunnelhdr;
+	__u8	pad;
+};
+
+/* ROUTE filter */
+
+enum {
+	TCA_ROUTE4_UNSPEC,
+	TCA_ROUTE4_CLASSID,
+	TCA_ROUTE4_TO,
+	TCA_ROUTE4_FROM,
+	TCA_ROUTE4_IIF,
+	TCA_ROUTE4_POLICE,
+	TCA_ROUTE4_ACT,
+	__TCA_ROUTE4_MAX
+};
+
+#define TCA_ROUTE4_MAX (__TCA_ROUTE4_MAX - 1)
+
+
+/* FW filter */
+
+enum {
+	TCA_FW_UNSPEC,
+	TCA_FW_CLASSID,
+	TCA_FW_POLICE,
+	TCA_FW_INDEV, /*  used by CONFIG_NET_CLS_IND */
+	TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */
+	TCA_FW_MASK,
+	__TCA_FW_MAX
+};
+
+#define TCA_FW_MAX (__TCA_FW_MAX - 1)
+
+/* TC index filter */
+
+enum {
+	TCA_TCINDEX_UNSPEC,
+	TCA_TCINDEX_HASH,
+	TCA_TCINDEX_MASK,
+	TCA_TCINDEX_SHIFT,
+	TCA_TCINDEX_FALL_THROUGH,
+	TCA_TCINDEX_CLASSID,
+	TCA_TCINDEX_POLICE,
+	TCA_TCINDEX_ACT,
+	__TCA_TCINDEX_MAX
+};
+
+#define TCA_TCINDEX_MAX     (__TCA_TCINDEX_MAX - 1)
+
+/* Flow filter */
+
+enum {
+	FLOW_KEY_SRC,
+	FLOW_KEY_DST,
+	FLOW_KEY_PROTO,
+	FLOW_KEY_PROTO_SRC,
+	FLOW_KEY_PROTO_DST,
+	FLOW_KEY_IIF,
+	FLOW_KEY_PRIORITY,
+	FLOW_KEY_MARK,
+	FLOW_KEY_NFCT,
+	FLOW_KEY_NFCT_SRC,
+	FLOW_KEY_NFCT_DST,
+	FLOW_KEY_NFCT_PROTO_SRC,
+	FLOW_KEY_NFCT_PROTO_DST,
+	FLOW_KEY_RTCLASSID,
+	FLOW_KEY_SKUID,
+	FLOW_KEY_SKGID,
+	FLOW_KEY_VLAN_TAG,
+	FLOW_KEY_RXHASH,
+	__FLOW_KEY_MAX,
+};
+
+#define FLOW_KEY_MAX	(__FLOW_KEY_MAX - 1)
+
+enum {
+	FLOW_MODE_MAP,
+	FLOW_MODE_HASH,
+};
+
+enum {
+	TCA_FLOW_UNSPEC,
+	TCA_FLOW_KEYS,
+	TCA_FLOW_MODE,
+	TCA_FLOW_BASECLASS,
+	TCA_FLOW_RSHIFT,
+	TCA_FLOW_ADDEND,
+	TCA_FLOW_MASK,
+	TCA_FLOW_XOR,
+	TCA_FLOW_DIVISOR,
+	TCA_FLOW_ACT,
+	TCA_FLOW_POLICE,
+	TCA_FLOW_EMATCHES,
+	TCA_FLOW_PERTURB,
+	__TCA_FLOW_MAX
+};
+
+#define TCA_FLOW_MAX	(__TCA_FLOW_MAX - 1)
+
+/* Basic filter */
+
+enum {
+	TCA_BASIC_UNSPEC,
+	TCA_BASIC_CLASSID,
+	TCA_BASIC_EMATCHES,
+	TCA_BASIC_ACT,
+	TCA_BASIC_POLICE,
+	__TCA_BASIC_MAX
+};
+
+#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1)
+
+
+/* Cgroup classifier */
+
+enum {
+	TCA_CGROUP_UNSPEC,
+	TCA_CGROUP_ACT,
+	TCA_CGROUP_POLICE,
+	TCA_CGROUP_EMATCHES,
+	__TCA_CGROUP_MAX,
+};
+
+#define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1)
+
+/* BPF classifier */
+
+#define TCA_BPF_FLAG_ACT_DIRECT		(1 << 0)
+
+enum {
+	TCA_BPF_UNSPEC,
+	TCA_BPF_ACT,
+	TCA_BPF_POLICE,
+	TCA_BPF_CLASSID,
+	TCA_BPF_OPS_LEN,
+	TCA_BPF_OPS,
+	TCA_BPF_FD,
+	TCA_BPF_NAME,
+	TCA_BPF_FLAGS,
+	__TCA_BPF_MAX,
+};
+
+#define TCA_BPF_MAX (__TCA_BPF_MAX - 1)
+
+/* Flower classifier */
+
+enum {
+	TCA_FLOWER_UNSPEC,
+	TCA_FLOWER_CLASSID,
+	TCA_FLOWER_INDEV,
+	TCA_FLOWER_ACT,
+	TCA_FLOWER_KEY_ETH_DST,		/* ETH_ALEN */
+	TCA_FLOWER_KEY_ETH_DST_MASK,	/* ETH_ALEN */
+	TCA_FLOWER_KEY_ETH_SRC,		/* ETH_ALEN */
+	TCA_FLOWER_KEY_ETH_SRC_MASK,	/* ETH_ALEN */
+	TCA_FLOWER_KEY_ETH_TYPE,	/* be16 */
+	TCA_FLOWER_KEY_IP_PROTO,	/* u8 */
+	TCA_FLOWER_KEY_IPV4_SRC,	/* be32 */
+	TCA_FLOWER_KEY_IPV4_SRC_MASK,	/* be32 */
+	TCA_FLOWER_KEY_IPV4_DST,	/* be32 */
+	TCA_FLOWER_KEY_IPV4_DST_MASK,	/* be32 */
+	TCA_FLOWER_KEY_IPV6_SRC,	/* struct in6_addr */
+	TCA_FLOWER_KEY_IPV6_SRC_MASK,	/* struct in6_addr */
+	TCA_FLOWER_KEY_IPV6_DST,	/* struct in6_addr */
+	TCA_FLOWER_KEY_IPV6_DST_MASK,	/* struct in6_addr */
+	TCA_FLOWER_KEY_TCP_SRC,		/* be16 */
+	TCA_FLOWER_KEY_TCP_DST,		/* be16 */
+	TCA_FLOWER_KEY_UDP_SRC,		/* be16 */
+	TCA_FLOWER_KEY_UDP_DST,		/* be16 */
+	__TCA_FLOWER_MAX,
+};
+
+#define TCA_FLOWER_MAX (__TCA_FLOWER_MAX - 1)
+
+/* Extended Matches */
+
+struct tcf_ematch_tree_hdr {
+	__u16		nmatches;
+	__u16		progid;
+};
+
+enum {
+	TCA_EMATCH_TREE_UNSPEC,
+	TCA_EMATCH_TREE_HDR,
+	TCA_EMATCH_TREE_LIST,
+	__TCA_EMATCH_TREE_MAX
+};
+#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1)
+
+struct tcf_ematch_hdr {
+	__u16		matchid;
+	__u16		kind;
+	__u16		flags;
+	__u16		pad; /* currently unused */
+};
+
+/*  0                   1
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 
+ * +-----------------------+-+-+---+
+ * |         Unused        |S|I| R |
+ * +-----------------------+-+-+---+
+ *
+ * R(2) ::= relation to next ematch
+ *          where: 0 0 END (last ematch)
+ *                 0 1 AND
+ *                 1 0 OR
+ *                 1 1 Unused (invalid)
+ * I(1) ::= invert result
+ * S(1) ::= simple payload
+ */
+#define TCF_EM_REL_END	0
+#define TCF_EM_REL_AND	(1<<0)
+#define TCF_EM_REL_OR	(1<<1)
+#define TCF_EM_INVERT	(1<<2)
+#define TCF_EM_SIMPLE	(1<<3)
+
+#define TCF_EM_REL_MASK	3
+#define TCF_EM_REL_VALID(v) (((v) & TCF_EM_REL_MASK) != TCF_EM_REL_MASK)
+
+enum {
+	TCF_LAYER_LINK,
+	TCF_LAYER_NETWORK,
+	TCF_LAYER_TRANSPORT,
+	__TCF_LAYER_MAX
+};
+#define TCF_LAYER_MAX (__TCF_LAYER_MAX - 1)
+
+/* Ematch type assignments
+ *   1..32767		Reserved for ematches inside kernel tree
+ *   32768..65535	Free to use, not reliable
+ */
+#define	TCF_EM_CONTAINER	0
+#define	TCF_EM_CMP		1
+#define	TCF_EM_NBYTE		2
+#define	TCF_EM_U32		3
+#define	TCF_EM_META		4
+#define	TCF_EM_TEXT		5
+#define	TCF_EM_VLAN		6
+#define	TCF_EM_CANID		7
+#define	TCF_EM_IPSET		8
+#define	TCF_EM_MAX		8
+
+enum {
+	TCF_EM_PROG_TC
+};
+
+enum {
+	TCF_EM_OPND_EQ,
+	TCF_EM_OPND_GT,
+	TCF_EM_OPND_LT
+};
+
+#endif
diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
new file mode 100644
index 0000000..8cb18b4
--- /dev/null
+++ b/include/linux/pkt_sched.h
@@ -0,0 +1,857 @@
+#ifndef __LINUX_PKT_SCHED_H
+#define __LINUX_PKT_SCHED_H
+
+#include <linux/types.h>
+
+/* Logical priority bands not depending on specific packet scheduler.
+   Every scheduler will map them to real traffic classes, if it has
+   no more precise mechanism to classify packets.
+
+   These numbers have no special meaning, though their coincidence
+   with obsolete IPv6 values is not occasional :-). New IPv6 drafts
+   preferred full anarchy inspired by diffserv group.
+
+   Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy
+   class, actually, as rule it will be handled with more care than
+   filler or even bulk.
+ */
+
+#define TC_PRIO_BESTEFFORT		0
+#define TC_PRIO_FILLER			1
+#define TC_PRIO_BULK			2
+#define TC_PRIO_INTERACTIVE_BULK	4
+#define TC_PRIO_INTERACTIVE		6
+#define TC_PRIO_CONTROL			7
+
+#define TC_PRIO_MAX			15
+
+/* Generic queue statistics, available for all the elements.
+   Particular schedulers may have also their private records.
+ */
+
+struct tc_stats {
+	__u64	bytes;			/* Number of 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
+					 * flow goes out of allocated bandwidth */
+	__u32	bps;			/* Current flow byte rate */
+	__u32	pps;			/* Current flow packet rate */
+	__u32	qlen;
+	__u32	backlog;
+};
+
+struct tc_estimator {
+	signed char	interval;
+	unsigned char	ewma_log;
+};
+
+/* "Handles"
+   ---------
+
+    All the traffic control objects have 32bit identifiers, or "handles".
+
+    They can be considered as opaque numbers from user API viewpoint,
+    but actually they always consist of two fields: major and
+    minor numbers, which are interpreted by kernel specially,
+    that may be used by applications, though not recommended.
+
+    F.e. qdisc handles always have minor number equal to zero,
+    classes (or flows) have major equal to parent qdisc major, and
+    minor uniquely identifying class inside qdisc.
+
+    Macros to manipulate handles:
+ */
+
+#define TC_H_MAJ_MASK (0xFFFF0000U)
+#define TC_H_MIN_MASK (0x0000FFFFU)
+#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK)
+#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK)
+#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK))
+
+#define TC_H_UNSPEC	(0U)
+#define TC_H_ROOT	(0xFFFFFFFFU)
+#define TC_H_INGRESS    (0xFFFFFFF1U)
+#define TC_H_CLSACT	TC_H_INGRESS
+
+#define TC_H_MIN_INGRESS	0xFFF2U
+#define TC_H_MIN_EGRESS		0xFFF3U
+
+/* Need to corrospond to iproute2 tc/tc_core.h "enum link_layer" */
+enum tc_link_layer {
+	TC_LINKLAYER_UNAWARE, /* Indicate unaware old iproute2 util */
+	TC_LINKLAYER_ETHERNET,
+	TC_LINKLAYER_ATM,
+};
+#define TC_LINKLAYER_MASK 0x0F /* limit use to lower 4 bits */
+
+struct tc_ratespec {
+	unsigned char	cell_log;
+	__u8		linklayer; /* lower 4 bits */
+	unsigned short	overhead;
+	short		cell_align;
+	unsigned short	mpu;
+	__u32		rate;
+};
+
+#define TC_RTAB_SIZE	1024
+
+struct tc_sizespec {
+	unsigned char	cell_log;
+	unsigned char	size_log;
+	short		cell_align;
+	int		overhead;
+	unsigned int	linklayer;
+	unsigned int	mpu;
+	unsigned int	mtu;
+	unsigned int	tsize;
+};
+
+enum {
+	TCA_STAB_UNSPEC,
+	TCA_STAB_BASE,
+	TCA_STAB_DATA,
+	__TCA_STAB_MAX
+};
+
+#define TCA_STAB_MAX (__TCA_STAB_MAX - 1)
+
+/* FIFO section */
+
+struct tc_fifo_qopt {
+	__u32	limit;	/* Queue length: bytes for bfifo, packets for pfifo */
+};
+
+/* PRIO section */
+
+#define TCQ_PRIO_BANDS	16
+#define TCQ_MIN_PRIO_BANDS 2
+
+struct tc_prio_qopt {
+	int	bands;			/* Number of bands */
+	__u8	priomap[TC_PRIO_MAX+1];	/* Map: logical priority -> PRIO band */
+};
+
+/* MULTIQ section */
+
+struct tc_multiq_qopt {
+	__u16	bands;			/* Number of bands */
+	__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 {
+	struct tc_ratespec rate;
+	struct tc_ratespec peakrate;
+	__u32		limit;
+	__u32		buffer;
+	__u32		mtu;
+};
+
+enum {
+	TCA_TBF_UNSPEC,
+	TCA_TBF_PARMS,
+	TCA_TBF_RTAB,
+	TCA_TBF_PTAB,
+	TCA_TBF_RATE64,
+	TCA_TBF_PRATE64,
+	TCA_TBF_BURST,
+	TCA_TBF_PBURST,
+	__TCA_TBF_MAX,
+};
+
+#define TCA_TBF_MAX (__TCA_TBF_MAX - 1)
+
+
+/* TEQL section */
+
+/* TEQL does not require any parameters */
+
+/* SFQ section */
+
+struct tc_sfq_qopt {
+	unsigned	quantum;	/* Bytes per round allocated to flow */
+	int		perturb_period;	/* Period of hash perturbation */
+	__u32		limit;		/* Maximal packets in queue */
+	unsigned	divisor;	/* Hash divisor  */
+	unsigned	flows;		/* Maximal number of flows  */
+};
+
+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;
+};
+
+/* RED section */
+
+enum {
+	TCA_RED_UNSPEC,
+	TCA_RED_PARMS,
+	TCA_RED_STAB,
+	TCA_RED_MAX_P,
+	__TCA_RED_MAX,
+};
+
+#define TCA_RED_MAX (__TCA_RED_MAX - 1)
+
+struct tc_red_qopt {
+	__u32		limit;		/* HARD maximal queue length (bytes)	*/
+	__u32		qth_min;	/* Min average length threshold (bytes) */
+	__u32		qth_max;	/* Max average length threshold (bytes) */
+	unsigned char   Wlog;		/* log(W)		*/
+	unsigned char   Plog;		/* log(P_max/(qth_max-qth_min))	*/
+	unsigned char   Scell_log;	/* cell size for idle damping */
+	unsigned char	flags;
+#define TC_RED_ECN		1
+#define TC_RED_HARDDROP		2
+#define TC_RED_ADAPTATIVE	4
+};
+
+struct tc_red_xstats {
+	__u32           early;          /* Early drops */
+	__u32           pdrop;          /* Drops due to queue limits */
+	__u32           other;          /* Drops due to drop() calls */
+	__u32           marked;         /* Marked packets */
+};
+
+/* GRED section */
+
+#define MAX_DPs 16
+
+enum {
+       TCA_GRED_UNSPEC,
+       TCA_GRED_PARMS,
+       TCA_GRED_STAB,
+       TCA_GRED_DPS,
+       TCA_GRED_MAX_P,
+       TCA_GRED_LIMIT,
+       __TCA_GRED_MAX,
+};
+
+#define TCA_GRED_MAX (__TCA_GRED_MAX - 1)
+
+struct tc_gred_qopt {
+	__u32		limit;        /* HARD maximal queue length (bytes)    */
+	__u32		qth_min;      /* Min average length threshold (bytes) */
+	__u32		qth_max;      /* Max average length threshold (bytes) */
+	__u32		DP;           /* up to 2^32 DPs */
+	__u32		backlog;
+	__u32		qave;
+	__u32		forced;
+	__u32		early;
+	__u32		other;
+	__u32		pdrop;
+	__u8		Wlog;         /* log(W)               */
+	__u8		Plog;         /* log(P_max/(qth_max-qth_min)) */
+	__u8		Scell_log;    /* cell size for idle damping */
+	__u8		prio;         /* prio of this VQ */
+	__u32		packets;
+	__u32		bytesin;
+};
+
+/* gred setup */
+struct tc_gred_sopt {
+	__u32		DPs;
+	__u32		def_DP;
+	__u8		grio;
+	__u8		flags;
+	__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
+#define TC_HTB_PROTOVER		3 /* the same as HTB and TC's major */
+
+struct tc_htb_opt {
+	struct tc_ratespec 	rate;
+	struct tc_ratespec 	ceil;
+	__u32	buffer;
+	__u32	cbuffer;
+	__u32	quantum;
+	__u32	level;		/* out only */
+	__u32	prio;
+};
+struct tc_htb_glob {
+	__u32 version;		/* to match HTB/TC */
+    	__u32 rate2quantum;	/* bps->quantum divisor */
+    	__u32 defcls;		/* default class number */
+	__u32 debug;		/* debug flags */
+
+	/* stats */
+	__u32 direct_pkts; /* count of non shaped packets */
+};
+enum {
+	TCA_HTB_UNSPEC,
+	TCA_HTB_PARMS,
+	TCA_HTB_INIT,
+	TCA_HTB_CTAB,
+	TCA_HTB_RTAB,
+	TCA_HTB_DIRECT_QLEN,
+	TCA_HTB_RATE64,
+	TCA_HTB_CEIL64,
+	__TCA_HTB_MAX,
+};
+
+#define TCA_HTB_MAX (__TCA_HTB_MAX - 1)
+
+struct tc_htb_xstats {
+	__u32 lends;
+	__u32 borrows;
+	__u32 giants;	/* too big packets (rate will not be accurate) */
+	__u32 tokens;
+	__u32 ctokens;
+};
+
+/* HFSC section */
+
+struct tc_hfsc_qopt {
+	__u16	defcls;		/* default class */
+};
+
+struct tc_service_curve {
+	__u32	m1;		/* slope of the first segment in bps */
+	__u32	d;		/* x-projection of the first segment in us */
+	__u32	m2;		/* slope of the second segment in bps */
+};
+
+struct tc_hfsc_stats {
+	__u64	work;		/* total work done */
+	__u64	rtwork;		/* work done by real-time criteria */
+	__u32	period;		/* current period */
+	__u32	level;		/* class level in hierarchy */
+};
+
+enum {
+	TCA_HFSC_UNSPEC,
+	TCA_HFSC_RSC,
+	TCA_HFSC_FSC,
+	TCA_HFSC_USC,
+	__TCA_HFSC_MAX,
+};
+
+#define TCA_HFSC_MAX (__TCA_HFSC_MAX - 1)
+
+
+/* CBQ section */
+
+#define TC_CBQ_MAXPRIO		8
+#define TC_CBQ_MAXLEVEL		8
+#define TC_CBQ_DEF_EWMA		5
+
+struct tc_cbq_lssopt {
+	unsigned char	change;
+	unsigned char	flags;
+#define TCF_CBQ_LSS_BOUNDED	1
+#define TCF_CBQ_LSS_ISOLATED	2
+	unsigned char  	ewma_log;
+	unsigned char  	level;
+#define TCF_CBQ_LSS_FLAGS	1
+#define TCF_CBQ_LSS_EWMA	2
+#define TCF_CBQ_LSS_MAXIDLE	4
+#define TCF_CBQ_LSS_MINIDLE	8
+#define TCF_CBQ_LSS_OFFTIME	0x10
+#define TCF_CBQ_LSS_AVPKT	0x20
+	__u32		maxidle;
+	__u32		minidle;
+	__u32		offtime;
+	__u32		avpkt;
+};
+
+struct tc_cbq_wrropt {
+	unsigned char	flags;
+	unsigned char	priority;
+	unsigned char	cpriority;
+	unsigned char	__reserved;
+	__u32		allot;
+	__u32		weight;
+};
+
+struct tc_cbq_ovl {
+	unsigned char	strategy;
+#define	TC_CBQ_OVL_CLASSIC	0
+#define	TC_CBQ_OVL_DELAY	1
+#define	TC_CBQ_OVL_LOWPRIO	2
+#define	TC_CBQ_OVL_DROP		3
+#define	TC_CBQ_OVL_RCLASSIC	4
+	unsigned char	priority2;
+	__u16		pad;
+	__u32		penalty;
+};
+
+struct tc_cbq_police {
+	unsigned char	police;
+	unsigned char	__res1;
+	unsigned short	__res2;
+};
+
+struct tc_cbq_fopt {
+	__u32		split;
+	__u32		defmap;
+	__u32		defchange;
+};
+
+struct tc_cbq_xstats {
+	__u32		borrows;
+	__u32		overactions;
+	__s32		avgidle;
+	__s32		undertime;
+};
+
+enum {
+	TCA_CBQ_UNSPEC,
+	TCA_CBQ_LSSOPT,
+	TCA_CBQ_WRROPT,
+	TCA_CBQ_FOPT,
+	TCA_CBQ_OVL_STRATEGY,
+	TCA_CBQ_RATE,
+	TCA_CBQ_RTAB,
+	TCA_CBQ_POLICE,
+	__TCA_CBQ_MAX,
+};
+
+#define TCA_CBQ_MAX	(__TCA_CBQ_MAX - 1)
+
+/* dsmark section */
+
+enum {
+	TCA_DSMARK_UNSPEC,
+	TCA_DSMARK_INDICES,
+	TCA_DSMARK_DEFAULT_INDEX,
+	TCA_DSMARK_SET_TC_INDEX,
+	TCA_DSMARK_MASK,
+	TCA_DSMARK_VALUE,
+	__TCA_DSMARK_MAX,
+};
+
+#define TCA_DSMARK_MAX (__TCA_DSMARK_MAX - 1)
+
+/* ATM  section */
+
+enum {
+	TCA_ATM_UNSPEC,
+	TCA_ATM_FD,		/* file/socket descriptor */
+	TCA_ATM_PTR,		/* pointer to descriptor - later */
+	TCA_ATM_HDR,		/* LL header */
+	TCA_ATM_EXCESS,		/* excess traffic class (0 for CLP)  */
+	TCA_ATM_ADDR,		/* PVC address (for output only) */
+	TCA_ATM_STATE,		/* VC state (ATM_VS_*; for output only) */
+	__TCA_ATM_MAX,
+};
+
+#define TCA_ATM_MAX	(__TCA_ATM_MAX - 1)
+
+/* Network emulator */
+
+enum {
+	TCA_NETEM_UNSPEC,
+	TCA_NETEM_CORR,
+	TCA_NETEM_DELAY_DIST,
+	TCA_NETEM_REORDER,
+	TCA_NETEM_CORRUPT,
+	TCA_NETEM_LOSS,
+	TCA_NETEM_RATE,
+	TCA_NETEM_ECN,
+	TCA_NETEM_RATE64,
+	__TCA_NETEM_MAX,
+};
+
+#define TCA_NETEM_MAX (__TCA_NETEM_MAX - 1)
+
+struct tc_netem_qopt {
+	__u32	latency;	/* added delay (us) */
+	__u32   limit;		/* fifo limit (packets) */
+	__u32	loss;		/* random packet loss (0=none ~0=100%) */
+	__u32	gap;		/* re-ordering gap (0 for none) */
+	__u32   duplicate;	/* random packet dup  (0=none ~0=100%) */
+	__u32	jitter;		/* random jitter in latency (us) */
+};
+
+struct tc_netem_corr {
+	__u32	delay_corr;	/* delay correlation */
+	__u32	loss_corr;	/* packet loss correlation */
+	__u32	dup_corr;	/* duplicate correlation  */
+};
+
+struct tc_netem_reorder {
+	__u32	probability;
+	__u32	correlation;
+};
+
+struct tc_netem_corrupt {
+	__u32	probability;
+	__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 */
+
+enum {
+	TCA_DRR_UNSPEC,
+	TCA_DRR_QUANTUM,
+	__TCA_DRR_MAX
+};
+
+#define TCA_DRR_MAX	(__TCA_DRR_MAX - 1)
+
+struct tc_drr_stats {
+	__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;
+};
+
+/* CODEL */
+
+enum {
+	TCA_CODEL_UNSPEC,
+	TCA_CODEL_TARGET,
+	TCA_CODEL_LIMIT,
+	TCA_CODEL_INTERVAL,
+	TCA_CODEL_ECN,
+	TCA_CODEL_CE_THRESHOLD,
+	__TCA_CODEL_MAX
+};
+
+#define TCA_CODEL_MAX	(__TCA_CODEL_MAX - 1)
+
+struct tc_codel_xstats {
+	__u32	maxpacket; /* largest packet we've seen so far */
+	__u32	count;	   /* how many drops we've done since the last time we
+			    * entered dropping state
+			    */
+	__u32	lastcount; /* count at entry to dropping state */
+	__u32	ldelay;    /* in-queue delay seen by most recently dequeued packet */
+	__s32	drop_next; /* time to drop next packet */
+	__u32	drop_overlimit; /* number of time max qdisc packet limit was hit */
+	__u32	ecn_mark;  /* number of packets we ECN marked instead of dropped */
+	__u32	dropping;  /* are we in dropping state ? */
+	__u32	ce_mark;   /* number of CE marked packets because of ce_threshold */
+};
+
+/* FQ_CODEL */
+
+enum {
+	TCA_FQ_CODEL_UNSPEC,
+	TCA_FQ_CODEL_TARGET,
+	TCA_FQ_CODEL_LIMIT,
+	TCA_FQ_CODEL_INTERVAL,
+	TCA_FQ_CODEL_ECN,
+	TCA_FQ_CODEL_FLOWS,
+	TCA_FQ_CODEL_QUANTUM,
+	TCA_FQ_CODEL_CE_THRESHOLD,
+	__TCA_FQ_CODEL_MAX
+};
+
+#define TCA_FQ_CODEL_MAX	(__TCA_FQ_CODEL_MAX - 1)
+
+enum {
+	TCA_FQ_CODEL_XSTATS_QDISC,
+	TCA_FQ_CODEL_XSTATS_CLASS,
+};
+
+struct tc_fq_codel_qd_stats {
+	__u32	maxpacket;	/* largest packet we've seen so far */
+	__u32	drop_overlimit; /* number of time max qdisc
+				 * packet limit was hit
+				 */
+	__u32	ecn_mark;	/* number of packets we ECN marked
+				 * instead of being dropped
+				 */
+	__u32	new_flow_count; /* number of time packets
+				 * created a 'new flow'
+				 */
+	__u32	new_flows_len;	/* count of flows in new list */
+	__u32	old_flows_len;	/* count of flows in old list */
+	__u32	ce_mark;	/* packets above ce_threshold */
+};
+
+struct tc_fq_codel_cl_stats {
+	__s32	deficit;
+	__u32	ldelay;		/* in-queue delay seen by most recently
+				 * dequeued packet
+				 */
+	__u32	count;
+	__u32	lastcount;
+	__u32	dropping;
+	__s32	drop_next;
+};
+
+struct tc_fq_codel_xstats {
+	__u32	type;
+	union {
+		struct tc_fq_codel_qd_stats qdisc_stats;
+		struct tc_fq_codel_cl_stats class_stats;
+	};
+};
+
+/* FQ */
+
+enum {
+	TCA_FQ_UNSPEC,
+
+	TCA_FQ_PLIMIT,		/* limit of total number of packets in queue */
+
+	TCA_FQ_FLOW_PLIMIT,	/* limit of packets per flow */
+
+	TCA_FQ_QUANTUM,		/* RR quantum */
+
+	TCA_FQ_INITIAL_QUANTUM,		/* RR quantum for new flow */
+
+	TCA_FQ_RATE_ENABLE,	/* enable/disable rate limiting */
+
+	TCA_FQ_FLOW_DEFAULT_RATE,/* obsolete, do not use */
+
+	TCA_FQ_FLOW_MAX_RATE,	/* per flow max rate */
+
+	TCA_FQ_BUCKETS_LOG,	/* log2(number of buckets) */
+
+	TCA_FQ_FLOW_REFILL_DELAY,	/* flow credit refill delay in usec */
+
+	TCA_FQ_ORPHAN_MASK,	/* mask applied to orphaned skb hashes */
+
+	__TCA_FQ_MAX
+};
+
+#define TCA_FQ_MAX	(__TCA_FQ_MAX - 1)
+
+struct tc_fq_qd_stats {
+	__u64	gc_flows;
+	__u64	highprio_packets;
+	__u64	tcp_retrans;
+	__u64	throttled;
+	__u64	flows_plimit;
+	__u64	pkts_too_long;
+	__u64	allocation_errors;
+	__s64	time_next_delayed_flow;
+	__u32	flows;
+	__u32	inactive_flows;
+	__u32	throttled_flows;
+	__u32	pad;
+};
+
+/* Heavy-Hitter Filter */
+
+enum {
+	TCA_HHF_UNSPEC,
+	TCA_HHF_BACKLOG_LIMIT,
+	TCA_HHF_QUANTUM,
+	TCA_HHF_HH_FLOWS_LIMIT,
+	TCA_HHF_RESET_TIMEOUT,
+	TCA_HHF_ADMIT_BYTES,
+	TCA_HHF_EVICT_TIMEOUT,
+	TCA_HHF_NON_HH_WEIGHT,
+	__TCA_HHF_MAX
+};
+
+#define TCA_HHF_MAX	(__TCA_HHF_MAX - 1)
+
+struct tc_hhf_xstats {
+	__u32	drop_overlimit; /* number of times max qdisc packet limit
+				 * was hit
+				 */
+	__u32	hh_overlimit;   /* number of times max heavy-hitters was hit */
+	__u32	hh_tot_count;   /* number of captured heavy-hitters so far */
+	__u32	hh_cur_count;   /* number of current heavy-hitters */
+};
+
+/* PIE */
+enum {
+	TCA_PIE_UNSPEC,
+	TCA_PIE_TARGET,
+	TCA_PIE_LIMIT,
+	TCA_PIE_TUPDATE,
+	TCA_PIE_ALPHA,
+	TCA_PIE_BETA,
+	TCA_PIE_ECN,
+	TCA_PIE_BYTEMODE,
+	__TCA_PIE_MAX
+};
+#define TCA_PIE_MAX   (__TCA_PIE_MAX - 1)
+
+struct tc_pie_xstats {
+	__u32 prob;             /* current probability */
+	__u32 delay;            /* current delay in ms */
+	__u32 avg_dq_rate;      /* current average dq_rate in bits/pie_time */
+	__u32 packets_in;       /* total number of packets enqueued */
+	__u32 dropped;          /* packets dropped due to pie_action */
+	__u32 overlimit;        /* dropped due to lack of space in queue */
+	__u32 maxq;             /* maximum queue size */
+	__u32 ecn_mark;         /* packets marked with ecn*/
+};
+#endif
diff --git a/include/uapi/linux/rtnetlink.h b/include/linux/rtnetlink.h
similarity index 86%
rename from include/uapi/linux/rtnetlink.h
rename to include/linux/rtnetlink.h
index 358e83e..5fc9337 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_RTNETLINK_H
 #define __LINUX_RTNETLINK_H
 
@@ -123,8 +122,6 @@
 
 	RTM_NEWNETCONF = 80,
 #define RTM_NEWNETCONF RTM_NEWNETCONF
-	RTM_DELNETCONF,
-#define RTM_DELNETCONF RTM_DELNETCONF
 	RTM_GETNETCONF = 82,
 #define RTM_GETNETCONF RTM_GETNETCONF
 
@@ -142,28 +139,6 @@
 	RTM_GETNSID = 90,
 #define RTM_GETNSID RTM_GETNSID
 
-	RTM_NEWSTATS = 92,
-#define RTM_NEWSTATS RTM_NEWSTATS
-	RTM_GETSTATS = 94,
-#define RTM_GETSTATS RTM_GETSTATS
-
-	RTM_NEWCACHEREPORT = 96,
-#define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT
-
-	RTM_NEWCHAIN = 100,
-#define RTM_NEWCHAIN RTM_NEWCHAIN
-	RTM_DELCHAIN,
-#define RTM_DELCHAIN RTM_DELCHAIN
-	RTM_GETCHAIN,
-#define RTM_GETCHAIN RTM_GETCHAIN
-
-	RTM_NEWNEXTHOP = 104,
-#define RTM_NEWNEXTHOP	RTM_NEWNEXTHOP
-	RTM_DELNEXTHOP,
-#define RTM_DELNEXTHOP	RTM_DELNEXTHOP
-	RTM_GETNEXTHOP,
-#define RTM_GETNEXTHOP	RTM_GETNEXTHOP
-
 	__RTM_MAX,
 #define RTM_MAX		(((__RTM_MAX + 3) & ~3) - 1)
 };
@@ -268,11 +243,6 @@
 #define RTPROT_DHCP	16      /* DHCP client */
 #define RTPROT_MROUTED	17      /* Multicast daemon */
 #define RTPROT_BABEL	42      /* Babel daemon */
-#define RTPROT_BGP	186     /* BGP Routes */
-#define RTPROT_ISIS	187     /* ISIS Routes */
-#define RTPROT_OSPF	188     /* OSPF Routes */
-#define RTPROT_RIP	189     /* RIP Routes */
-#define RTPROT_EIGRP	192     /* EIGRP Routes */
 
 /* rtm_scope
 
@@ -301,7 +271,6 @@
 #define RTM_F_EQUALIZE		0x400	/* Multipath equalizer: NI	*/
 #define RTM_F_PREFIX		0x800	/* Prefix addresses		*/
 #define RTM_F_LOOKUP_TABLE	0x1000	/* set rtm_table to FIB lookup result */
-#define RTM_F_FIB_MATCH	        0x2000	/* return full fib lookup match */
 
 /* Reserved table identifiers */
 
@@ -345,11 +314,6 @@
 	RTA_EXPIRES,
 	RTA_PAD,
 	RTA_UID,
-	RTA_TTL_PROPAGATE,
-	RTA_IP_PROTO,
-	RTA_SPORT,
-	RTA_DPORT,
-	RTA_NH_ID,
 	__RTA_MAX
 };
 
@@ -381,9 +345,8 @@
 #define RTNH_F_ONLINK		4	/* Gateway is forced on link	*/
 #define RTNH_F_OFFLOAD		8	/* offloaded route */
 #define RTNH_F_LINKDOWN		16	/* carrier-down on nexthop */
-#define RTNH_F_UNRESOLVED	32	/* The entry is unresolved (ipmr) */
 
-#define RTNH_COMPARE_MASK	(RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD)
+#define RTNH_COMPARE_MASK	(RTNH_F_DEAD | RTNH_F_LINKDOWN)
 
 /* Macros to handle hexthops */
 
@@ -454,8 +417,6 @@
 #define RTAX_QUICKACK RTAX_QUICKACK
 	RTAX_CC_ALGO,
 #define RTAX_CC_ALGO RTAX_CC_ALGO
-	RTAX_FASTOPEN_NO_COOKIE,
-#define RTAX_FASTOPEN_NO_COOKIE RTAX_FASTOPEN_NO_COOKIE
 	__RTAX_MAX
 };
 
@@ -564,19 +525,9 @@
 	int		tcm_ifindex;
 	__u32		tcm_handle;
 	__u32		tcm_parent;
-/* tcm_block_index is used instead of tcm_parent
- * in case tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK
- */
-#define tcm_block_index tcm_parent
 	__u32		tcm_info;
 };
 
-/* For manipulation of filters in shared block, tcm_ifindex is set to
- * TCM_IFINDEX_MAGIC_BLOCK, and tcm_parent is aliased to tcm_block_index
- * which is the block index.
- */
-#define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU)
-
 enum {
 	TCA_UNSPEC,
 	TCA_KIND,
@@ -587,12 +538,6 @@
 	TCA_FCNT,
 	TCA_STATS2,
 	TCA_STAB,
-	TCA_PAD,
-	TCA_DUMP_INVISIBLE,
-	TCA_CHAIN,
-	TCA_HW_OFFLOAD,
-	TCA_INGRESS_BLOCK,
-	TCA_EGRESS_BLOCK,
 	__TCA_MAX
 };
 
@@ -704,14 +649,6 @@
 #define RTNLGRP_MPLS_ROUTE	RTNLGRP_MPLS_ROUTE
 	RTNLGRP_NSID,
 #define RTNLGRP_NSID		RTNLGRP_NSID
-	RTNLGRP_MPLS_NETCONF,
-#define RTNLGRP_MPLS_NETCONF	RTNLGRP_MPLS_NETCONF
-	RTNLGRP_IPV4_MROUTE_R,
-#define RTNLGRP_IPV4_MROUTE_R	RTNLGRP_IPV4_MROUTE_R
-	RTNLGRP_IPV6_MROUTE_R,
-#define RTNLGRP_IPV6_MROUTE_R	RTNLGRP_IPV6_MROUTE_R
-	RTNLGRP_NEXTHOP,
-#define RTNLGRP_NEXTHOP		RTNLGRP_NEXTHOP
 	__RTNLGRP_MAX
 };
 #define RTNLGRP_MAX	(__RTNLGRP_MAX - 1)
@@ -722,29 +659,10 @@
 	unsigned char	tca__pad1;
 	unsigned short	tca__pad2;
 };
-
-enum {
-	TCA_ROOT_UNSPEC,
-	TCA_ROOT_TAB,
-#define TCA_ACT_TAB TCA_ROOT_TAB
-#define TCAA_MAX TCA_ROOT_TAB
-	TCA_ROOT_FLAGS,
-	TCA_ROOT_COUNT,
-	TCA_ROOT_TIME_DELTA, /* in msecs */
-	__TCA_ROOT_MAX,
-#define	TCA_ROOT_MAX (__TCA_ROOT_MAX - 1)
-};
-
 #define TA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg))))
 #define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg))
-/* tcamsg flags stored in attribute TCA_ROOT_FLAGS
- *
- * TCA_FLAG_LARGE_DUMP_ON user->kernel to request for larger than TCA_ACT_MAX_PRIO
- * actions in a dump. All dump responses will contain the number of actions
- * being dumped stored in for user app's consumption in TCA_ROOT_COUNT
- *
- */
-#define TCA_FLAG_LARGE_DUMP_ON		(1 << 0)
+#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)
diff --git a/include/uapi/linux/sock_diag.h b/include/linux/sock_diag.h
similarity index 88%
rename from include/uapi/linux/sock_diag.h
rename to include/linux/sock_diag.h
index a69cf20..dafcb89 100644
--- a/include/uapi/linux/sock_diag.h
+++ b/include/linux/sock_diag.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __SOCK_DIAG_H__
 #define __SOCK_DIAG_H__
 
@@ -21,7 +20,6 @@
 	SK_MEMINFO_WMEM_QUEUED,
 	SK_MEMINFO_OPTMEM,
 	SK_MEMINFO_BACKLOG,
-	SK_MEMINFO_DROPS,
 
 	SK_MEMINFO_VARS,
 };
diff --git a/include/uapi/linux/tc_act/tc_bpf.h b/include/linux/tc_act/tc_bpf.h
similarity index 84%
rename from include/uapi/linux/tc_act/tc_bpf.h
rename to include/linux/tc_act/tc_bpf.h
index 653c4f9..07f17cc 100644
--- a/include/uapi/linux/tc_act/tc_bpf.h
+++ b/include/linux/tc_act/tc_bpf.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
 /*
  * Copyright (c) 2015 Jiri Pirko <jiri@resnulli.us>
  *
@@ -13,6 +12,8 @@
 
 #include <linux/pkt_cls.h>
 
+#define TCA_ACT_BPF 13
+
 struct tc_act_bpf {
 	tc_gen;
 };
@@ -25,9 +26,6 @@
 	TCA_ACT_BPF_OPS,
 	TCA_ACT_BPF_FD,
 	TCA_ACT_BPF_NAME,
-	TCA_ACT_BPF_PAD,
-	TCA_ACT_BPF_TAG,
-	TCA_ACT_BPF_ID,
 	__TCA_ACT_BPF_MAX,
 };
 #define TCA_ACT_BPF_MAX (__TCA_ACT_BPF_MAX - 1)
diff --git a/include/uapi/linux/tc_act/tc_connmark.h b/include/linux/tc_act/tc_connmark.h
similarity index 78%
rename from include/uapi/linux/tc_act/tc_connmark.h
rename to include/linux/tc_act/tc_connmark.h
index 9f8f6f7..994b097 100644
--- a/include/uapi/linux/tc_act/tc_connmark.h
+++ b/include/linux/tc_act/tc_connmark.h
@@ -1,10 +1,11 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __UAPI_TC_CONNMARK_H
 #define __UAPI_TC_CONNMARK_H
 
 #include <linux/types.h>
 #include <linux/pkt_cls.h>
 
+#define TCA_ACT_CONNMARK 14
+
 struct tc_connmark {
 	tc_gen;
 	__u16 zone;
@@ -14,7 +15,6 @@
 	TCA_CONNMARK_UNSPEC,
 	TCA_CONNMARK_PARMS,
 	TCA_CONNMARK_TM,
-	TCA_CONNMARK_PAD,
 	__TCA_CONNMARK_MAX
 };
 #define TCA_CONNMARK_MAX (__TCA_CONNMARK_MAX - 1)
diff --git a/include/uapi/linux/tc_act/tc_csum.h b/include/linux/tc_act/tc_csum.h
similarity index 76%
rename from include/uapi/linux/tc_act/tc_csum.h
rename to include/linux/tc_act/tc_csum.h
index 94b2044..a047c49 100644
--- a/include/uapi/linux/tc_act/tc_csum.h
+++ b/include/linux/tc_act/tc_csum.h
@@ -1,15 +1,15 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #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_PAD,
 	__TCA_CSUM_MAX
 };
 #define TCA_CSUM_MAX (__TCA_CSUM_MAX - 1)
@@ -20,8 +20,7 @@
 	TCA_CSUM_UPDATE_FLAG_IGMP    = 4,
 	TCA_CSUM_UPDATE_FLAG_TCP     = 8,
 	TCA_CSUM_UPDATE_FLAG_UDP     = 16,
-	TCA_CSUM_UPDATE_FLAG_UDPLITE = 32,
-	TCA_CSUM_UPDATE_FLAG_SCTP    = 64,
+	TCA_CSUM_UPDATE_FLAG_UDPLITE = 32
 };
 
 struct tc_csum {
diff --git a/include/uapi/linux/tc_act/tc_defact.h b/include/linux/tc_act/tc_defact.h
similarity index 76%
rename from include/uapi/linux/tc_act/tc_defact.h
rename to include/linux/tc_act/tc_defact.h
index e3ecd8b..17dddb4 100644
--- a/include/uapi/linux/tc_act/tc_defact.h
+++ b/include/linux/tc_act/tc_defact.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_TC_DEF_H
 #define __LINUX_TC_DEF_H
 
@@ -13,7 +12,6 @@
 	TCA_DEF_TM,
 	TCA_DEF_PARMS,
 	TCA_DEF_DATA,
-	TCA_DEF_PAD,
 	__TCA_DEF_MAX
 };
 #define TCA_DEF_MAX (__TCA_DEF_MAX - 1)
diff --git a/include/uapi/linux/tc_act/tc_gact.h b/include/linux/tc_act/tc_gact.h
similarity index 87%
rename from include/uapi/linux/tc_act/tc_gact.h
rename to include/linux/tc_act/tc_gact.h
index 37e5392..f7bf94e 100644
--- a/include/uapi/linux/tc_act/tc_gact.h
+++ b/include/linux/tc_act/tc_gact.h
@@ -1,10 +1,10 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_TC_GACT_H
 #define __LINUX_TC_GACT_H
 
 #include <linux/types.h>
 #include <linux/pkt_cls.h>
 
+#define TCA_ACT_GACT 5
 struct tc_gact {
 	tc_gen;
 
@@ -25,7 +25,6 @@
 	TCA_GACT_TM,
 	TCA_GACT_PARMS,
 	TCA_GACT_PROB,
-	TCA_GACT_PAD,
 	__TCA_GACT_MAX
 };
 #define TCA_GACT_MAX (__TCA_GACT_MAX - 1)
diff --git a/include/uapi/linux/tc_act/tc_ipt.h b/include/linux/tc_act/tc_ipt.h
similarity index 81%
rename from include/uapi/linux/tc_act/tc_ipt.h
rename to include/linux/tc_act/tc_ipt.h
index c48d7da..130aaad 100644
--- a/include/uapi/linux/tc_act/tc_ipt.h
+++ b/include/linux/tc_act/tc_ipt.h
@@ -1,9 +1,11 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_TC_IPT_H
 #define __LINUX_TC_IPT_H
 
 #include <linux/pkt_cls.h>
 
+#define TCA_ACT_IPT 6
+#define TCA_ACT_XT 10
+
 enum {
 	TCA_IPT_UNSPEC,
 	TCA_IPT_TABLE,
@@ -12,7 +14,6 @@
 	TCA_IPT_CNT,
 	TCA_IPT_TM,
 	TCA_IPT_TARG,
-	TCA_IPT_PAD,
 	__TCA_IPT_MAX
 };
 #define TCA_IPT_MAX (__TCA_IPT_MAX - 1)
diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/linux/tc_act/tc_mirred.h
similarity index 70%
rename from include/uapi/linux/tc_act/tc_mirred.h
rename to include/linux/tc_act/tc_mirred.h
index 2500a00..7561750 100644
--- a/include/uapi/linux/tc_act/tc_mirred.h
+++ b/include/linux/tc_act/tc_mirred.h
@@ -1,28 +1,27 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_TC_MIR_H
 #define __LINUX_TC_MIR_H
 
 #include <linux/types.h>
 #include <linux/pkt_cls.h>
 
+#define TCA_ACT_MIRRED 8
 #define TCA_EGRESS_REDIR 1  /* packet redirect to EGRESS*/
 #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
 #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
 #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
-
+                                                                                
 struct tc_mirred {
 	tc_gen;
 	int                     eaction;   /* one of IN/EGRESS_MIRROR/REDIR */
 	__u32                   ifindex;  /* ifindex of egress port */
 };
-
+                                                                                
 enum {
 	TCA_MIRRED_UNSPEC,
 	TCA_MIRRED_TM,
 	TCA_MIRRED_PARMS,
-	TCA_MIRRED_PAD,
 	__TCA_MIRRED_MAX
 };
 #define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
-
+                                                                                
 #endif
diff --git a/include/uapi/linux/tc_act/tc_nat.h b/include/linux/tc_act/tc_nat.h
similarity index 81%
rename from include/uapi/linux/tc_act/tc_nat.h
rename to include/linux/tc_act/tc_nat.h
index 21399c2..6663aeb 100644
--- a/include/uapi/linux/tc_act/tc_nat.h
+++ b/include/linux/tc_act/tc_nat.h
@@ -1,15 +1,15 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_TC_NAT_H
 #define __LINUX_TC_NAT_H
 
 #include <linux/pkt_cls.h>
 #include <linux/types.h>
 
+#define TCA_ACT_NAT 9
+
 enum {
 	TCA_NAT_UNSPEC,
 	TCA_NAT_PARMS,
 	TCA_NAT_TM,
-	TCA_NAT_PAD,
 	__TCA_NAT_MAX
 };
 #define TCA_NAT_MAX (__TCA_NAT_MAX - 1)
diff --git a/include/linux/tc_act/tc_pedit.h b/include/linux/tc_act/tc_pedit.h
new file mode 100644
index 0000000..716cfab
--- /dev/null
+++ b/include/linux/tc_act/tc_pedit.h
@@ -0,0 +1,34 @@
+#ifndef __LINUX_TC_PED_H
+#define __LINUX_TC_PED_H
+
+#include <linux/types.h>
+#include <linux/pkt_cls.h>
+
+#define TCA_ACT_PEDIT 7
+
+enum {
+	TCA_PEDIT_UNSPEC,
+	TCA_PEDIT_TM,
+	TCA_PEDIT_PARMS,
+	__TCA_PEDIT_MAX
+};
+#define TCA_PEDIT_MAX (__TCA_PEDIT_MAX - 1)
+                                                                                
+struct tc_pedit_key {
+	__u32           mask;  /* AND */
+	__u32           val;   /*XOR */
+	__u32           off;  /*offset */
+	__u32           at;
+	__u32           offmask;
+	__u32           shift;
+};
+                                                                                
+struct tc_pedit_sel {
+	tc_gen;
+	unsigned char           nkeys;
+	unsigned char           flags;
+	struct tc_pedit_key     keys[0];
+};
+#define tc_pedit tc_pedit_sel
+
+#endif
diff --git a/include/uapi/linux/tc_act/tc_skbedit.h b/include/linux/tc_act/tc_skbedit.h
similarity index 83%
rename from include/uapi/linux/tc_act/tc_skbedit.h
rename to include/linux/tc_act/tc_skbedit.h
index 800e933..7a2e910 100644
--- a/include/uapi/linux/tc_act/tc_skbedit.h
+++ b/include/linux/tc_act/tc_skbedit.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /*
  * Copyright (c) 2008, Intel Corporation.
  *
@@ -23,12 +22,11 @@
 
 #include <linux/pkt_cls.h>
 
+#define TCA_ACT_SKBEDIT 11
+
 #define SKBEDIT_F_PRIORITY		0x1
 #define SKBEDIT_F_QUEUE_MAPPING		0x2
 #define SKBEDIT_F_MARK			0x4
-#define SKBEDIT_F_PTYPE			0x8
-#define SKBEDIT_F_MASK			0x10
-#define SKBEDIT_F_INHERITDSFIELD	0x20
 
 struct tc_skbedit {
 	tc_gen;
@@ -41,10 +39,6 @@
 	TCA_SKBEDIT_PRIORITY,
 	TCA_SKBEDIT_QUEUE_MAPPING,
 	TCA_SKBEDIT_MARK,
-	TCA_SKBEDIT_PAD,
-	TCA_SKBEDIT_PTYPE,
-	TCA_SKBEDIT_MASK,
-	TCA_SKBEDIT_FLAGS,
 	__TCA_SKBEDIT_MAX
 };
 #define TCA_SKBEDIT_MAX (__TCA_SKBEDIT_MAX - 1)
diff --git a/include/uapi/linux/tc_act/tc_vlan.h b/include/linux/tc_act/tc_vlan.h
similarity index 82%
rename from include/uapi/linux/tc_act/tc_vlan.h
rename to include/linux/tc_act/tc_vlan.h
index 168995b..f7b8d44 100644
--- a/include/uapi/linux/tc_act/tc_vlan.h
+++ b/include/linux/tc_act/tc_vlan.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
 /*
  * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
  *
@@ -13,9 +12,10 @@
 
 #include <linux/pkt_cls.h>
 
+#define TCA_ACT_VLAN 12
+
 #define TCA_VLAN_ACT_POP	1
 #define TCA_VLAN_ACT_PUSH	2
-#define TCA_VLAN_ACT_MODIFY	3
 
 struct tc_vlan {
 	tc_gen;
@@ -28,8 +28,6 @@
 	TCA_VLAN_PARMS,
 	TCA_VLAN_PUSH_VLAN_ID,
 	TCA_VLAN_PUSH_VLAN_PROTOCOL,
-	TCA_VLAN_PAD,
-	TCA_VLAN_PUSH_VLAN_PRIORITY,
 	__TCA_VLAN_MAX,
 };
 #define TCA_VLAN_MAX (__TCA_VLAN_MAX - 1)
diff --git a/include/uapi/linux/tc_ematch/tc_em_cmp.h b/include/linux/tc_ematch/tc_em_cmp.h
similarity index 84%
rename from include/uapi/linux/tc_ematch/tc_em_cmp.h
rename to include/linux/tc_ematch/tc_em_cmp.h
index 2549d9d..f34bb1b 100644
--- a/include/uapi/linux/tc_ematch/tc_em_cmp.h
+++ b/include/linux/tc_ematch/tc_em_cmp.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_TC_EM_CMP_H
 #define __LINUX_TC_EM_CMP_H
 
diff --git a/include/uapi/linux/tc_ematch/tc_em_meta.h b/include/linux/tc_ematch/tc_em_meta.h
similarity index 96%
rename from include/uapi/linux/tc_ematch/tc_em_meta.h
rename to include/linux/tc_ematch/tc_em_meta.h
index cf30b5b..b11f8ce 100644
--- a/include/uapi/linux/tc_ematch/tc_em_meta.h
+++ b/include/linux/tc_ematch/tc_em_meta.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_TC_EM_META_H
 #define __LINUX_TC_EM_META_H
 
diff --git a/include/uapi/linux/tc_ematch/tc_em_nbyte.h b/include/linux/tc_ematch/tc_em_nbyte.h
similarity index 75%
rename from include/uapi/linux/tc_ematch/tc_em_nbyte.h
rename to include/linux/tc_ematch/tc_em_nbyte.h
index c76333f..7172cfb 100644
--- a/include/uapi/linux/tc_ematch/tc_em_nbyte.h
+++ b/include/linux/tc_ematch/tc_em_nbyte.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_TC_EM_NBYTE_H
 #define __LINUX_TC_EM_NBYTE_H
 
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/tcp.h b/include/linux/tcp.h
new file mode 100644
index 0000000..1e9b4a6
--- /dev/null
+++ b/include/linux/tcp.h
@@ -0,0 +1,212 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		Definitions for the TCP protocol.
+ *
+ * Version:	@(#)tcp.h	1.0.2	04/28/93
+ *
+ * Author:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_TCP_H
+#define _LINUX_TCP_H
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/socket.h>
+
+struct tcphdr {
+	__be16	source;
+	__be16	dest;
+	__be32	seq;
+	__be32	ack_seq;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	__u16	res1:4,
+		doff:4,
+		fin:1,
+		syn:1,
+		rst:1,
+		psh:1,
+		ack:1,
+		urg:1,
+		ece:1,
+		cwr:1;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	__u16	doff:4,
+		res1:4,
+		cwr:1,
+		ece:1,
+		urg:1,
+		ack:1,
+		psh:1,
+		rst:1,
+		syn:1,
+		fin:1;
+#else
+#error	"Adjust your <asm/byteorder.h> defines"
+#endif	
+	__be16	window;
+	__sum16	check;
+	__be16	urg_ptr;
+};
+
+/*
+ *	The union cast uses a gcc extension to avoid aliasing problems
+ *  (union is compatible to any of its members)
+ *  This means this part of the code is -fstrict-aliasing safe now.
+ */
+union tcp_word_hdr { 
+	struct tcphdr hdr;
+	__be32 		  words[5];
+}; 
+
+#define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words [3]) 
+
+enum { 
+	TCP_FLAG_CWR = __constant_cpu_to_be32(0x00800000),
+	TCP_FLAG_ECE = __constant_cpu_to_be32(0x00400000),
+	TCP_FLAG_URG = __constant_cpu_to_be32(0x00200000),
+	TCP_FLAG_ACK = __constant_cpu_to_be32(0x00100000),
+	TCP_FLAG_PSH = __constant_cpu_to_be32(0x00080000),
+	TCP_FLAG_RST = __constant_cpu_to_be32(0x00040000),
+	TCP_FLAG_SYN = __constant_cpu_to_be32(0x00020000),
+	TCP_FLAG_FIN = __constant_cpu_to_be32(0x00010000),
+	TCP_RESERVED_BITS = __constant_cpu_to_be32(0x0F000000),
+	TCP_DATA_OFFSET = __constant_cpu_to_be32(0xF0000000)
+}; 
+
+/*
+ * TCP general constants
+ */
+#define TCP_MSS_DEFAULT		 536U	/* IPv4 (RFC1122, RFC2581) */
+#define TCP_MSS_DESIRED		1220U	/* IPv6 (tunneled), EDNS0 (RFC3226) */
+
+/* TCP socket options */
+#define TCP_NODELAY		1	/* Turn off Nagle's algorithm. */
+#define TCP_MAXSEG		2	/* Limit MSS */
+#define TCP_CORK		3	/* Never send partially complete segments */
+#define TCP_KEEPIDLE		4	/* Start keeplives after this period */
+#define TCP_KEEPINTVL		5	/* Interval between keepalives */
+#define TCP_KEEPCNT		6	/* Number of keepalives before death */
+#define TCP_SYNCNT		7	/* Number of SYN retransmits */
+#define TCP_LINGER2		8	/* Life time of orphaned FIN-WAIT-2 state */
+#define TCP_DEFER_ACCEPT	9	/* Wake up listener only when data arrive */
+#define TCP_WINDOW_CLAMP	10	/* Bound advertised window */
+#define TCP_INFO		11	/* Information about this connection. */
+#define TCP_QUICKACK		12	/* Block/reenable quick acks */
+#define TCP_CONGESTION		13	/* Congestion control algorithm */
+#define TCP_MD5SIG		14	/* TCP MD5 Signature (RFC2385) */
+#define TCP_THIN_LINEAR_TIMEOUTS 16      /* Use linear timeouts for thin streams*/
+#define TCP_THIN_DUPACK         17      /* Fast retrans. after 1 dupack */
+#define TCP_USER_TIMEOUT	18	/* How long for loss retry before timeout */
+#define TCP_REPAIR		19	/* TCP sock is under repair right now */
+#define TCP_REPAIR_QUEUE	20
+#define TCP_QUEUE_SEQ		21
+#define TCP_REPAIR_OPTIONS	22
+#define TCP_FASTOPEN		23	/* Enable FastOpen on listeners */
+#define TCP_TIMESTAMP		24
+#define TCP_NOTSENT_LOWAT	25	/* limit number of unsent bytes in write queue */
+#define TCP_CC_INFO		26	/* Get Congestion Control (optional) info */
+#define TCP_SAVE_SYN		27	/* Record SYN headers for new connections */
+#define TCP_SAVED_SYN		28	/* Get SYN headers recorded for connection */
+
+struct tcp_repair_opt {
+	__u32	opt_code;
+	__u32	opt_val;
+};
+
+enum {
+	TCP_NO_QUEUE,
+	TCP_RECV_QUEUE,
+	TCP_SEND_QUEUE,
+	TCP_QUEUES_NR,
+};
+
+/* for TCP_INFO socket option */
+#define TCPI_OPT_TIMESTAMPS	1
+#define TCPI_OPT_SACK		2
+#define TCPI_OPT_WSCALE		4
+#define TCPI_OPT_ECN		8 /* ECN was negociated at TCP session init */
+#define TCPI_OPT_ECN_SEEN	16 /* we received at least one packet with ECT */
+#define TCPI_OPT_SYN_DATA	32 /* SYN-ACK acked data in SYN sent or rcvd */
+
+enum tcp_ca_state {
+	TCP_CA_Open = 0,
+#define TCPF_CA_Open	(1<<TCP_CA_Open)
+	TCP_CA_Disorder = 1,
+#define TCPF_CA_Disorder (1<<TCP_CA_Disorder)
+	TCP_CA_CWR = 2,
+#define TCPF_CA_CWR	(1<<TCP_CA_CWR)
+	TCP_CA_Recovery = 3,
+#define TCPF_CA_Recovery (1<<TCP_CA_Recovery)
+	TCP_CA_Loss = 4
+#define TCPF_CA_Loss	(1<<TCP_CA_Loss)
+};
+
+struct tcp_info {
+	__u8	tcpi_state;
+	__u8	tcpi_ca_state;
+	__u8	tcpi_retransmits;
+	__u8	tcpi_probes;
+	__u8	tcpi_backoff;
+	__u8	tcpi_options;
+	__u8	tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4;
+
+	__u32	tcpi_rto;
+	__u32	tcpi_ato;
+	__u32	tcpi_snd_mss;
+	__u32	tcpi_rcv_mss;
+
+	__u32	tcpi_unacked;
+	__u32	tcpi_sacked;
+	__u32	tcpi_lost;
+	__u32	tcpi_retrans;
+	__u32	tcpi_fackets;
+
+	/* Times. */
+	__u32	tcpi_last_data_sent;
+	__u32	tcpi_last_ack_sent;     /* Not remembered, sorry. */
+	__u32	tcpi_last_data_recv;
+	__u32	tcpi_last_ack_recv;
+
+	/* Metrics. */
+	__u32	tcpi_pmtu;
+	__u32	tcpi_rcv_ssthresh;
+	__u32	tcpi_rtt;
+	__u32	tcpi_rttvar;
+	__u32	tcpi_snd_ssthresh;
+	__u32	tcpi_snd_cwnd;
+	__u32	tcpi_advmss;
+	__u32	tcpi_reordering;
+
+	__u32	tcpi_rcv_rtt;
+	__u32	tcpi_rcv_space;
+
+	__u32	tcpi_total_retrans;
+
+	__u64	tcpi_pacing_rate;
+	__u64	tcpi_max_pacing_rate;
+	__u64	tcpi_bytes_acked;    /* RFC4898 tcpEStatsAppHCThruOctetsAcked */
+	__u64	tcpi_bytes_received; /* RFC4898 tcpEStatsAppHCThruOctetsReceived */
+	__u32	tcpi_segs_out;	     /* RFC4898 tcpEStatsPerfSegsOut */
+	__u32	tcpi_segs_in;	     /* RFC4898 tcpEStatsPerfSegsIn */
+};
+
+/* for TCP_MD5SIG socket option */
+#define TCP_MD5SIG_MAXKEYLEN	80
+
+struct tcp_md5sig {
+	struct __kernel_sockaddr_storage tcpm_addr;	/* address associated */
+	__u16	__tcpm_pad1;				/* zero */
+	__u16	tcpm_keylen;				/* key length */
+	__u32	__tcpm_pad2;				/* zero */
+	__u8	tcpm_key[TCP_MD5SIG_MAXKEYLEN];		/* key (binary) */
+};
+
+#endif /* _LINUX_TCP_H */
diff --git a/include/uapi/linux/tcp_metrics.h b/include/linux/tcp_metrics.h
similarity index 94%
rename from include/uapi/linux/tcp_metrics.h
rename to include/linux/tcp_metrics.h
index 7cb4a17..9353392 100644
--- a/include/uapi/linux/tcp_metrics.h
+++ b/include/linux/tcp_metrics.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /* tcp_metrics.h - TCP Metrics Interface */
 
 #ifndef _LINUX_TCP_METRICS_H
@@ -41,7 +40,6 @@
 	TCP_METRICS_ATTR_FOPEN_COOKIE,		/* binary */
 	TCP_METRICS_ATTR_SADDR_IPV4,		/* u32 */
 	TCP_METRICS_ATTR_SADDR_IPV6,		/* binary */
-	TCP_METRICS_ATTR_PAD,
 
 	__TCP_METRICS_ATTR_MAX,
 };
diff --git a/include/linux/tipc.h b/include/linux/tipc.h
new file mode 100644
index 0000000..ebd3b63
--- /dev/null
+++ b/include/linux/tipc.h
@@ -0,0 +1,232 @@
+/*
+ * include/uapi/linux/tipc.h: Header for TIPC socket interface
+ *
+ * Copyright (c) 2003-2006, Ericsson AB
+ * Copyright (c) 2005, 2010-2011, Wind River Systems
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LINUX_TIPC_H_
+#define _LINUX_TIPC_H_
+
+#include <linux/types.h>
+#include <linux/sockios.h>
+
+/*
+ * TIPC addressing primitives
+ */
+
+struct tipc_portid {
+	__u32 ref;
+	__u32 node;
+};
+
+struct tipc_name {
+	__u32 type;
+	__u32 instance;
+};
+
+struct tipc_name_seq {
+	__u32 type;
+	__u32 lower;
+	__u32 upper;
+};
+
+static __inline__ __u32 tipc_addr(unsigned int zone,
+			      unsigned int cluster,
+			      unsigned int node)
+{
+	return (zone << 24) | (cluster << 12) | node;
+}
+
+static __inline__ unsigned int tipc_zone(__u32 addr)
+{
+	return addr >> 24;
+}
+
+static __inline__ unsigned int tipc_cluster(__u32 addr)
+{
+	return (addr >> 12) & 0xfff;
+}
+
+static __inline__ unsigned int tipc_node(__u32 addr)
+{
+	return addr & 0xfff;
+}
+
+/*
+ * Application-accessible port name types
+ */
+
+#define TIPC_CFG_SRV		0	/* configuration service name type */
+#define TIPC_TOP_SRV		1	/* topology service name type */
+#define TIPC_LINK_STATE		2	/* link state name type */
+#define TIPC_RESERVED_TYPES	64	/* lowest user-publishable name type */
+
+/*
+ * Publication scopes when binding port names and port name sequences
+ */
+
+#define TIPC_ZONE_SCOPE		1
+#define TIPC_CLUSTER_SCOPE	2
+#define TIPC_NODE_SCOPE		3
+
+/*
+ * Limiting values for messages
+ */
+
+#define TIPC_MAX_USER_MSG_SIZE	66000U
+
+/*
+ * Message importance levels
+ */
+
+#define TIPC_LOW_IMPORTANCE		0
+#define TIPC_MEDIUM_IMPORTANCE		1
+#define TIPC_HIGH_IMPORTANCE		2
+#define TIPC_CRITICAL_IMPORTANCE	3
+
+/*
+ * Msg rejection/connection shutdown reasons
+ */
+
+#define TIPC_OK			0
+#define TIPC_ERR_NO_NAME	1
+#define TIPC_ERR_NO_PORT	2
+#define TIPC_ERR_NO_NODE	3
+#define TIPC_ERR_OVERLOAD	4
+#define TIPC_CONN_SHUTDOWN	5
+
+/*
+ * TIPC topology subscription service definitions
+ */
+
+#define TIPC_SUB_PORTS		0x01	/* filter for port availability */
+#define TIPC_SUB_SERVICE	0x02	/* filter for service availability */
+#define TIPC_SUB_CANCEL		0x04	/* cancel a subscription */
+
+#define TIPC_WAIT_FOREVER	(~0)	/* timeout for permanent subscription */
+
+struct tipc_subscr {
+	struct tipc_name_seq seq;	/* name sequence of interest */
+	__u32 timeout;			/* subscription duration (in ms) */
+	__u32 filter;			/* bitmask of filter options */
+	char usr_handle[8];		/* available for subscriber use */
+};
+
+#define TIPC_PUBLISHED		1	/* publication event */
+#define TIPC_WITHDRAWN		2	/* withdraw event */
+#define TIPC_SUBSCR_TIMEOUT	3	/* subscription timeout event */
+
+struct tipc_event {
+	__u32 event;			/* event type */
+	__u32 found_lower;		/* matching name seq instances */
+	__u32 found_upper;		/*    "      "    "     "      */
+	struct tipc_portid port;	/* associated port */
+	struct tipc_subscr s;		/* associated subscription */
+};
+
+/*
+ * Socket API
+ */
+
+#ifndef AF_TIPC
+#define AF_TIPC		30
+#endif
+
+#ifndef PF_TIPC
+#define PF_TIPC		AF_TIPC
+#endif
+
+#ifndef SOL_TIPC
+#define SOL_TIPC	271
+#endif
+
+#define TIPC_ADDR_NAMESEQ	1
+#define TIPC_ADDR_MCAST		1
+#define TIPC_ADDR_NAME		2
+#define TIPC_ADDR_ID		3
+
+struct sockaddr_tipc {
+	unsigned short family;
+	unsigned char  addrtype;
+	signed   char  scope;
+	union {
+		struct tipc_portid id;
+		struct tipc_name_seq nameseq;
+		struct {
+			struct tipc_name name;
+			__u32 domain;
+		} name;
+	} addr;
+};
+
+/*
+ * Ancillary data objects supported by recvmsg()
+ */
+
+#define TIPC_ERRINFO	1	/* error info */
+#define TIPC_RETDATA	2	/* returned data */
+#define TIPC_DESTNAME	3	/* destination name */
+
+/*
+ * TIPC-specific socket option values
+ */
+
+#define TIPC_IMPORTANCE		127	/* Default: TIPC_LOW_IMPORTANCE */
+#define TIPC_SRC_DROPPABLE	128	/* Default: based on socket type */
+#define TIPC_DEST_DROPPABLE	129	/* Default: based on socket type */
+#define TIPC_CONN_TIMEOUT	130	/* Default: 8000 (ms)  */
+#define TIPC_NODE_RECVQ_DEPTH	131	/* Default: none (read only) */
+#define TIPC_SOCK_RECVQ_DEPTH	132	/* Default: none (read only) */
+
+/*
+ * Maximum sizes of TIPC bearer-related names (including terminating NULL)
+ * The string formatting for each name element is:
+ * media: media
+ * interface: media:interface name
+ * link: Z.C.N:interface-Z.C.N:interface
+ *
+ */
+
+#define TIPC_MAX_MEDIA_NAME	16
+#define TIPC_MAX_IF_NAME	16
+#define TIPC_MAX_BEARER_NAME	32
+#define TIPC_MAX_LINK_NAME	60
+
+#define SIOCGETLINKNAME		SIOCPROTOPRIVATE
+
+struct tipc_sioc_ln_req {
+	__u32 peer;
+	__u32 bearer_id;
+	char linkname[TIPC_MAX_LINK_NAME];
+};
+#endif
diff --git a/include/uapi/linux/tipc_netlink.h b/include/linux/tipc_netlink.h
similarity index 75%
rename from include/uapi/linux/tipc_netlink.h
rename to include/linux/tipc_netlink.h
index efb958f..25eb645 100644
--- a/include/uapi/linux/tipc_netlink.h
+++ b/include/linux/tipc_netlink.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
 /*
  * Copyright (c) 2014, Ericsson AB
  * All rights reserved.
@@ -57,12 +56,7 @@
 	TIPC_NL_NET_GET,
 	TIPC_NL_NET_SET,
 	TIPC_NL_NAME_TABLE_GET,
-	TIPC_NL_MON_SET,
-	TIPC_NL_MON_GET,
-	TIPC_NL_MON_PEER_GET,
 	TIPC_NL_PEER_REMOVE,
-	TIPC_NL_BEARER_ADD,
-	TIPC_NL_UDP_GET_REMOTEIP,
 
 	__TIPC_NL_CMD_MAX,
 	TIPC_NL_CMD_MAX = __TIPC_NL_CMD_MAX - 1
@@ -79,8 +73,6 @@
 	TIPC_NLA_NODE,			/* nest */
 	TIPC_NLA_NET,			/* nest */
 	TIPC_NLA_NAME_TABLE,		/* nest */
-	TIPC_NLA_MON,			/* nest */
-	TIPC_NLA_MON_PEER,		/* nest */
 
 	__TIPC_NLA_MAX,
 	TIPC_NLA_MAX = __TIPC_NLA_MAX - 1
@@ -102,7 +94,6 @@
 	TIPC_NLA_UDP_UNSPEC,
 	TIPC_NLA_UDP_LOCAL,		/* sockaddr_storage */
 	TIPC_NLA_UDP_REMOTE,		/* sockaddr_storage */
-	TIPC_NLA_UDP_MULTI_REMOTEIP,	/* flag */
 
 	__TIPC_NLA_UDP_MAX,
 	TIPC_NLA_UDP_MAX = __TIPC_NLA_UDP_MAX - 1
@@ -114,14 +105,6 @@
 	TIPC_NLA_SOCK_REF,		/* u32 */
 	TIPC_NLA_SOCK_CON,		/* nest */
 	TIPC_NLA_SOCK_HAS_PUBL,		/* flag */
-	TIPC_NLA_SOCK_STAT,		/* nest */
-	TIPC_NLA_SOCK_TYPE,		/* u32 */
-	TIPC_NLA_SOCK_INO,		/* u32 */
-	TIPC_NLA_SOCK_UID,		/* u32 */
-	TIPC_NLA_SOCK_TIPC_STATE,	/* u32 */
-	TIPC_NLA_SOCK_COOKIE,		/* u64 */
-	TIPC_NLA_SOCK_PAD,		/* flag */
-	TIPC_NLA_SOCK_GROUP,		/* nest */
 
 	__TIPC_NLA_SOCK_MAX,
 	TIPC_NLA_SOCK_MAX = __TIPC_NLA_SOCK_MAX - 1
@@ -170,8 +153,6 @@
 	TIPC_NLA_NET_UNSPEC,
 	TIPC_NLA_NET_ID,		/* u32 */
 	TIPC_NLA_NET_ADDR,		/* u32 */
-	TIPC_NLA_NET_NODEID,		/* u64 */
-	TIPC_NLA_NET_NODEID_W1,		/* u64 */
 
 	__TIPC_NLA_NET_MAX,
 	TIPC_NLA_NET_MAX = __TIPC_NLA_NET_MAX - 1
@@ -186,20 +167,6 @@
 	TIPC_NLA_NAME_TABLE_MAX = __TIPC_NLA_NAME_TABLE_MAX - 1
 };
 
-/* Monitor info */
-enum {
-	TIPC_NLA_MON_UNSPEC,
-	TIPC_NLA_MON_ACTIVATION_THRESHOLD,	/* u32 */
-	TIPC_NLA_MON_REF,			/* u32 */
-	TIPC_NLA_MON_ACTIVE,			/* flag */
-	TIPC_NLA_MON_BEARER_NAME,		/* string */
-	TIPC_NLA_MON_PEERCNT,			/* u32 */
-	TIPC_NLA_MON_LISTGEN,			/* u32 */
-
-	__TIPC_NLA_MON_MAX,
-	TIPC_NLA_MON_MAX = __TIPC_NLA_MON_MAX - 1
-};
-
 /* Publication info */
 enum {
 	TIPC_NLA_PUBL_UNSPEC,
@@ -216,37 +183,6 @@
 	TIPC_NLA_PUBL_MAX = __TIPC_NLA_PUBL_MAX - 1
 };
 
-/* Monitor peer info */
-enum {
-	TIPC_NLA_MON_PEER_UNSPEC,
-
-	TIPC_NLA_MON_PEER_ADDR,			/* u32 */
-	TIPC_NLA_MON_PEER_DOMGEN,		/* u32 */
-	TIPC_NLA_MON_PEER_APPLIED,		/* u32 */
-	TIPC_NLA_MON_PEER_UPMAP,		/* u64 */
-	TIPC_NLA_MON_PEER_MEMBERS,		/* tlv */
-	TIPC_NLA_MON_PEER_UP,			/* flag */
-	TIPC_NLA_MON_PEER_HEAD,			/* flag */
-	TIPC_NLA_MON_PEER_LOCAL,		/* flag */
-	TIPC_NLA_MON_PEER_PAD,			/* flag */
-
-	__TIPC_NLA_MON_PEER_MAX,
-	TIPC_NLA_MON_PEER_MAX = __TIPC_NLA_MON_PEER_MAX - 1
-};
-
-/* Nest, socket group info */
-enum {
-	TIPC_NLA_SOCK_GROUP_ID,			/* u32 */
-	TIPC_NLA_SOCK_GROUP_OPEN,		/* flag */
-	TIPC_NLA_SOCK_GROUP_NODE_SCOPE,		/* flag */
-	TIPC_NLA_SOCK_GROUP_CLUSTER_SCOPE,	/* flag */
-	TIPC_NLA_SOCK_GROUP_INSTANCE,		/* u32 */
-	TIPC_NLA_SOCK_GROUP_BC_SEND_NEXT,	/* u32 */
-
-	__TIPC_NLA_SOCK_GROUP_MAX,
-	TIPC_NLA_SOCK_GROUP_MAX = __TIPC_NLA_SOCK_GROUP_MAX - 1
-};
-
 /* Nest, connection info */
 enum {
 	TIPC_NLA_CON_UNSPEC,
@@ -261,18 +197,6 @@
 	TIPC_NLA_CON_MAX = __TIPC_NLA_CON_MAX - 1
 };
 
-/* Nest, socket statistics info */
-enum {
-	TIPC_NLA_SOCK_STAT_RCVQ,	/* u32 */
-	TIPC_NLA_SOCK_STAT_SENDQ,	/* u32 */
-	TIPC_NLA_SOCK_STAT_LINK_CONG,	/* flag */
-	TIPC_NLA_SOCK_STAT_CONN_CONG,	/* flag */
-	TIPC_NLA_SOCK_STAT_DROP,	/* u32 */
-
-	__TIPC_NLA_SOCK_STAT_MAX,
-	TIPC_NLA_SOCK_STAT_MAX = __TIPC_NLA_SOCK_STAT_MAX - 1
-};
-
 /* Nest, link propreties. Valid for link, media and bearer */
 enum {
 	TIPC_NLA_PROP_UNSPEC,
@@ -280,9 +204,6 @@
 	TIPC_NLA_PROP_PRIO,		/* u32 */
 	TIPC_NLA_PROP_TOL,		/* u32 */
 	TIPC_NLA_PROP_WIN,		/* u32 */
-	TIPC_NLA_PROP_MTU,		/* u32 */
-	TIPC_NLA_PROP_BROADCAST,	/* u32 */
-	TIPC_NLA_PROP_BROADCAST_RATIO,	/* u32 */
 
 	__TIPC_NLA_PROP_MAX,
 	TIPC_NLA_PROP_MAX = __TIPC_NLA_PROP_MAX - 1
diff --git a/include/uapi/linux/unix_diag.h b/include/linux/unix_diag.h
similarity index 89%
rename from include/uapi/linux/unix_diag.h
rename to include/linux/unix_diag.h
index a198857..1eb0b8d 100644
--- a/include/uapi/linux/unix_diag.h
+++ b/include/linux/unix_diag.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __UNIX_DIAG_H__
 #define __UNIX_DIAG_H__
 
@@ -20,7 +19,6 @@
 #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 */
-#define UDIAG_SHOW_UID		0x00000040	/* show socket's UID */
 
 struct unix_diag_msg {
 	__u8	udiag_family;
@@ -41,7 +39,6 @@
 	UNIX_DIAG_RQLEN,
 	UNIX_DIAG_MEMINFO,
 	UNIX_DIAG_SHUTDOWN,
-	UNIX_DIAG_UID,
 
 	__UNIX_DIAG_MAX,
 };
diff --git a/include/uapi/linux/veth.h b/include/linux/veth.h
similarity index 71%
rename from include/uapi/linux/veth.h
rename to include/linux/veth.h
index 52b58e5..3354c1e 100644
--- a/include/uapi/linux/veth.h
+++ b/include/linux/veth.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __NET_VETH_H_
 #define __NET_VETH_H_
 
diff --git a/include/uapi/linux/xfrm.h b/include/linux/xfrm.h
similarity index 95%
rename from include/uapi/linux/xfrm.h
rename to include/linux/xfrm.h
index 5cdda9d..b8f5451 100644
--- a/include/uapi/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _LINUX_XFRM_H
 #define _LINUX_XFRM_H
 
@@ -299,18 +298,12 @@
 	XFRMA_ALG_AUTH_TRUNC,	/* struct xfrm_algo_auth */
 	XFRMA_MARK,		/* struct xfrm_mark */
 	XFRMA_TFCPAD,		/* __u32 */
-	XFRMA_REPLAY_ESN_VAL,	/* struct xfrm_replay_state_esn */
+	XFRMA_REPLAY_ESN_VAL,	/* struct xfrm_replay_esn */
 	XFRMA_SA_EXTRA_FLAGS,	/* __u32 */
 	XFRMA_PROTO,		/* __u8 */
 	XFRMA_ADDRESS_FILTER,	/* struct xfrm_address_filter */
-	XFRMA_PAD,
-	XFRMA_OFFLOAD_DEV,	/* struct xfrm_state_offload */
-	XFRMA_SET_MARK,		/* __u32 */
-	XFRMA_SET_MARK_MASK,	/* __u32 */
-	XFRMA_IF_ID,		/* __u32 */
 	__XFRMA_MAX
 
-#define XFRMA_OUTPUT_MARK XFRMA_SET_MARK	/* Compatibility */
 #define XFRMA_MAX (__XFRMA_MAX - 1)
 };
 
@@ -500,13 +493,6 @@
 	__u8				dplen;
 };
 
-struct xfrm_user_offload {
-	int				ifindex;
-	__u8				flags;
-};
-#define XFRM_OFFLOAD_IPV6	1
-#define XFRM_OFFLOAD_INBOUND	2
-
 /* backwards compatibility for userspace */
 #define XFRMGRP_ACQUIRE		1
 #define XFRMGRP_EXPIRE		2
diff --git a/include/list.h b/include/list.h
deleted file mode 100644
index 5d86b13..0000000
--- a/include/list.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __LIST_H__
-#define __LIST_H__ 1
-/* List and hash list stuff from kernel */
-
-#include <stddef.h>
-
-#define container_of(ptr, type, member) ({			\
-	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
-	(type *)( (char *)__mptr - offsetof(type,member) );})
-
-struct list_head {
-	struct list_head *next, *prev;
-};
-
-static inline void INIT_LIST_HEAD(struct list_head *list)
-{
-	list->next = list;
-	list->prev = list;
-}
-
-static inline void __list_add(struct list_head *new,
-			      struct list_head *prev,
-			      struct list_head *next)
-{
-	next->prev = new;
-	new->next = next;
-	new->prev = prev;
-	prev->next = new;
-}
-
-static inline void list_add(struct list_head *new, struct list_head *head)
-{
-	__list_add(new, head, head->next);
-}
-
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
-{
-	__list_add(new, head->prev, head);
-}
-
-static inline void __list_del(struct list_head *prev, struct list_head *next)
-{
-	next->prev = prev;
-	prev->next = next;
-}
-
-static inline void list_del(struct list_head *entry)
-{
-	__list_del(entry->prev, entry->next);
-}
-
-#define list_entry(ptr, type, member) \
-	container_of(ptr, type, member)
-
-#define list_first_entry(ptr, type, member) \
-	list_entry((ptr)->next, type, member)
-
-#define list_last_entry(ptr, type, member) \
-	list_entry((ptr)->prev, type, member)
-
-#define list_next_entry(pos, member) \
-	list_entry((pos)->member.next, typeof(*(pos)), member)
-
-#define list_prev_entry(pos, member) \
-	list_entry((pos)->member.prev, typeof(*(pos)), member)
-
-#define list_for_each_entry(pos, head, member)				\
-	for (pos = list_first_entry(head, typeof(*pos), member);	\
-	     &pos->member != (head);					\
-	     pos = list_next_entry(pos, member))
-
-#define list_for_each_entry_safe(pos, n, head, member)			\
-	for (pos = list_first_entry(head, typeof(*pos), member),	\
-		n = list_next_entry(pos, member);			\
-	     &pos->member != (head);					\
-	     pos = n, n = list_next_entry(n, member))
-
-#define list_for_each_entry_reverse(pos, head, member)			\
-	for (pos = list_last_entry(head, typeof(*pos), member);		\
-	     &pos->member != (head);					\
-	     pos = list_prev_entry(pos, member))
-
-struct hlist_head {
-	struct hlist_node *first;
-};
-
-struct hlist_node {
-	struct hlist_node *next, **pprev;
-};
-
-static inline void hlist_del(struct hlist_node *n)
-{
-	struct hlist_node *next = n->next;
-	struct hlist_node **pprev = n->pprev;
-	*pprev = next;
-	if (next)
-		next->pprev = pprev;
-}
-
-static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
-{
-	struct hlist_node *first = h->first;
-	n->next = first;
-	if (first)
-		first->pprev = &n->next;
-	h->first = n;
-	n->pprev = &h->first;
-}
-
-static inline int list_empty(const struct list_head *head)
-{
-	return head->next == head;
-}
-
-#define hlist_for_each(pos, head) \
-	for (pos = (head)->first; pos ; pos = pos->next)
-
-
-#define hlist_for_each_safe(pos, n, head) \
-	for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
-	     pos = n)
-
-#define hlist_entry_safe(ptr, type, member) \
-	({ typeof(ptr) ____ptr = (ptr); \
-	   ____ptr ? hlist_entry(____ptr, type, member) : NULL; \
-	})
-
-#define hlist_for_each_entry(pos, head, member)				\
-	for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\
-	     pos;							\
-	     pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
-
-#endif /* __LIST_H__ */
diff --git a/include/ll_map.h b/include/ll_map.h
index 4de1041..949bfc3 100644
--- a/include/ll_map.h
+++ b/include/ll_map.h
@@ -1,17 +1,15 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef __LL_MAP_H__
 #define __LL_MAP_H__ 1
 
-int ll_remember_index(struct nlmsghdr *n, void *arg);
+int ll_remember_index(const struct sockaddr_nl *who,
+		      struct nlmsghdr *n, void *arg);
 
 void ll_init_map(struct rtnl_handle *rth);
 unsigned ll_name_to_index(const char *name);
 const char *ll_index_to_name(unsigned idx);
+const char *ll_idx_n2a(unsigned idx, char *buf);
 int ll_index_to_type(unsigned idx);
 int ll_index_to_flags(unsigned idx);
-void ll_drop_by_index(unsigned index);
 unsigned namehash(const char *str);
 
-const char *ll_idx_n2a(unsigned int idx);
-
 #endif /* __LL_MAP_H__ */
diff --git a/include/names.h b/include/names.h
index 2fcaacc..6fed581 100644
--- a/include/names.h
+++ b/include/names.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef DB_NAMES_H_
 #define DB_NAMES_H_ 1
 
@@ -22,5 +21,6 @@
 void db_names_free(struct db_names *db);
 
 char *id_to_name(struct db_names *db, int id, char *name);
+int name_to_id(struct db_names *db, int *id, const char *name);
 
 #endif
diff --git a/include/namespace.h b/include/namespace.h
index e47f9b5..51324b2 100644
--- a/include/namespace.h
+++ b/include/namespace.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef __NAMESPACE_H__
 #define __NAMESPACE_H__ 1
 
@@ -8,13 +7,8 @@
 #include <sys/syscall.h>
 #include <errno.h>
 
-#ifndef NETNS_RUN_DIR
 #define NETNS_RUN_DIR "/var/run/netns"
-#endif
-
-#ifndef NETNS_ETC_DIR
 #define NETNS_ETC_DIR "/etc/netns"
-#endif
 
 #ifndef CLONE_NEWNET
 #define CLONE_NEWNET 0x40000000	/* New network namespace (lo, device, names sockets, etc) */
diff --git a/include/rt_names.h b/include/rt_names.h
index 62ebbd6..921be06 100644
--- a/include/rt_names.h
+++ b/include/rt_names.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef RT_NAMES_H_
 #define RT_NAMES_H_ 1
 
diff --git a/include/rtm_map.h b/include/rtm_map.h
index f85e52c..d6e5885 100644
--- a/include/rtm_map.h
+++ b/include/rtm_map.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef __RTM_MAP_H__
 #define __RTM_MAP_H__ 1
 
diff --git a/include/uapi/asm-generic/sockios.h b/include/uapi/asm-generic/sockios.h
deleted file mode 100644
index 44fa3ed..0000000
--- a/include/uapi/asm-generic/sockios.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef __ASM_GENERIC_SOCKIOS_H
-#define __ASM_GENERIC_SOCKIOS_H
-
-/* Socket-level I/O control calls. */
-#define FIOSETOWN	0x8901
-#define SIOCSPGRP	0x8902
-#define FIOGETOWN	0x8903
-#define SIOCGPGRP	0x8904
-#define SIOCATMARK	0x8905
-#define SIOCGSTAMP_OLD	0x8906		/* Get stamp (timeval) */
-#define SIOCGSTAMPNS_OLD 0x8907		/* Get stamp (timespec) */
-
-#endif /* __ASM_GENERIC_SOCKIOS_H */
diff --git a/include/uapi/linux/atmarp.h b/include/uapi/linux/atmarp.h
deleted file mode 100644
index 8e44d12..0000000
--- a/include/uapi/linux/atmarp.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* atmarp.h - ATM ARP protocol and kernel-demon interface definitions */
- 
-/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
- 
-
-#ifndef _LINUX_ATMARP_H
-#define _LINUX_ATMARP_H
-
-#include <linux/types.h>
-#include <linux/atmapi.h>
-#include <linux/atmioc.h>
-
-
-#define ATMARP_RETRY_DELAY 30		/* request next resolution or forget
-					   NAK after 30 sec - should go into
-					   atmclip.h */
-#define ATMARP_MAX_UNRES_PACKETS 5	/* queue that many packets while
-					   waiting for the resolver */
-
-
-#define ATMARPD_CTRL	_IO('a',ATMIOC_CLIP+1)	/* become atmarpd ctrl sock */
-#define ATMARP_MKIP	_IO('a',ATMIOC_CLIP+2)	/* attach socket to IP */
-#define ATMARP_SETENTRY	_IO('a',ATMIOC_CLIP+3)	/* fill or hide ARP entry */
-#define ATMARP_ENCAP	_IO('a',ATMIOC_CLIP+5)	/* change encapsulation */
-
-
-enum atmarp_ctrl_type {
-	act_invalid,		/* catch uninitialized structures */
-	act_need,		/* need address resolution */
-	act_up,			/* interface is coming up */
-	act_down,		/* interface is going down */
-	act_change		/* interface configuration has changed */
-};
-
-struct atmarp_ctrl {
-	enum atmarp_ctrl_type	type;	/* message type */
-	int			itf_num;/* interface number (if present) */
-	__be32			ip;	/* IP address (act_need only) */
-};
-
-#endif
diff --git a/include/uapi/linux/atmdev.h b/include/uapi/linux/atmdev.h
deleted file mode 100644
index 9bdb96a..0000000
--- a/include/uapi/linux/atmdev.h
+++ /dev/null
@@ -1,216 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* atmdev.h - ATM device driver declarations and various related items */
- 
-/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
- 
-
-#ifndef LINUX_ATMDEV_H
-#define LINUX_ATMDEV_H
-
-
-#include <linux/atmapi.h>
-#include <linux/atm.h>
-#include <linux/atmioc.h>
-
-
-#define ESI_LEN		6
-
-#define ATM_OC3_PCR	(155520000/270*260/8/53)
-			/* OC3 link rate:  155520000 bps
-			   SONET overhead: /270*260 (9 section, 1 path)
-			   bits per cell:  /8/53
-			   max cell rate:  353207.547 cells/sec */
-#define ATM_25_PCR	((25600000/8-8000)/54)
-			/* 25 Mbps ATM cell rate (59111) */
-#define ATM_OC12_PCR	(622080000/1080*1040/8/53)
-			/* OC12 link rate: 622080000 bps
-			   SONET overhead: /1080*1040
-			   bits per cell:  /8/53
-			   max cell rate:  1412830.188 cells/sec */
-#define ATM_DS3_PCR	(8000*12)
-			/* DS3: 12 cells in a 125 usec time slot */
-
-
-#define __AAL_STAT_ITEMS \
-    __HANDLE_ITEM(tx);			/* TX okay */ \
-    __HANDLE_ITEM(tx_err);		/* TX errors */ \
-    __HANDLE_ITEM(rx);			/* RX okay */ \
-    __HANDLE_ITEM(rx_err);		/* RX errors */ \
-    __HANDLE_ITEM(rx_drop);		/* RX out of memory */
-
-struct atm_aal_stats {
-#define __HANDLE_ITEM(i) int i
-	__AAL_STAT_ITEMS
-#undef __HANDLE_ITEM
-};
-
-
-struct atm_dev_stats {
-	struct atm_aal_stats aal0;
-	struct atm_aal_stats aal34;
-	struct atm_aal_stats aal5;
-} __ATM_API_ALIGN;
-
-
-#define ATM_GETLINKRATE	_IOW('a',ATMIOC_ITF+1,struct atmif_sioc)
-					/* get link rate */
-#define ATM_GETNAMES	_IOW('a',ATMIOC_ITF+3,struct atm_iobuf)
-					/* get interface names (numbers) */
-#define ATM_GETTYPE	_IOW('a',ATMIOC_ITF+4,struct atmif_sioc)
-					/* get interface type name */
-#define ATM_GETESI	_IOW('a',ATMIOC_ITF+5,struct atmif_sioc)
-					/* get interface ESI */
-#define ATM_GETADDR	_IOW('a',ATMIOC_ITF+6,struct atmif_sioc)
-					/* get itf's local ATM addr. list */
-#define ATM_RSTADDR	_IOW('a',ATMIOC_ITF+7,struct atmif_sioc)
-					/* reset itf's ATM address list */
-#define ATM_ADDADDR	_IOW('a',ATMIOC_ITF+8,struct atmif_sioc)
-					/* add a local ATM address */
-#define ATM_DELADDR	_IOW('a',ATMIOC_ITF+9,struct atmif_sioc)
-					/* remove a local ATM address */
-#define ATM_GETCIRANGE	_IOW('a',ATMIOC_ITF+10,struct atmif_sioc)
-					/* get connection identifier range */
-#define ATM_SETCIRANGE	_IOW('a',ATMIOC_ITF+11,struct atmif_sioc)
-					/* set connection identifier range */
-#define ATM_SETESI	_IOW('a',ATMIOC_ITF+12,struct atmif_sioc)
-					/* set interface ESI */
-#define ATM_SETESIF	_IOW('a',ATMIOC_ITF+13,struct atmif_sioc)
-					/* force interface ESI */
-#define ATM_ADDLECSADDR	_IOW('a', ATMIOC_ITF+14, struct atmif_sioc)
-					/* register a LECS address */
-#define ATM_DELLECSADDR	_IOW('a', ATMIOC_ITF+15, struct atmif_sioc)
-					/* unregister a LECS address */
-#define ATM_GETLECSADDR	_IOW('a', ATMIOC_ITF+16, struct atmif_sioc)
-					/* retrieve LECS address(es) */
-
-#define ATM_GETSTAT	_IOW('a',ATMIOC_SARCOM+0,struct atmif_sioc)
-					/* get AAL layer statistics */
-#define ATM_GETSTATZ	_IOW('a',ATMIOC_SARCOM+1,struct atmif_sioc)
-					/* get AAL layer statistics and zero */
-#define ATM_GETLOOP	_IOW('a',ATMIOC_SARCOM+2,struct atmif_sioc)
-					/* get loopback mode */
-#define ATM_SETLOOP	_IOW('a',ATMIOC_SARCOM+3,struct atmif_sioc)
-					/* set loopback mode */
-#define ATM_QUERYLOOP	_IOW('a',ATMIOC_SARCOM+4,struct atmif_sioc)
-					/* query supported loopback modes */
-#define ATM_SETSC	_IOW('a',ATMIOC_SPECIAL+1,int)
-					/* enable or disable single-copy */
-#define ATM_SETBACKEND	_IOW('a',ATMIOC_SPECIAL+2,atm_backend_t)
-					/* set backend handler */
-#define ATM_NEWBACKENDIF _IOW('a',ATMIOC_SPECIAL+3,atm_backend_t)
-					/* use backend to make new if */
-#define ATM_ADDPARTY  	_IOW('a', ATMIOC_SPECIAL+4,struct atm_iobuf)
- 					/* add party to p2mp call */
-#ifdef CONFIG_COMPAT
-/* It actually takes struct sockaddr_atmsvc, not struct atm_iobuf */
-#define COMPAT_ATM_ADDPARTY  	_IOW('a', ATMIOC_SPECIAL+4,struct compat_atm_iobuf)
-#endif
-#define ATM_DROPPARTY 	_IOW('a', ATMIOC_SPECIAL+5,int)
-					/* drop party from p2mp call */
-
-/*
- * These are backend handkers that can be set via the ATM_SETBACKEND call
- * above.  In the future we may support dynamic loading of these - for now,
- * they're just being used to share the ATMIOC_BACKEND ioctls
- */
-#define ATM_BACKEND_RAW		0	
-#define ATM_BACKEND_PPP		1	/* PPPoATM - RFC2364 */
-#define ATM_BACKEND_BR2684	2	/* Bridged RFC1483/2684 */
-
-/* for ATM_GETTYPE */
-#define ATM_ITFTYP_LEN	8	/* maximum length of interface type name */
-
-/*
- * Loopback modes for ATM_{PHY,SAR}_{GET,SET}LOOP
- */
-
-/* Point of loopback				CPU-->SAR-->PHY-->line--> ... */
-#define __ATM_LM_NONE	0	/* no loop back     ^     ^     ^      ^      */
-#define __ATM_LM_AAL	1	/* loop back PDUs --'     |     |      |      */
-#define __ATM_LM_ATM	2	/* loop back ATM cells ---'     |      |      */
-/* RESERVED		4	loop back on PHY side  ---'		      */
-#define __ATM_LM_PHY	8	/* loop back bits (digital) ----'      |      */
-#define __ATM_LM_ANALOG 16	/* loop back the analog signal --------'      */
-
-/* Direction of loopback */
-#define __ATM_LM_MKLOC(n)	((n))	    /* Local (i.e. loop TX to RX) */
-#define __ATM_LM_MKRMT(n)	((n) << 8)  /* Remote (i.e. loop RX to TX) */
-
-#define __ATM_LM_XTLOC(n)	((n) & 0xff)
-#define __ATM_LM_XTRMT(n)	(((n) >> 8) & 0xff)
-
-#define ATM_LM_NONE	0	/* no loopback */
-
-#define ATM_LM_LOC_AAL	__ATM_LM_MKLOC(__ATM_LM_AAL)
-#define ATM_LM_LOC_ATM	__ATM_LM_MKLOC(__ATM_LM_ATM)
-#define ATM_LM_LOC_PHY	__ATM_LM_MKLOC(__ATM_LM_PHY)
-#define ATM_LM_LOC_ANALOG __ATM_LM_MKLOC(__ATM_LM_ANALOG)
-
-#define ATM_LM_RMT_AAL	__ATM_LM_MKRMT(__ATM_LM_AAL)
-#define ATM_LM_RMT_ATM	__ATM_LM_MKRMT(__ATM_LM_ATM)
-#define ATM_LM_RMT_PHY	__ATM_LM_MKRMT(__ATM_LM_PHY)
-#define ATM_LM_RMT_ANALOG __ATM_LM_MKRMT(__ATM_LM_ANALOG)
-
-/*
- * Note: ATM_LM_LOC_* and ATM_LM_RMT_* can be combined, provided that
- * __ATM_LM_XTLOC(x) <= __ATM_LM_XTRMT(x)
- */
-
-
-struct atm_iobuf {
-	int length;
-	void *buffer;
-};
-
-/* for ATM_GETCIRANGE / ATM_SETCIRANGE */
-
-#define ATM_CI_MAX      -1              /* use maximum range of VPI/VCI */
- 
-struct atm_cirange {
-	signed char	vpi_bits;	/* 1..8, ATM_CI_MAX (-1) for maximum */
-	signed char	vci_bits;	/* 1..16, ATM_CI_MAX (-1) for maximum */
-};
-
-/* for ATM_SETSC; actually taken from the ATM_VF number space */
-
-#define ATM_SC_RX	1024		/* enable RX single-copy */
-#define ATM_SC_TX	2048		/* enable TX single-copy */
-
-#define ATM_BACKLOG_DEFAULT 32 /* if we get more, we're likely to time out
-				  anyway */
-
-/* MF: change_qos (Modify) flags */
-
-#define ATM_MF_IMMED	 1	/* Block until change is effective */
-#define ATM_MF_INC_RSV	 2	/* Change reservation on increase */
-#define ATM_MF_INC_SHP	 4	/* Change shaping on increase */
-#define ATM_MF_DEC_RSV	 8	/* Change reservation on decrease */
-#define ATM_MF_DEC_SHP	16	/* Change shaping on decrease */
-#define ATM_MF_BWD	32	/* Set the backward direction parameters */
-
-#define ATM_MF_SET	(ATM_MF_INC_RSV | ATM_MF_INC_SHP | ATM_MF_DEC_RSV | \
-			  ATM_MF_DEC_SHP | ATM_MF_BWD)
-
-/*
- * ATM_VS_* are used to express VC state in a human-friendly way.
- */
-
-#define ATM_VS_IDLE	0	/* VC is not used */
-#define ATM_VS_CONNECTED 1	/* VC is connected */
-#define ATM_VS_CLOSING	2	/* VC is closing */
-#define ATM_VS_LISTEN	3	/* VC is listening for incoming setups */
-#define ATM_VS_INUSE	4	/* VC is in use (registered with atmsigd) */
-#define ATM_VS_BOUND	5	/* VC is bound */
-
-#define ATM_VS2TXT_MAP \
-    "IDLE", "CONNECTED", "CLOSING", "LISTEN", "INUSE", "BOUND"
-
-#define ATM_VF2TXT_MAP \
-    "ADDR",	"READY",	"PARTIAL",	"REGIS", \
-    "RELEASED", "HASQOS",	"LISTEN",	"META", \
-    "256",	"512",		"1024",		"2048", \
-    "SESSION",	"HASSAP",	"BOUND",	"CLOSE"
-
-
-
-#endif /* LINUX_ATMDEV_H */
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
deleted file mode 100644
index f7a7553..0000000
--- a/include/uapi/linux/bpf.h
+++ /dev/null
@@ -1,3616 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- */
-#ifndef __LINUX_BPF_H__
-#define __LINUX_BPF_H__
-
-#include <linux/types.h>
-#include <linux/bpf_common.h>
-
-/* Extended instruction set based on top of classic BPF */
-
-/* instruction classes */
-#define BPF_JMP32	0x06	/* jmp mode in word width */
-#define BPF_ALU64	0x07	/* alu mode in double word width */
-
-/* ld/ldx fields */
-#define BPF_DW		0x18	/* double word (64-bit) */
-#define BPF_XADD	0xc0	/* exclusive add */
-
-/* alu/jmp fields */
-#define BPF_MOV		0xb0	/* mov reg to reg */
-#define BPF_ARSH	0xc0	/* sign extending arithmetic shift right */
-
-/* change endianness of a register */
-#define BPF_END		0xd0	/* flags for endianness conversion: */
-#define BPF_TO_LE	0x00	/* convert to little-endian */
-#define BPF_TO_BE	0x08	/* convert to big-endian */
-#define BPF_FROM_LE	BPF_TO_LE
-#define BPF_FROM_BE	BPF_TO_BE
-
-/* jmp encodings */
-#define BPF_JNE		0x50	/* jump != */
-#define BPF_JLT		0xa0	/* LT is unsigned, '<' */
-#define BPF_JLE		0xb0	/* LE is unsigned, '<=' */
-#define BPF_JSGT	0x60	/* SGT is signed '>', GT in x86 */
-#define BPF_JSGE	0x70	/* SGE is signed '>=', GE in x86 */
-#define BPF_JSLT	0xc0	/* SLT is signed, '<' */
-#define BPF_JSLE	0xd0	/* SLE is signed, '<=' */
-#define BPF_CALL	0x80	/* function call */
-#define BPF_EXIT	0x90	/* function return */
-
-/* Register numbers */
-enum {
-	BPF_REG_0 = 0,
-	BPF_REG_1,
-	BPF_REG_2,
-	BPF_REG_3,
-	BPF_REG_4,
-	BPF_REG_5,
-	BPF_REG_6,
-	BPF_REG_7,
-	BPF_REG_8,
-	BPF_REG_9,
-	BPF_REG_10,
-	__MAX_BPF_REG,
-};
-
-/* BPF has 10 general purpose 64-bit registers and stack frame. */
-#define MAX_BPF_REG	__MAX_BPF_REG
-
-struct bpf_insn {
-	__u8	code;		/* opcode */
-	__u8	dst_reg:4;	/* dest register */
-	__u8	src_reg:4;	/* source register */
-	__s16	off;		/* signed offset */
-	__s32	imm;		/* signed immediate constant */
-};
-
-/* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */
-struct bpf_lpm_trie_key {
-	__u32	prefixlen;	/* up to 32 for AF_INET, 128 for AF_INET6 */
-	__u8	data[0];	/* Arbitrary size */
-};
-
-struct bpf_cgroup_storage_key {
-	__u64	cgroup_inode_id;	/* cgroup inode id */
-	__u32	attach_type;		/* program attach type */
-};
-
-/* BPF syscall commands, see bpf(2) man-page for details. */
-enum bpf_cmd {
-	BPF_MAP_CREATE,
-	BPF_MAP_LOOKUP_ELEM,
-	BPF_MAP_UPDATE_ELEM,
-	BPF_MAP_DELETE_ELEM,
-	BPF_MAP_GET_NEXT_KEY,
-	BPF_PROG_LOAD,
-	BPF_OBJ_PIN,
-	BPF_OBJ_GET,
-	BPF_PROG_ATTACH,
-	BPF_PROG_DETACH,
-	BPF_PROG_TEST_RUN,
-	BPF_PROG_GET_NEXT_ID,
-	BPF_MAP_GET_NEXT_ID,
-	BPF_PROG_GET_FD_BY_ID,
-	BPF_MAP_GET_FD_BY_ID,
-	BPF_OBJ_GET_INFO_BY_FD,
-	BPF_PROG_QUERY,
-	BPF_RAW_TRACEPOINT_OPEN,
-	BPF_BTF_LOAD,
-	BPF_BTF_GET_FD_BY_ID,
-	BPF_TASK_FD_QUERY,
-	BPF_MAP_LOOKUP_AND_DELETE_ELEM,
-	BPF_MAP_FREEZE,
-	BPF_BTF_GET_NEXT_ID,
-};
-
-enum bpf_map_type {
-	BPF_MAP_TYPE_UNSPEC,
-	BPF_MAP_TYPE_HASH,
-	BPF_MAP_TYPE_ARRAY,
-	BPF_MAP_TYPE_PROG_ARRAY,
-	BPF_MAP_TYPE_PERF_EVENT_ARRAY,
-	BPF_MAP_TYPE_PERCPU_HASH,
-	BPF_MAP_TYPE_PERCPU_ARRAY,
-	BPF_MAP_TYPE_STACK_TRACE,
-	BPF_MAP_TYPE_CGROUP_ARRAY,
-	BPF_MAP_TYPE_LRU_HASH,
-	BPF_MAP_TYPE_LRU_PERCPU_HASH,
-	BPF_MAP_TYPE_LPM_TRIE,
-	BPF_MAP_TYPE_ARRAY_OF_MAPS,
-	BPF_MAP_TYPE_HASH_OF_MAPS,
-	BPF_MAP_TYPE_DEVMAP,
-	BPF_MAP_TYPE_SOCKMAP,
-	BPF_MAP_TYPE_CPUMAP,
-	BPF_MAP_TYPE_XSKMAP,
-	BPF_MAP_TYPE_SOCKHASH,
-	BPF_MAP_TYPE_CGROUP_STORAGE,
-	BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
-	BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
-	BPF_MAP_TYPE_QUEUE,
-	BPF_MAP_TYPE_STACK,
-	BPF_MAP_TYPE_SK_STORAGE,
-	BPF_MAP_TYPE_DEVMAP_HASH,
-};
-
-/* Note that tracing related programs such as
- * BPF_PROG_TYPE_{KPROBE,TRACEPOINT,PERF_EVENT,RAW_TRACEPOINT}
- * are not subject to a stable API since kernel internal data
- * structures can change from release to release and may
- * therefore break existing tracing BPF programs. Tracing BPF
- * programs correspond to /a/ specific kernel which is to be
- * analyzed, and not /a/ specific kernel /and/ all future ones.
- */
-enum bpf_prog_type {
-	BPF_PROG_TYPE_UNSPEC,
-	BPF_PROG_TYPE_SOCKET_FILTER,
-	BPF_PROG_TYPE_KPROBE,
-	BPF_PROG_TYPE_SCHED_CLS,
-	BPF_PROG_TYPE_SCHED_ACT,
-	BPF_PROG_TYPE_TRACEPOINT,
-	BPF_PROG_TYPE_XDP,
-	BPF_PROG_TYPE_PERF_EVENT,
-	BPF_PROG_TYPE_CGROUP_SKB,
-	BPF_PROG_TYPE_CGROUP_SOCK,
-	BPF_PROG_TYPE_LWT_IN,
-	BPF_PROG_TYPE_LWT_OUT,
-	BPF_PROG_TYPE_LWT_XMIT,
-	BPF_PROG_TYPE_SOCK_OPS,
-	BPF_PROG_TYPE_SK_SKB,
-	BPF_PROG_TYPE_CGROUP_DEVICE,
-	BPF_PROG_TYPE_SK_MSG,
-	BPF_PROG_TYPE_RAW_TRACEPOINT,
-	BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
-	BPF_PROG_TYPE_LWT_SEG6LOCAL,
-	BPF_PROG_TYPE_LIRC_MODE2,
-	BPF_PROG_TYPE_SK_REUSEPORT,
-	BPF_PROG_TYPE_FLOW_DISSECTOR,
-	BPF_PROG_TYPE_CGROUP_SYSCTL,
-	BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
-	BPF_PROG_TYPE_CGROUP_SOCKOPT,
-};
-
-enum bpf_attach_type {
-	BPF_CGROUP_INET_INGRESS,
-	BPF_CGROUP_INET_EGRESS,
-	BPF_CGROUP_INET_SOCK_CREATE,
-	BPF_CGROUP_SOCK_OPS,
-	BPF_SK_SKB_STREAM_PARSER,
-	BPF_SK_SKB_STREAM_VERDICT,
-	BPF_CGROUP_DEVICE,
-	BPF_SK_MSG_VERDICT,
-	BPF_CGROUP_INET4_BIND,
-	BPF_CGROUP_INET6_BIND,
-	BPF_CGROUP_INET4_CONNECT,
-	BPF_CGROUP_INET6_CONNECT,
-	BPF_CGROUP_INET4_POST_BIND,
-	BPF_CGROUP_INET6_POST_BIND,
-	BPF_CGROUP_UDP4_SENDMSG,
-	BPF_CGROUP_UDP6_SENDMSG,
-	BPF_LIRC_MODE2,
-	BPF_FLOW_DISSECTOR,
-	BPF_CGROUP_SYSCTL,
-	BPF_CGROUP_UDP4_RECVMSG,
-	BPF_CGROUP_UDP6_RECVMSG,
-	BPF_CGROUP_GETSOCKOPT,
-	BPF_CGROUP_SETSOCKOPT,
-	__MAX_BPF_ATTACH_TYPE
-};
-
-#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
-
-/* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
- *
- * NONE(default): No further bpf programs allowed in the subtree.
- *
- * BPF_F_ALLOW_OVERRIDE: If a sub-cgroup installs some bpf program,
- * the program in this cgroup yields to sub-cgroup program.
- *
- * BPF_F_ALLOW_MULTI: If a sub-cgroup installs some bpf program,
- * that cgroup program gets run in addition to the program in this cgroup.
- *
- * Only one program is allowed to be attached to a cgroup with
- * NONE or BPF_F_ALLOW_OVERRIDE flag.
- * Attaching another program on top of NONE or BPF_F_ALLOW_OVERRIDE will
- * release old program and attach the new one. Attach flags has to match.
- *
- * Multiple programs are allowed to be attached to a cgroup with
- * BPF_F_ALLOW_MULTI flag. They are executed in FIFO order
- * (those that were attached first, run first)
- * The programs of sub-cgroup are executed first, then programs of
- * this cgroup and then programs of parent cgroup.
- * When children program makes decision (like picking TCP CA or sock bind)
- * parent program has a chance to override it.
- *
- * A cgroup with MULTI or OVERRIDE flag allows any attach flags in sub-cgroups.
- * A cgroup with NONE doesn't allow any programs in sub-cgroups.
- * Ex1:
- * cgrp1 (MULTI progs A, B) ->
- *    cgrp2 (OVERRIDE prog C) ->
- *      cgrp3 (MULTI prog D) ->
- *        cgrp4 (OVERRIDE prog E) ->
- *          cgrp5 (NONE prog F)
- * the event in cgrp5 triggers execution of F,D,A,B in that order.
- * if prog F is detached, the execution is E,D,A,B
- * if prog F and D are detached, the execution is E,A,B
- * if prog F, E and D are detached, the execution is C,A,B
- *
- * All eligible programs are executed regardless of return code from
- * earlier programs.
- */
-#define BPF_F_ALLOW_OVERRIDE	(1U << 0)
-#define BPF_F_ALLOW_MULTI	(1U << 1)
-
-/* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the
- * verifier will perform strict alignment checking as if the kernel
- * has been built with CONFIG_EFFICIENT_UNALIGNED_ACCESS not set,
- * and NET_IP_ALIGN defined to 2.
- */
-#define BPF_F_STRICT_ALIGNMENT	(1U << 0)
-
-/* If BPF_F_ANY_ALIGNMENT is used in BPF_PROF_LOAD command, the
- * verifier will allow any alignment whatsoever.  On platforms
- * with strict alignment requirements for loads ands stores (such
- * as sparc and mips) the verifier validates that all loads and
- * stores provably follow this requirement.  This flag turns that
- * checking and enforcement off.
- *
- * It is mostly used for testing when we want to validate the
- * context and memory access aspects of the verifier, but because
- * of an unaligned access the alignment check would trigger before
- * the one we are interested in.
- */
-#define BPF_F_ANY_ALIGNMENT	(1U << 1)
-
-/* BPF_F_TEST_RND_HI32 is used in BPF_PROG_LOAD command for testing purpose.
- * Verifier does sub-register def/use analysis and identifies instructions whose
- * def only matters for low 32-bit, high 32-bit is never referenced later
- * through implicit zero extension. Therefore verifier notifies JIT back-ends
- * that it is safe to ignore clearing high 32-bit for these instructions. This
- * saves some back-ends a lot of code-gen. However such optimization is not
- * necessary on some arches, for example x86_64, arm64 etc, whose JIT back-ends
- * hence hasn't used verifier's analysis result. But, we really want to have a
- * way to be able to verify the correctness of the described optimization on
- * x86_64 on which testsuites are frequently exercised.
- *
- * So, this flag is introduced. Once it is set, verifier will randomize high
- * 32-bit for those instructions who has been identified as safe to ignore them.
- * Then, if verifier is not doing correct analysis, such randomization will
- * regress tests to expose bugs.
- */
-#define BPF_F_TEST_RND_HI32	(1U << 2)
-
-/* The verifier internal test flag. Behavior is undefined */
-#define BPF_F_TEST_STATE_FREQ	(1U << 3)
-
-/* When BPF ldimm64's insn[0].src_reg != 0 then this can have
- * two extensions:
- *
- * insn[0].src_reg:  BPF_PSEUDO_MAP_FD   BPF_PSEUDO_MAP_VALUE
- * insn[0].imm:      map fd              map fd
- * insn[1].imm:      0                   offset into value
- * insn[0].off:      0                   0
- * insn[1].off:      0                   0
- * ldimm64 rewrite:  address of map      address of map[0]+offset
- * verifier type:    CONST_PTR_TO_MAP    PTR_TO_MAP_VALUE
- */
-#define BPF_PSEUDO_MAP_FD	1
-#define BPF_PSEUDO_MAP_VALUE	2
-
-/* when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative
- * offset to another bpf function
- */
-#define BPF_PSEUDO_CALL		1
-
-/* flags for BPF_MAP_UPDATE_ELEM command */
-#define BPF_ANY		0 /* create new element or update existing */
-#define BPF_NOEXIST	1 /* create new element if it didn't exist */
-#define BPF_EXIST	2 /* update existing element */
-#define BPF_F_LOCK	4 /* spin_lock-ed map_lookup/map_update */
-
-/* flags for BPF_MAP_CREATE command */
-#define BPF_F_NO_PREALLOC	(1U << 0)
-/* Instead of having one common LRU list in the
- * BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list
- * which can scale and perform better.
- * Note, the LRU nodes (including free nodes) cannot be moved
- * across different LRU lists.
- */
-#define BPF_F_NO_COMMON_LRU	(1U << 1)
-/* Specify numa node during map creation */
-#define BPF_F_NUMA_NODE		(1U << 2)
-
-#define BPF_OBJ_NAME_LEN 16U
-
-/* Flags for accessing BPF object from syscall side. */
-#define BPF_F_RDONLY		(1U << 3)
-#define BPF_F_WRONLY		(1U << 4)
-
-/* Flag for stack_map, store build_id+offset instead of pointer */
-#define BPF_F_STACK_BUILD_ID	(1U << 5)
-
-/* Zero-initialize hash function seed. This should only be used for testing. */
-#define BPF_F_ZERO_SEED		(1U << 6)
-
-/* Flags for accessing BPF object from program side. */
-#define BPF_F_RDONLY_PROG	(1U << 7)
-#define BPF_F_WRONLY_PROG	(1U << 8)
-
-/* Clone map from listener for newly accepted socket */
-#define BPF_F_CLONE		(1U << 9)
-
-/* flags for BPF_PROG_QUERY */
-#define BPF_F_QUERY_EFFECTIVE	(1U << 0)
-
-enum bpf_stack_build_id_status {
-	/* user space need an empty entry to identify end of a trace */
-	BPF_STACK_BUILD_ID_EMPTY = 0,
-	/* with valid build_id and offset */
-	BPF_STACK_BUILD_ID_VALID = 1,
-	/* couldn't get build_id, fallback to ip */
-	BPF_STACK_BUILD_ID_IP = 2,
-};
-
-#define BPF_BUILD_ID_SIZE 20
-struct bpf_stack_build_id {
-	__s32		status;
-	unsigned char	build_id[BPF_BUILD_ID_SIZE];
-	union {
-		__u64	offset;
-		__u64	ip;
-	};
-};
-
-union bpf_attr {
-	struct { /* anonymous struct used by BPF_MAP_CREATE command */
-		__u32	map_type;	/* one of enum bpf_map_type */
-		__u32	key_size;	/* size of key in bytes */
-		__u32	value_size;	/* size of value in bytes */
-		__u32	max_entries;	/* max number of entries in a map */
-		__u32	map_flags;	/* BPF_MAP_CREATE related
-					 * flags defined above.
-					 */
-		__u32	inner_map_fd;	/* fd pointing to the inner map */
-		__u32	numa_node;	/* numa node (effective only if
-					 * BPF_F_NUMA_NODE is set).
-					 */
-		char	map_name[BPF_OBJ_NAME_LEN];
-		__u32	map_ifindex;	/* ifindex of netdev to create on */
-		__u32	btf_fd;		/* fd pointing to a BTF type data */
-		__u32	btf_key_type_id;	/* BTF type_id of the key */
-		__u32	btf_value_type_id;	/* BTF type_id of the value */
-	};
-
-	struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
-		__u32		map_fd;
-		__aligned_u64	key;
-		union {
-			__aligned_u64 value;
-			__aligned_u64 next_key;
-		};
-		__u64		flags;
-	};
-
-	struct { /* anonymous struct used by BPF_PROG_LOAD command */
-		__u32		prog_type;	/* one of enum bpf_prog_type */
-		__u32		insn_cnt;
-		__aligned_u64	insns;
-		__aligned_u64	license;
-		__u32		log_level;	/* verbosity level of verifier */
-		__u32		log_size;	/* size of user buffer */
-		__aligned_u64	log_buf;	/* user supplied buffer */
-		__u32		kern_version;	/* not used */
-		__u32		prog_flags;
-		char		prog_name[BPF_OBJ_NAME_LEN];
-		__u32		prog_ifindex;	/* ifindex of netdev to prep for */
-		/* For some prog types expected attach type must be known at
-		 * load time to verify attach type specific parts of prog
-		 * (context accesses, allowed helpers, etc).
-		 */
-		__u32		expected_attach_type;
-		__u32		prog_btf_fd;	/* fd pointing to BTF type data */
-		__u32		func_info_rec_size;	/* userspace bpf_func_info size */
-		__aligned_u64	func_info;	/* func info */
-		__u32		func_info_cnt;	/* number of bpf_func_info records */
-		__u32		line_info_rec_size;	/* userspace bpf_line_info size */
-		__aligned_u64	line_info;	/* line info */
-		__u32		line_info_cnt;	/* number of bpf_line_info records */
-	};
-
-	struct { /* anonymous struct used by BPF_OBJ_* commands */
-		__aligned_u64	pathname;
-		__u32		bpf_fd;
-		__u32		file_flags;
-	};
-
-	struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
-		__u32		target_fd;	/* container object to attach to */
-		__u32		attach_bpf_fd;	/* eBPF program to attach */
-		__u32		attach_type;
-		__u32		attach_flags;
-	};
-
-	struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */
-		__u32		prog_fd;
-		__u32		retval;
-		__u32		data_size_in;	/* input: len of data_in */
-		__u32		data_size_out;	/* input/output: len of data_out
-						 *   returns ENOSPC if data_out
-						 *   is too small.
-						 */
-		__aligned_u64	data_in;
-		__aligned_u64	data_out;
-		__u32		repeat;
-		__u32		duration;
-		__u32		ctx_size_in;	/* input: len of ctx_in */
-		__u32		ctx_size_out;	/* input/output: len of ctx_out
-						 *   returns ENOSPC if ctx_out
-						 *   is too small.
-						 */
-		__aligned_u64	ctx_in;
-		__aligned_u64	ctx_out;
-	} test;
-
-	struct { /* anonymous struct used by BPF_*_GET_*_ID */
-		union {
-			__u32		start_id;
-			__u32		prog_id;
-			__u32		map_id;
-			__u32		btf_id;
-		};
-		__u32		next_id;
-		__u32		open_flags;
-	};
-
-	struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
-		__u32		bpf_fd;
-		__u32		info_len;
-		__aligned_u64	info;
-	} info;
-
-	struct { /* anonymous struct used by BPF_PROG_QUERY command */
-		__u32		target_fd;	/* container object to query */
-		__u32		attach_type;
-		__u32		query_flags;
-		__u32		attach_flags;
-		__aligned_u64	prog_ids;
-		__u32		prog_cnt;
-	} query;
-
-	struct {
-		__u64 name;
-		__u32 prog_fd;
-	} raw_tracepoint;
-
-	struct { /* anonymous struct for BPF_BTF_LOAD */
-		__aligned_u64	btf;
-		__aligned_u64	btf_log_buf;
-		__u32		btf_size;
-		__u32		btf_log_size;
-		__u32		btf_log_level;
-	};
-
-	struct {
-		__u32		pid;		/* input: pid */
-		__u32		fd;		/* input: fd */
-		__u32		flags;		/* input: flags */
-		__u32		buf_len;	/* input/output: buf len */
-		__aligned_u64	buf;		/* input/output:
-						 *   tp_name for tracepoint
-						 *   symbol for kprobe
-						 *   filename for uprobe
-						 */
-		__u32		prog_id;	/* output: prod_id */
-		__u32		fd_type;	/* output: BPF_FD_TYPE_* */
-		__u64		probe_offset;	/* output: probe_offset */
-		__u64		probe_addr;	/* output: probe_addr */
-	} task_fd_query;
-} __attribute__((aligned(8)));
-
-/* The description below is an attempt at providing documentation to eBPF
- * developers about the multiple available eBPF helper functions. It can be
- * parsed and used to produce a manual page. The workflow is the following,
- * and requires the rst2man utility:
- *
- *     $ ./scripts/bpf_helpers_doc.py \
- *             --filename include/uapi/linux/bpf.h > /tmp/bpf-helpers.rst
- *     $ rst2man /tmp/bpf-helpers.rst > /tmp/bpf-helpers.7
- *     $ man /tmp/bpf-helpers.7
- *
- * Note that in order to produce this external documentation, some RST
- * formatting is used in the descriptions to get "bold" and "italics" in
- * manual pages. Also note that the few trailing white spaces are
- * intentional, removing them would break paragraphs for rst2man.
- *
- * Start of BPF helper function descriptions:
- *
- * void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)
- * 	Description
- * 		Perform a lookup in *map* for an entry associated to *key*.
- * 	Return
- * 		Map value associated to *key*, or **NULL** if no entry was
- * 		found.
- *
- * int bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags)
- * 	Description
- * 		Add or update the value of the entry associated to *key* in
- * 		*map* with *value*. *flags* is one of:
- *
- * 		**BPF_NOEXIST**
- * 			The entry for *key* must not exist in the map.
- * 		**BPF_EXIST**
- * 			The entry for *key* must already exist in the map.
- * 		**BPF_ANY**
- * 			No condition on the existence of the entry for *key*.
- *
- * 		Flag value **BPF_NOEXIST** cannot be used for maps of types
- * 		**BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY**  (all
- * 		elements always exist), the helper would return an error.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_map_delete_elem(struct bpf_map *map, const void *key)
- * 	Description
- * 		Delete entry with *key* from *map*.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_probe_read(void *dst, u32 size, const void *src)
- * 	Description
- * 		For tracing programs, safely attempt to read *size* bytes from
- * 		address *src* and store the data in *dst*.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * u64 bpf_ktime_get_ns(void)
- * 	Description
- * 		Return the time elapsed since system boot, in nanoseconds.
- * 	Return
- * 		Current *ktime*.
- *
- * int bpf_trace_printk(const char *fmt, u32 fmt_size, ...)
- * 	Description
- * 		This helper is a "printk()-like" facility for debugging. It
- * 		prints a message defined by format *fmt* (of size *fmt_size*)
- * 		to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if
- * 		available. It can take up to three additional **u64**
- * 		arguments (as an eBPF helpers, the total number of arguments is
- * 		limited to five).
- *
- * 		Each time the helper is called, it appends a line to the trace.
- * 		Lines are discarded while *\/sys/kernel/debug/tracing/trace* is
- * 		open, use *\/sys/kernel/debug/tracing/trace_pipe* to avoid this.
- * 		The format of the trace is customizable, and the exact output
- * 		one will get depends on the options set in
- * 		*\/sys/kernel/debug/tracing/trace_options* (see also the
- * 		*README* file under the same directory). However, it usually
- * 		defaults to something like:
- *
- * 		::
- *
- * 			telnet-470   [001] .N.. 419421.045894: 0x00000001: <formatted msg>
- *
- * 		In the above:
- *
- * 			* ``telnet`` is the name of the current task.
- * 			* ``470`` is the PID of the current task.
- * 			* ``001`` is the CPU number on which the task is
- * 			  running.
- * 			* In ``.N..``, each character refers to a set of
- * 			  options (whether irqs are enabled, scheduling
- * 			  options, whether hard/softirqs are running, level of
- * 			  preempt_disabled respectively). **N** means that
- * 			  **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED**
- * 			  are set.
- * 			* ``419421.045894`` is a timestamp.
- * 			* ``0x00000001`` is a fake value used by BPF for the
- * 			  instruction pointer register.
- * 			* ``<formatted msg>`` is the message formatted with
- * 			  *fmt*.
- *
- * 		The conversion specifiers supported by *fmt* are similar, but
- * 		more limited than for printk(). They are **%d**, **%i**,
- * 		**%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**,
- * 		**%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size
- * 		of field, padding with zeroes, etc.) is available, and the
- * 		helper will return **-EINVAL** (but print nothing) if it
- * 		encounters an unknown specifier.
- *
- * 		Also, note that **bpf_trace_printk**\ () is slow, and should
- * 		only be used for debugging purposes. For this reason, a notice
- * 		bloc (spanning several lines) is printed to kernel logs and
- * 		states that the helper should not be used "for production use"
- * 		the first time this helper is used (or more precisely, when
- * 		**trace_printk**\ () buffers are allocated). For passing values
- * 		to user space, perf events should be preferred.
- * 	Return
- * 		The number of bytes written to the buffer, or a negative error
- * 		in case of failure.
- *
- * u32 bpf_get_prandom_u32(void)
- * 	Description
- * 		Get a pseudo-random number.
- *
- * 		From a security point of view, this helper uses its own
- * 		pseudo-random internal state, and cannot be used to infer the
- * 		seed of other random functions in the kernel. However, it is
- * 		essential to note that the generator used by the helper is not
- * 		cryptographically secure.
- * 	Return
- * 		A random 32-bit unsigned value.
- *
- * u32 bpf_get_smp_processor_id(void)
- * 	Description
- * 		Get the SMP (symmetric multiprocessing) processor id. Note that
- * 		all programs run with preemption disabled, which means that the
- * 		SMP processor id is stable during all the execution of the
- * 		program.
- * 	Return
- * 		The SMP id of the processor running the program.
- *
- * int bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len, u64 flags)
- * 	Description
- * 		Store *len* bytes from address *from* into the packet
- * 		associated to *skb*, at *offset*. *flags* are a combination of
- * 		**BPF_F_RECOMPUTE_CSUM** (automatically recompute the
- * 		checksum for the packet after storing the bytes) and
- * 		**BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\
- * 		**->swhash** and *skb*\ **->l4hash** to 0).
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_l3_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 size)
- * 	Description
- * 		Recompute the layer 3 (e.g. IP) checksum for the packet
- * 		associated to *skb*. Computation is incremental, so the helper
- * 		must know the former value of the header field that was
- * 		modified (*from*), the new value of this field (*to*), and the
- * 		number of bytes (2 or 4) for this field, stored in *size*.
- * 		Alternatively, it is possible to store the difference between
- * 		the previous and the new values of the header field in *to*, by
- * 		setting *from* and *size* to 0. For both methods, *offset*
- * 		indicates the location of the IP checksum within the packet.
- *
- * 		This helper works in combination with **bpf_csum_diff**\ (),
- * 		which does not update the checksum in-place, but offers more
- * 		flexibility and can handle sizes larger than 2 or 4 for the
- * 		checksum to update.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_l4_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 flags)
- * 	Description
- * 		Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the
- * 		packet associated to *skb*. Computation is incremental, so the
- * 		helper must know the former value of the header field that was
- * 		modified (*from*), the new value of this field (*to*), and the
- * 		number of bytes (2 or 4) for this field, stored on the lowest
- * 		four bits of *flags*. Alternatively, it is possible to store
- * 		the difference between the previous and the new values of the
- * 		header field in *to*, by setting *from* and the four lowest
- * 		bits of *flags* to 0. For both methods, *offset* indicates the
- * 		location of the IP checksum within the packet. In addition to
- * 		the size of the field, *flags* can be added (bitwise OR) actual
- * 		flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left
- * 		untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and
- * 		for updates resulting in a null checksum the value is set to
- * 		**CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates
- * 		the checksum is to be computed against a pseudo-header.
- *
- * 		This helper works in combination with **bpf_csum_diff**\ (),
- * 		which does not update the checksum in-place, but offers more
- * 		flexibility and can handle sizes larger than 2 or 4 for the
- * 		checksum to update.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_tail_call(void *ctx, struct bpf_map *prog_array_map, u32 index)
- * 	Description
- * 		This special helper is used to trigger a "tail call", or in
- * 		other words, to jump into another eBPF program. The same stack
- * 		frame is used (but values on stack and in registers for the
- * 		caller are not accessible to the callee). This mechanism allows
- * 		for program chaining, either for raising the maximum number of
- * 		available eBPF instructions, or to execute given programs in
- * 		conditional blocks. For security reasons, there is an upper
- * 		limit to the number of successive tail calls that can be
- * 		performed.
- *
- * 		Upon call of this helper, the program attempts to jump into a
- * 		program referenced at index *index* in *prog_array_map*, a
- * 		special map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes
- * 		*ctx*, a pointer to the context.
- *
- * 		If the call succeeds, the kernel immediately runs the first
- * 		instruction of the new program. This is not a function call,
- * 		and it never returns to the previous program. If the call
- * 		fails, then the helper has no effect, and the caller continues
- * 		to run its subsequent instructions. A call can fail if the
- * 		destination program for the jump does not exist (i.e. *index*
- * 		is superior to the number of entries in *prog_array_map*), or
- * 		if the maximum number of tail calls has been reached for this
- * 		chain of programs. This limit is defined in the kernel by the
- * 		macro **MAX_TAIL_CALL_CNT** (not accessible to user space),
- * 		which is currently set to 32.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_clone_redirect(struct sk_buff *skb, u32 ifindex, u64 flags)
- * 	Description
- * 		Clone and redirect the packet associated to *skb* to another
- * 		net device of index *ifindex*. Both ingress and egress
- * 		interfaces can be used for redirection. The **BPF_F_INGRESS**
- * 		value in *flags* is used to make the distinction (ingress path
- * 		is selected if the flag is present, egress path otherwise).
- * 		This is the only flag supported for now.
- *
- * 		In comparison with **bpf_redirect**\ () helper,
- * 		**bpf_clone_redirect**\ () has the associated cost of
- * 		duplicating the packet buffer, but this can be executed out of
- * 		the eBPF program. Conversely, **bpf_redirect**\ () is more
- * 		efficient, but it is handled through an action code where the
- * 		redirection happens only after the eBPF program has returned.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * u64 bpf_get_current_pid_tgid(void)
- * 	Return
- * 		A 64-bit integer containing the current tgid and pid, and
- * 		created as such:
- * 		*current_task*\ **->tgid << 32 \|**
- * 		*current_task*\ **->pid**.
- *
- * u64 bpf_get_current_uid_gid(void)
- * 	Return
- * 		A 64-bit integer containing the current GID and UID, and
- * 		created as such: *current_gid* **<< 32 \|** *current_uid*.
- *
- * int bpf_get_current_comm(char *buf, u32 size_of_buf)
- * 	Description
- * 		Copy the **comm** attribute of the current task into *buf* of
- * 		*size_of_buf*. The **comm** attribute contains the name of
- * 		the executable (excluding the path) for the current task. The
- * 		*size_of_buf* must be strictly positive. On success, the
- * 		helper makes sure that the *buf* is NUL-terminated. On failure,
- * 		it is filled with zeroes.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * u32 bpf_get_cgroup_classid(struct sk_buff *skb)
- * 	Description
- * 		Retrieve the classid for the current task, i.e. for the net_cls
- * 		cgroup to which *skb* belongs.
- *
- * 		This helper can be used on TC egress path, but not on ingress.
- *
- * 		The net_cls cgroup provides an interface to tag network packets
- * 		based on a user-provided identifier for all traffic coming from
- * 		the tasks belonging to the related cgroup. See also the related
- * 		kernel documentation, available from the Linux sources in file
- * 		*Documentation/admin-guide/cgroup-v1/net_cls.rst*.
- *
- * 		The Linux kernel has two versions for cgroups: there are
- * 		cgroups v1 and cgroups v2. Both are available to users, who can
- * 		use a mixture of them, but note that the net_cls cgroup is for
- * 		cgroup v1 only. This makes it incompatible with BPF programs
- * 		run on cgroups, which is a cgroup-v2-only feature (a socket can
- * 		only hold data for one version of cgroups at a time).
- *
- * 		This helper is only available is the kernel was compiled with
- * 		the **CONFIG_CGROUP_NET_CLASSID** configuration option set to
- * 		"**y**" or to "**m**".
- * 	Return
- * 		The classid, or 0 for the default unconfigured classid.
- *
- * int bpf_skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci)
- * 	Description
- * 		Push a *vlan_tci* (VLAN tag control information) of protocol
- * 		*vlan_proto* to the packet associated to *skb*, then update
- * 		the checksum. Note that if *vlan_proto* is different from
- * 		**ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to
- * 		be **ETH_P_8021Q**.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_vlan_pop(struct sk_buff *skb)
- * 	Description
- * 		Pop a VLAN header from the packet associated to *skb*.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_get_tunnel_key(struct sk_buff *skb, struct bpf_tunnel_key *key, u32 size, u64 flags)
- * 	Description
- * 		Get tunnel metadata. This helper takes a pointer *key* to an
- * 		empty **struct bpf_tunnel_key** of **size**, that will be
- * 		filled with tunnel metadata for the packet associated to *skb*.
- * 		The *flags* can be set to **BPF_F_TUNINFO_IPV6**, which
- * 		indicates that the tunnel is based on IPv6 protocol instead of
- * 		IPv4.
- *
- * 		The **struct bpf_tunnel_key** is an object that generalizes the
- * 		principal parameters used by various tunneling protocols into a
- * 		single struct. This way, it can be used to easily make a
- * 		decision based on the contents of the encapsulation header,
- * 		"summarized" in this struct. In particular, it holds the IP
- * 		address of the remote end (IPv4 or IPv6, depending on the case)
- * 		in *key*\ **->remote_ipv4** or *key*\ **->remote_ipv6**. Also,
- * 		this struct exposes the *key*\ **->tunnel_id**, which is
- * 		generally mapped to a VNI (Virtual Network Identifier), making
- * 		it programmable together with the **bpf_skb_set_tunnel_key**\
- * 		() helper.
- *
- * 		Let's imagine that the following code is part of a program
- * 		attached to the TC ingress interface, on one end of a GRE
- * 		tunnel, and is supposed to filter out all messages coming from
- * 		remote ends with IPv4 address other than 10.0.0.1:
- *
- * 		::
- *
- * 			int ret;
- * 			struct bpf_tunnel_key key = {};
- * 			
- * 			ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
- * 			if (ret < 0)
- * 				return TC_ACT_SHOT;	// drop packet
- * 			
- * 			if (key.remote_ipv4 != 0x0a000001)
- * 				return TC_ACT_SHOT;	// drop packet
- * 			
- * 			return TC_ACT_OK;		// accept packet
- *
- * 		This interface can also be used with all encapsulation devices
- * 		that can operate in "collect metadata" mode: instead of having
- * 		one network device per specific configuration, the "collect
- * 		metadata" mode only requires a single device where the
- * 		configuration can be extracted from this helper.
- *
- * 		This can be used together with various tunnels such as VXLan,
- * 		Geneve, GRE or IP in IP (IPIP).
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_set_tunnel_key(struct sk_buff *skb, struct bpf_tunnel_key *key, u32 size, u64 flags)
- * 	Description
- * 		Populate tunnel metadata for packet associated to *skb.* The
- * 		tunnel metadata is set to the contents of *key*, of *size*. The
- * 		*flags* can be set to a combination of the following values:
- *
- * 		**BPF_F_TUNINFO_IPV6**
- * 			Indicate that the tunnel is based on IPv6 protocol
- * 			instead of IPv4.
- * 		**BPF_F_ZERO_CSUM_TX**
- * 			For IPv4 packets, add a flag to tunnel metadata
- * 			indicating that checksum computation should be skipped
- * 			and checksum set to zeroes.
- * 		**BPF_F_DONT_FRAGMENT**
- * 			Add a flag to tunnel metadata indicating that the
- * 			packet should not be fragmented.
- * 		**BPF_F_SEQ_NUMBER**
- * 			Add a flag to tunnel metadata indicating that a
- * 			sequence number should be added to tunnel header before
- * 			sending the packet. This flag was added for GRE
- * 			encapsulation, but might be used with other protocols
- * 			as well in the future.
- *
- * 		Here is a typical usage on the transmit path:
- *
- * 		::
- *
- * 			struct bpf_tunnel_key key;
- * 			     populate key ...
- * 			bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0);
- * 			bpf_clone_redirect(skb, vxlan_dev_ifindex, 0);
- *
- * 		See also the description of the **bpf_skb_get_tunnel_key**\ ()
- * 		helper for additional information.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * u64 bpf_perf_event_read(struct bpf_map *map, u64 flags)
- * 	Description
- * 		Read the value of a perf event counter. This helper relies on a
- * 		*map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of
- * 		the perf event counter is selected when *map* is updated with
- * 		perf event file descriptors. The *map* is an array whose size
- * 		is the number of available CPUs, and each cell contains a value
- * 		relative to one CPU. The value to retrieve is indicated by
- * 		*flags*, that contains the index of the CPU to look up, masked
- * 		with **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to
- * 		**BPF_F_CURRENT_CPU** to indicate that the value for the
- * 		current CPU should be retrieved.
- *
- * 		Note that before Linux 4.13, only hardware perf event can be
- * 		retrieved.
- *
- * 		Also, be aware that the newer helper
- * 		**bpf_perf_event_read_value**\ () is recommended over
- * 		**bpf_perf_event_read**\ () in general. The latter has some ABI
- * 		quirks where error and counter value are used as a return code
- * 		(which is wrong to do since ranges may overlap). This issue is
- * 		fixed with **bpf_perf_event_read_value**\ (), which at the same
- * 		time provides more features over the **bpf_perf_event_read**\
- * 		() interface. Please refer to the description of
- * 		**bpf_perf_event_read_value**\ () for details.
- * 	Return
- * 		The value of the perf event counter read from the map, or a
- * 		negative error code in case of failure.
- *
- * int bpf_redirect(u32 ifindex, u64 flags)
- * 	Description
- * 		Redirect the packet to another net device of index *ifindex*.
- * 		This helper is somewhat similar to **bpf_clone_redirect**\
- * 		(), except that the packet is not cloned, which provides
- * 		increased performance.
- *
- * 		Except for XDP, both ingress and egress interfaces can be used
- * 		for redirection. The **BPF_F_INGRESS** value in *flags* is used
- * 		to make the distinction (ingress path is selected if the flag
- * 		is present, egress path otherwise). Currently, XDP only
- * 		supports redirection to the egress interface, and accepts no
- * 		flag at all.
- *
- * 		The same effect can be attained with the more generic
- * 		**bpf_redirect_map**\ (), which requires specific maps to be
- * 		used but offers better performance.
- * 	Return
- * 		For XDP, the helper returns **XDP_REDIRECT** on success or
- * 		**XDP_ABORTED** on error. For other program types, the values
- * 		are **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on
- * 		error.
- *
- * u32 bpf_get_route_realm(struct sk_buff *skb)
- * 	Description
- * 		Retrieve the realm or the route, that is to say the
- * 		**tclassid** field of the destination for the *skb*. The
- * 		indentifier retrieved is a user-provided tag, similar to the
- * 		one used with the net_cls cgroup (see description for
- * 		**bpf_get_cgroup_classid**\ () helper), but here this tag is
- * 		held by a route (a destination entry), not by a task.
- *
- * 		Retrieving this identifier works with the clsact TC egress hook
- * 		(see also **tc-bpf(8)**), or alternatively on conventional
- * 		classful egress qdiscs, but not on TC ingress path. In case of
- * 		clsact TC egress hook, this has the advantage that, internally,
- * 		the destination entry has not been dropped yet in the transmit
- * 		path. Therefore, the destination entry does not need to be
- * 		artificially held via **netif_keep_dst**\ () for a classful
- * 		qdisc until the *skb* is freed.
- *
- * 		This helper is available only if the kernel was compiled with
- * 		**CONFIG_IP_ROUTE_CLASSID** configuration option.
- * 	Return
- * 		The realm of the route for the packet associated to *skb*, or 0
- * 		if none was found.
- *
- * int bpf_perf_event_output(struct pt_regs *ctx, struct bpf_map *map, u64 flags, void *data, u64 size)
- * 	Description
- * 		Write raw *data* blob into a special BPF perf event held by
- * 		*map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf
- * 		event must have the following attributes: **PERF_SAMPLE_RAW**
- * 		as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and
- * 		**PERF_COUNT_SW_BPF_OUTPUT** as **config**.
- *
- * 		The *flags* are used to indicate the index in *map* for which
- * 		the value must be put, masked with **BPF_F_INDEX_MASK**.
- * 		Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU**
- * 		to indicate that the index of the current CPU core should be
- * 		used.
- *
- * 		The value to write, of *size*, is passed through eBPF stack and
- * 		pointed by *data*.
- *
- * 		The context of the program *ctx* needs also be passed to the
- * 		helper.
- *
- * 		On user space, a program willing to read the values needs to
- * 		call **perf_event_open**\ () on the perf event (either for
- * 		one or for all CPUs) and to store the file descriptor into the
- * 		*map*. This must be done before the eBPF program can send data
- * 		into it. An example is available in file
- * 		*samples/bpf/trace_output_user.c* in the Linux kernel source
- * 		tree (the eBPF program counterpart is in
- * 		*samples/bpf/trace_output_kern.c*).
- *
- * 		**bpf_perf_event_output**\ () achieves better performance
- * 		than **bpf_trace_printk**\ () for sharing data with user
- * 		space, and is much better suitable for streaming data from eBPF
- * 		programs.
- *
- * 		Note that this helper is not restricted to tracing use cases
- * 		and can be used with programs attached to TC or XDP as well,
- * 		where it allows for passing data to user space listeners. Data
- * 		can be:
- *
- * 		* Only custom structs,
- * 		* Only the packet payload, or
- * 		* A combination of both.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_load_bytes(const struct sk_buff *skb, u32 offset, void *to, u32 len)
- * 	Description
- * 		This helper was provided as an easy way to load data from a
- * 		packet. It can be used to load *len* bytes from *offset* from
- * 		the packet associated to *skb*, into the buffer pointed by
- * 		*to*.
- *
- * 		Since Linux 4.7, usage of this helper has mostly been replaced
- * 		by "direct packet access", enabling packet data to be
- * 		manipulated with *skb*\ **->data** and *skb*\ **->data_end**
- * 		pointing respectively to the first byte of packet data and to
- * 		the byte after the last byte of packet data. However, it
- * 		remains useful if one wishes to read large quantities of data
- * 		at once from a packet into the eBPF stack.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_get_stackid(struct pt_regs *ctx, struct bpf_map *map, u64 flags)
- * 	Description
- * 		Walk a user or a kernel stack and return its id. To achieve
- * 		this, the helper needs *ctx*, which is a pointer to the context
- * 		on which the tracing program is executed, and a pointer to a
- * 		*map* of type **BPF_MAP_TYPE_STACK_TRACE**.
- *
- * 		The last argument, *flags*, holds the number of stack frames to
- * 		skip (from 0 to 255), masked with
- * 		**BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set
- * 		a combination of the following flags:
- *
- * 		**BPF_F_USER_STACK**
- * 			Collect a user space stack instead of a kernel stack.
- * 		**BPF_F_FAST_STACK_CMP**
- * 			Compare stacks by hash only.
- * 		**BPF_F_REUSE_STACKID**
- * 			If two different stacks hash into the same *stackid*,
- * 			discard the old one.
- *
- * 		The stack id retrieved is a 32 bit long integer handle which
- * 		can be further combined with other data (including other stack
- * 		ids) and used as a key into maps. This can be useful for
- * 		generating a variety of graphs (such as flame graphs or off-cpu
- * 		graphs).
- *
- * 		For walking a stack, this helper is an improvement over
- * 		**bpf_probe_read**\ (), which can be used with unrolled loops
- * 		but is not efficient and consumes a lot of eBPF instructions.
- * 		Instead, **bpf_get_stackid**\ () can collect up to
- * 		**PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that
- * 		this limit can be controlled with the **sysctl** program, and
- * 		that it should be manually increased in order to profile long
- * 		user stacks (such as stacks for Java programs). To do so, use:
- *
- * 		::
- *
- * 			# sysctl kernel.perf_event_max_stack=<new value>
- * 	Return
- * 		The positive or null stack id on success, or a negative error
- * 		in case of failure.
- *
- * s64 bpf_csum_diff(__be32 *from, u32 from_size, __be32 *to, u32 to_size, __wsum seed)
- * 	Description
- * 		Compute a checksum difference, from the raw buffer pointed by
- * 		*from*, of length *from_size* (that must be a multiple of 4),
- * 		towards the raw buffer pointed by *to*, of size *to_size*
- * 		(same remark). An optional *seed* can be added to the value
- * 		(this can be cascaded, the seed may come from a previous call
- * 		to the helper).
- *
- * 		This is flexible enough to be used in several ways:
- *
- * 		* With *from_size* == 0, *to_size* > 0 and *seed* set to
- * 		  checksum, it can be used when pushing new data.
- * 		* With *from_size* > 0, *to_size* == 0 and *seed* set to
- * 		  checksum, it can be used when removing data from a packet.
- * 		* With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it
- * 		  can be used to compute a diff. Note that *from_size* and
- * 		  *to_size* do not need to be equal.
- *
- * 		This helper can be used in combination with
- * 		**bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ (), to
- * 		which one can feed in the difference computed with
- * 		**bpf_csum_diff**\ ().
- * 	Return
- * 		The checksum result, or a negative error code in case of
- * 		failure.
- *
- * int bpf_skb_get_tunnel_opt(struct sk_buff *skb, u8 *opt, u32 size)
- * 	Description
- * 		Retrieve tunnel options metadata for the packet associated to
- * 		*skb*, and store the raw tunnel option data to the buffer *opt*
- * 		of *size*.
- *
- * 		This helper can be used with encapsulation devices that can
- * 		operate in "collect metadata" mode (please refer to the related
- * 		note in the description of **bpf_skb_get_tunnel_key**\ () for
- * 		more details). A particular example where this can be used is
- * 		in combination with the Geneve encapsulation protocol, where it
- * 		allows for pushing (with **bpf_skb_get_tunnel_opt**\ () helper)
- * 		and retrieving arbitrary TLVs (Type-Length-Value headers) from
- * 		the eBPF program. This allows for full customization of these
- * 		headers.
- * 	Return
- * 		The size of the option data retrieved.
- *
- * int bpf_skb_set_tunnel_opt(struct sk_buff *skb, u8 *opt, u32 size)
- * 	Description
- * 		Set tunnel options metadata for the packet associated to *skb*
- * 		to the option data contained in the raw buffer *opt* of *size*.
- *
- * 		See also the description of the **bpf_skb_get_tunnel_opt**\ ()
- * 		helper for additional information.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_change_proto(struct sk_buff *skb, __be16 proto, u64 flags)
- * 	Description
- * 		Change the protocol of the *skb* to *proto*. Currently
- * 		supported are transition from IPv4 to IPv6, and from IPv6 to
- * 		IPv4. The helper takes care of the groundwork for the
- * 		transition, including resizing the socket buffer. The eBPF
- * 		program is expected to fill the new headers, if any, via
- * 		**skb_store_bytes**\ () and to recompute the checksums with
- * 		**bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\
- * 		(). The main case for this helper is to perform NAT64
- * 		operations out of an eBPF program.
- *
- * 		Internally, the GSO type is marked as dodgy so that headers are
- * 		checked and segments are recalculated by the GSO/GRO engine.
- * 		The size for GSO target is adapted as well.
- *
- * 		All values for *flags* are reserved for future usage, and must
- * 		be left at zero.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_change_type(struct sk_buff *skb, u32 type)
- * 	Description
- * 		Change the packet type for the packet associated to *skb*. This
- * 		comes down to setting *skb*\ **->pkt_type** to *type*, except
- * 		the eBPF program does not have a write access to *skb*\
- * 		**->pkt_type** beside this helper. Using a helper here allows
- * 		for graceful handling of errors.
- *
- * 		The major use case is to change incoming *skb*s to
- * 		**PACKET_HOST** in a programmatic way instead of having to
- * 		recirculate via **redirect**\ (..., **BPF_F_INGRESS**), for
- * 		example.
- *
- * 		Note that *type* only allows certain values. At this time, they
- * 		are:
- *
- * 		**PACKET_HOST**
- * 			Packet is for us.
- * 		**PACKET_BROADCAST**
- * 			Send packet to all.
- * 		**PACKET_MULTICAST**
- * 			Send packet to group.
- * 		**PACKET_OTHERHOST**
- * 			Send packet to someone else.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_under_cgroup(struct sk_buff *skb, struct bpf_map *map, u32 index)
- * 	Description
- * 		Check whether *skb* is a descendant of the cgroup2 held by
- * 		*map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*.
- * 	Return
- * 		The return value depends on the result of the test, and can be:
- *
- * 		* 0, if the *skb* failed the cgroup2 descendant test.
- * 		* 1, if the *skb* succeeded the cgroup2 descendant test.
- * 		* A negative error code, if an error occurred.
- *
- * u32 bpf_get_hash_recalc(struct sk_buff *skb)
- * 	Description
- * 		Retrieve the hash of the packet, *skb*\ **->hash**. If it is
- * 		not set, in particular if the hash was cleared due to mangling,
- * 		recompute this hash. Later accesses to the hash can be done
- * 		directly with *skb*\ **->hash**.
- *
- * 		Calling **bpf_set_hash_invalid**\ (), changing a packet
- * 		prototype with **bpf_skb_change_proto**\ (), or calling
- * 		**bpf_skb_store_bytes**\ () with the
- * 		**BPF_F_INVALIDATE_HASH** are actions susceptible to clear
- * 		the hash and to trigger a new computation for the next call to
- * 		**bpf_get_hash_recalc**\ ().
- * 	Return
- * 		The 32-bit hash.
- *
- * u64 bpf_get_current_task(void)
- * 	Return
- * 		A pointer to the current task struct.
- *
- * int bpf_probe_write_user(void *dst, const void *src, u32 len)
- * 	Description
- * 		Attempt in a safe way to write *len* bytes from the buffer
- * 		*src* to *dst* in memory. It only works for threads that are in
- * 		user context, and *dst* must be a valid user space address.
- *
- * 		This helper should not be used to implement any kind of
- * 		security mechanism because of TOC-TOU attacks, but rather to
- * 		debug, divert, and manipulate execution of semi-cooperative
- * 		processes.
- *
- * 		Keep in mind that this feature is meant for experiments, and it
- * 		has a risk of crashing the system and running programs.
- * 		Therefore, when an eBPF program using this helper is attached,
- * 		a warning including PID and process name is printed to kernel
- * 		logs.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_current_task_under_cgroup(struct bpf_map *map, u32 index)
- * 	Description
- * 		Check whether the probe is being run is the context of a given
- * 		subset of the cgroup2 hierarchy. The cgroup2 to test is held by
- * 		*map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*.
- * 	Return
- * 		The return value depends on the result of the test, and can be:
- *
- * 		* 0, if the *skb* task belongs to the cgroup2.
- * 		* 1, if the *skb* task does not belong to the cgroup2.
- * 		* A negative error code, if an error occurred.
- *
- * int bpf_skb_change_tail(struct sk_buff *skb, u32 len, u64 flags)
- * 	Description
- * 		Resize (trim or grow) the packet associated to *skb* to the
- * 		new *len*. The *flags* are reserved for future usage, and must
- * 		be left at zero.
- *
- * 		The basic idea is that the helper performs the needed work to
- * 		change the size of the packet, then the eBPF program rewrites
- * 		the rest via helpers like **bpf_skb_store_bytes**\ (),
- * 		**bpf_l3_csum_replace**\ (), **bpf_l3_csum_replace**\ ()
- * 		and others. This helper is a slow path utility intended for
- * 		replies with control messages. And because it is targeted for
- * 		slow path, the helper itself can afford to be slow: it
- * 		implicitly linearizes, unclones and drops offloads from the
- * 		*skb*.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_pull_data(struct sk_buff *skb, u32 len)
- * 	Description
- * 		Pull in non-linear data in case the *skb* is non-linear and not
- * 		all of *len* are part of the linear section. Make *len* bytes
- * 		from *skb* readable and writable. If a zero value is passed for
- * 		*len*, then the whole length of the *skb* is pulled.
- *
- * 		This helper is only needed for reading and writing with direct
- * 		packet access.
- *
- * 		For direct packet access, testing that offsets to access
- * 		are within packet boundaries (test on *skb*\ **->data_end**) is
- * 		susceptible to fail if offsets are invalid, or if the requested
- * 		data is in non-linear parts of the *skb*. On failure the
- * 		program can just bail out, or in the case of a non-linear
- * 		buffer, use a helper to make the data available. The
- * 		**bpf_skb_load_bytes**\ () helper is a first solution to access
- * 		the data. Another one consists in using **bpf_skb_pull_data**
- * 		to pull in once the non-linear parts, then retesting and
- * 		eventually access the data.
- *
- * 		At the same time, this also makes sure the *skb* is uncloned,
- * 		which is a necessary condition for direct write. As this needs
- * 		to be an invariant for the write part only, the verifier
- * 		detects writes and adds a prologue that is calling
- * 		**bpf_skb_pull_data()** to effectively unclone the *skb* from
- * 		the very beginning in case it is indeed cloned.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * s64 bpf_csum_update(struct sk_buff *skb, __wsum csum)
- * 	Description
- * 		Add the checksum *csum* into *skb*\ **->csum** in case the
- * 		driver has supplied a checksum for the entire packet into that
- * 		field. Return an error otherwise. This helper is intended to be
- * 		used in combination with **bpf_csum_diff**\ (), in particular
- * 		when the checksum needs to be updated after data has been
- * 		written into the packet through direct packet access.
- * 	Return
- * 		The checksum on success, or a negative error code in case of
- * 		failure.
- *
- * void bpf_set_hash_invalid(struct sk_buff *skb)
- * 	Description
- * 		Invalidate the current *skb*\ **->hash**. It can be used after
- * 		mangling on headers through direct packet access, in order to
- * 		indicate that the hash is outdated and to trigger a
- * 		recalculation the next time the kernel tries to access this
- * 		hash or when the **bpf_get_hash_recalc**\ () helper is called.
- *
- * int bpf_get_numa_node_id(void)
- * 	Description
- * 		Return the id of the current NUMA node. The primary use case
- * 		for this helper is the selection of sockets for the local NUMA
- * 		node, when the program is attached to sockets using the
- * 		**SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**),
- * 		but the helper is also available to other eBPF program types,
- * 		similarly to **bpf_get_smp_processor_id**\ ().
- * 	Return
- * 		The id of current NUMA node.
- *
- * int bpf_skb_change_head(struct sk_buff *skb, u32 len, u64 flags)
- * 	Description
- * 		Grows headroom of packet associated to *skb* and adjusts the
- * 		offset of the MAC header accordingly, adding *len* bytes of
- * 		space. It automatically extends and reallocates memory as
- * 		required.
- *
- * 		This helper can be used on a layer 3 *skb* to push a MAC header
- * 		for redirection into a layer 2 device.
- *
- * 		All values for *flags* are reserved for future usage, and must
- * 		be left at zero.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_xdp_adjust_head(struct xdp_buff *xdp_md, int delta)
- * 	Description
- * 		Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that
- * 		it is possible to use a negative value for *delta*. This helper
- * 		can be used to prepare the packet for pushing or popping
- * 		headers.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr)
- * 	Description
- * 		Copy a NUL terminated string from an unsafe address
- * 		*unsafe_ptr* to *dst*. The *size* should include the
- * 		terminating NUL byte. In case the string length is smaller than
- * 		*size*, the target is not padded with further NUL bytes. If the
- * 		string length is larger than *size*, just *size*-1 bytes are
- * 		copied and the last byte is set to NUL.
- *
- * 		On success, the length of the copied string is returned. This
- * 		makes this helper useful in tracing programs for reading
- * 		strings, and more importantly to get its length at runtime. See
- * 		the following snippet:
- *
- * 		::
- *
- * 			SEC("kprobe/sys_open")
- * 			void bpf_sys_open(struct pt_regs *ctx)
- * 			{
- * 			        char buf[PATHLEN]; // PATHLEN is defined to 256
- * 			        int res = bpf_probe_read_str(buf, sizeof(buf),
- * 				                             ctx->di);
- *
- * 				// Consume buf, for example push it to
- * 				// userspace via bpf_perf_event_output(); we
- * 				// can use res (the string length) as event
- * 				// size, after checking its boundaries.
- * 			}
- *
- * 		In comparison, using **bpf_probe_read()** helper here instead
- * 		to read the string would require to estimate the length at
- * 		compile time, and would often result in copying more memory
- * 		than necessary.
- *
- * 		Another useful use case is when parsing individual process
- * 		arguments or individual environment variables navigating
- * 		*current*\ **->mm->arg_start** and *current*\
- * 		**->mm->env_start**: using this helper and the return value,
- * 		one can quickly iterate at the right offset of the memory area.
- * 	Return
- * 		On success, the strictly positive length of the string,
- * 		including the trailing NUL character. On error, a negative
- * 		value.
- *
- * u64 bpf_get_socket_cookie(struct sk_buff *skb)
- * 	Description
- * 		If the **struct sk_buff** pointed by *skb* has a known socket,
- * 		retrieve the cookie (generated by the kernel) of this socket.
- * 		If no cookie has been set yet, generate a new cookie. Once
- * 		generated, the socket cookie remains stable for the life of the
- * 		socket. This helper can be useful for monitoring per socket
- * 		networking traffic statistics as it provides a global socket
- * 		identifier that can be assumed unique.
- * 	Return
- * 		A 8-byte long non-decreasing number on success, or 0 if the
- * 		socket field is missing inside *skb*.
- *
- * u64 bpf_get_socket_cookie(struct bpf_sock_addr *ctx)
- * 	Description
- * 		Equivalent to bpf_get_socket_cookie() helper that accepts
- * 		*skb*, but gets socket from **struct bpf_sock_addr** context.
- * 	Return
- * 		A 8-byte long non-decreasing number.
- *
- * u64 bpf_get_socket_cookie(struct bpf_sock_ops *ctx)
- * 	Description
- * 		Equivalent to bpf_get_socket_cookie() helper that accepts
- * 		*skb*, but gets socket from **struct bpf_sock_ops** context.
- * 	Return
- * 		A 8-byte long non-decreasing number.
- *
- * u32 bpf_get_socket_uid(struct sk_buff *skb)
- * 	Return
- * 		The owner UID of the socket associated to *skb*. If the socket
- * 		is **NULL**, or if it is not a full socket (i.e. if it is a
- * 		time-wait or a request socket instead), **overflowuid** value
- * 		is returned (note that **overflowuid** might also be the actual
- * 		UID value for the socket).
- *
- * u32 bpf_set_hash(struct sk_buff *skb, u32 hash)
- * 	Description
- * 		Set the full hash for *skb* (set the field *skb*\ **->hash**)
- * 		to value *hash*.
- * 	Return
- * 		0
- *
- * int bpf_setsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, char *optval, int optlen)
- * 	Description
- * 		Emulate a call to **setsockopt()** on the socket associated to
- * 		*bpf_socket*, which must be a full socket. The *level* at
- * 		which the option resides and the name *optname* of the option
- * 		must be specified, see **setsockopt(2)** for more information.
- * 		The option value of length *optlen* is pointed by *optval*.
- *
- * 		This helper actually implements a subset of **setsockopt()**.
- * 		It supports the following *level*\ s:
- *
- * 		* **SOL_SOCKET**, which supports the following *optname*\ s:
- * 		  **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**,
- * 		  **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**.
- * 		* **IPPROTO_TCP**, which supports the following *optname*\ s:
- * 		  **TCP_CONGESTION**, **TCP_BPF_IW**,
- * 		  **TCP_BPF_SNDCWND_CLAMP**.
- * 		* **IPPROTO_IP**, which supports *optname* **IP_TOS**.
- * 		* **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_adjust_room(struct sk_buff *skb, s32 len_diff, u32 mode, u64 flags)
- * 	Description
- * 		Grow or shrink the room for data in the packet associated to
- * 		*skb* by *len_diff*, and according to the selected *mode*.
- *
- *		There are two supported modes at this time:
- *
- *		* **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer
- *		  (room space is added or removed below the layer 2 header).
- *
- * 		* **BPF_ADJ_ROOM_NET**: Adjust room at the network layer
- * 		  (room space is added or removed below the layer 3 header).
- *
- *		The following flags are supported at this time:
- *
- *		* **BPF_F_ADJ_ROOM_FIXED_GSO**: Do not adjust gso_size.
- *		  Adjusting mss in this way is not allowed for datagrams.
- *
- *		* **BPF_F_ADJ_ROOM_ENCAP_L3_IPV4**,
- *		  **BPF_F_ADJ_ROOM_ENCAP_L3_IPV6**:
- *		  Any new space is reserved to hold a tunnel header.
- *		  Configure skb offsets and other fields accordingly.
- *
- *		* **BPF_F_ADJ_ROOM_ENCAP_L4_GRE**,
- *		  **BPF_F_ADJ_ROOM_ENCAP_L4_UDP**:
- *		  Use with ENCAP_L3 flags to further specify the tunnel type.
- *
- *		* **BPF_F_ADJ_ROOM_ENCAP_L2**\ (*len*):
- *		  Use with ENCAP_L3/L4 flags to further specify the tunnel
- *		  type; *len* is the length of the inner MAC header.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags)
- * 	Description
- * 		Redirect the packet to the endpoint referenced by *map* at
- * 		index *key*. Depending on its type, this *map* can contain
- * 		references to net devices (for forwarding packets through other
- * 		ports), or to CPUs (for redirecting XDP frames to another CPU;
- * 		but this is only implemented for native XDP (with driver
- * 		support) as of this writing).
- *
- * 		The lower two bits of *flags* are used as the return code if
- * 		the map lookup fails. This is so that the return value can be
- * 		one of the XDP program return codes up to XDP_TX, as chosen by
- * 		the caller. Any higher bits in the *flags* argument must be
- * 		unset.
- *
- * 		When used to redirect packets to net devices, this helper
- * 		provides a high performance increase over **bpf_redirect**\ ().
- * 		This is due to various implementation details of the underlying
- * 		mechanisms, one of which is the fact that **bpf_redirect_map**\
- * 		() tries to send packet as a "bulk" to the device.
- * 	Return
- * 		**XDP_REDIRECT** on success, or **XDP_ABORTED** on error.
- *
- * int bpf_sk_redirect_map(struct bpf_map *map, u32 key, u64 flags)
- * 	Description
- * 		Redirect the packet to the socket referenced by *map* (of type
- * 		**BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and
- * 		egress interfaces can be used for redirection. The
- * 		**BPF_F_INGRESS** value in *flags* is used to make the
- * 		distinction (ingress path is selected if the flag is present,
- * 		egress path otherwise). This is the only flag supported for now.
- * 	Return
- * 		**SK_PASS** on success, or **SK_DROP** on error.
- *
- * int bpf_sock_map_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags)
- * 	Description
- * 		Add an entry to, or update a *map* referencing sockets. The
- * 		*skops* is used as a new value for the entry associated to
- * 		*key*. *flags* is one of:
- *
- * 		**BPF_NOEXIST**
- * 			The entry for *key* must not exist in the map.
- * 		**BPF_EXIST**
- * 			The entry for *key* must already exist in the map.
- * 		**BPF_ANY**
- * 			No condition on the existence of the entry for *key*.
- *
- * 		If the *map* has eBPF programs (parser and verdict), those will
- * 		be inherited by the socket being added. If the socket is
- * 		already attached to eBPF programs, this results in an error.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_xdp_adjust_meta(struct xdp_buff *xdp_md, int delta)
- * 	Description
- * 		Adjust the address pointed by *xdp_md*\ **->data_meta** by
- * 		*delta* (which can be positive or negative). Note that this
- * 		operation modifies the address stored in *xdp_md*\ **->data**,
- * 		so the latter must be loaded only after the helper has been
- * 		called.
- *
- * 		The use of *xdp_md*\ **->data_meta** is optional and programs
- * 		are not required to use it. The rationale is that when the
- * 		packet is processed with XDP (e.g. as DoS filter), it is
- * 		possible to push further meta data along with it before passing
- * 		to the stack, and to give the guarantee that an ingress eBPF
- * 		program attached as a TC classifier on the same device can pick
- * 		this up for further post-processing. Since TC works with socket
- * 		buffers, it remains possible to set from XDP the **mark** or
- * 		**priority** pointers, or other pointers for the socket buffer.
- * 		Having this scratch space generic and programmable allows for
- * 		more flexibility as the user is free to store whatever meta
- * 		data they need.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_perf_event_read_value(struct bpf_map *map, u64 flags, struct bpf_perf_event_value *buf, u32 buf_size)
- * 	Description
- * 		Read the value of a perf event counter, and store it into *buf*
- * 		of size *buf_size*. This helper relies on a *map* of type
- * 		**BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event
- * 		counter is selected when *map* is updated with perf event file
- * 		descriptors. The *map* is an array whose size is the number of
- * 		available CPUs, and each cell contains a value relative to one
- * 		CPU. The value to retrieve is indicated by *flags*, that
- * 		contains the index of the CPU to look up, masked with
- * 		**BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to
- * 		**BPF_F_CURRENT_CPU** to indicate that the value for the
- * 		current CPU should be retrieved.
- *
- * 		This helper behaves in a way close to
- * 		**bpf_perf_event_read**\ () helper, save that instead of
- * 		just returning the value observed, it fills the *buf*
- * 		structure. This allows for additional data to be retrieved: in
- * 		particular, the enabled and running times (in *buf*\
- * 		**->enabled** and *buf*\ **->running**, respectively) are
- * 		copied. In general, **bpf_perf_event_read_value**\ () is
- * 		recommended over **bpf_perf_event_read**\ (), which has some
- * 		ABI issues and provides fewer functionalities.
- *
- * 		These values are interesting, because hardware PMU (Performance
- * 		Monitoring Unit) counters are limited resources. When there are
- * 		more PMU based perf events opened than available counters,
- * 		kernel will multiplex these events so each event gets certain
- * 		percentage (but not all) of the PMU time. In case that
- * 		multiplexing happens, the number of samples or counter value
- * 		will not reflect the case compared to when no multiplexing
- * 		occurs. This makes comparison between different runs difficult.
- * 		Typically, the counter value should be normalized before
- * 		comparing to other experiments. The usual normalization is done
- * 		as follows.
- *
- * 		::
- *
- * 			normalized_counter = counter * t_enabled / t_running
- *
- * 		Where t_enabled is the time enabled for event and t_running is
- * 		the time running for event since last normalization. The
- * 		enabled and running times are accumulated since the perf event
- * 		open. To achieve scaling factor between two invocations of an
- * 		eBPF program, users can can use CPU id as the key (which is
- * 		typical for perf array usage model) to remember the previous
- * 		value and do the calculation inside the eBPF program.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_perf_prog_read_value(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, u32 buf_size)
- * 	Description
- * 		For en eBPF program attached to a perf event, retrieve the
- * 		value of the event counter associated to *ctx* and store it in
- * 		the structure pointed by *buf* and of size *buf_size*. Enabled
- * 		and running times are also stored in the structure (see
- * 		description of helper **bpf_perf_event_read_value**\ () for
- * 		more details).
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_getsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, char *optval, int optlen)
- * 	Description
- * 		Emulate a call to **getsockopt()** on the socket associated to
- * 		*bpf_socket*, which must be a full socket. The *level* at
- * 		which the option resides and the name *optname* of the option
- * 		must be specified, see **getsockopt(2)** for more information.
- * 		The retrieved value is stored in the structure pointed by
- * 		*opval* and of length *optlen*.
- *
- * 		This helper actually implements a subset of **getsockopt()**.
- * 		It supports the following *level*\ s:
- *
- * 		* **IPPROTO_TCP**, which supports *optname*
- * 		  **TCP_CONGESTION**.
- * 		* **IPPROTO_IP**, which supports *optname* **IP_TOS**.
- * 		* **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_override_return(struct pt_regs *regs, u64 rc)
- * 	Description
- * 		Used for error injection, this helper uses kprobes to override
- * 		the return value of the probed function, and to set it to *rc*.
- * 		The first argument is the context *regs* on which the kprobe
- * 		works.
- *
- * 		This helper works by setting setting the PC (program counter)
- * 		to an override function which is run in place of the original
- * 		probed function. This means the probed function is not run at
- * 		all. The replacement function just returns with the required
- * 		value.
- *
- * 		This helper has security implications, and thus is subject to
- * 		restrictions. It is only available if the kernel was compiled
- * 		with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration
- * 		option, and in this case it only works on functions tagged with
- * 		**ALLOW_ERROR_INJECTION** in the kernel code.
- *
- * 		Also, the helper is only available for the architectures having
- * 		the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing,
- * 		x86 architecture is the only one to support this feature.
- * 	Return
- * 		0
- *
- * int bpf_sock_ops_cb_flags_set(struct bpf_sock_ops *bpf_sock, int argval)
- * 	Description
- * 		Attempt to set the value of the **bpf_sock_ops_cb_flags** field
- * 		for the full TCP socket associated to *bpf_sock_ops* to
- * 		*argval*.
- *
- * 		The primary use of this field is to determine if there should
- * 		be calls to eBPF programs of type
- * 		**BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP
- * 		code. A program of the same type can change its value, per
- * 		connection and as necessary, when the connection is
- * 		established. This field is directly accessible for reading, but
- * 		this helper must be used for updates in order to return an
- * 		error if an eBPF program tries to set a callback that is not
- * 		supported in the current kernel.
- *
- * 		*argval* is a flag array which can combine these flags:
- *
- * 		* **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out)
- * 		* **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission)
- * 		* **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change)
- * 		* **BPF_SOCK_OPS_RTT_CB_FLAG** (every RTT)
- *
- * 		Therefore, this function can be used to clear a callback flag by
- * 		setting the appropriate bit to zero. e.g. to disable the RTO
- * 		callback:
- *
- * 		**bpf_sock_ops_cb_flags_set(bpf_sock,**
- * 			**bpf_sock->bpf_sock_ops_cb_flags & ~BPF_SOCK_OPS_RTO_CB_FLAG)**
- *
- * 		Here are some examples of where one could call such eBPF
- * 		program:
- *
- * 		* When RTO fires.
- * 		* When a packet is retransmitted.
- * 		* When the connection terminates.
- * 		* When a packet is sent.
- * 		* When a packet is received.
- * 	Return
- * 		Code **-EINVAL** if the socket is not a full TCP socket;
- * 		otherwise, a positive number containing the bits that could not
- * 		be set is returned (which comes down to 0 if all bits were set
- * 		as required).
- *
- * int bpf_msg_redirect_map(struct sk_msg_buff *msg, struct bpf_map *map, u32 key, u64 flags)
- * 	Description
- * 		This helper is used in programs implementing policies at the
- * 		socket level. If the message *msg* is allowed to pass (i.e. if
- * 		the verdict eBPF program returns **SK_PASS**), redirect it to
- * 		the socket referenced by *map* (of type
- * 		**BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and
- * 		egress interfaces can be used for redirection. The
- * 		**BPF_F_INGRESS** value in *flags* is used to make the
- * 		distinction (ingress path is selected if the flag is present,
- * 		egress path otherwise). This is the only flag supported for now.
- * 	Return
- * 		**SK_PASS** on success, or **SK_DROP** on error.
- *
- * int bpf_msg_apply_bytes(struct sk_msg_buff *msg, u32 bytes)
- * 	Description
- * 		For socket policies, apply the verdict of the eBPF program to
- * 		the next *bytes* (number of bytes) of message *msg*.
- *
- * 		For example, this helper can be used in the following cases:
- *
- * 		* A single **sendmsg**\ () or **sendfile**\ () system call
- * 		  contains multiple logical messages that the eBPF program is
- * 		  supposed to read and for which it should apply a verdict.
- * 		* An eBPF program only cares to read the first *bytes* of a
- * 		  *msg*. If the message has a large payload, then setting up
- * 		  and calling the eBPF program repeatedly for all bytes, even
- * 		  though the verdict is already known, would create unnecessary
- * 		  overhead.
- *
- * 		When called from within an eBPF program, the helper sets a
- * 		counter internal to the BPF infrastructure, that is used to
- * 		apply the last verdict to the next *bytes*. If *bytes* is
- * 		smaller than the current data being processed from a
- * 		**sendmsg**\ () or **sendfile**\ () system call, the first
- * 		*bytes* will be sent and the eBPF program will be re-run with
- * 		the pointer for start of data pointing to byte number *bytes*
- * 		**+ 1**. If *bytes* is larger than the current data being
- * 		processed, then the eBPF verdict will be applied to multiple
- * 		**sendmsg**\ () or **sendfile**\ () calls until *bytes* are
- * 		consumed.
- *
- * 		Note that if a socket closes with the internal counter holding
- * 		a non-zero value, this is not a problem because data is not
- * 		being buffered for *bytes* and is sent as it is received.
- * 	Return
- * 		0
- *
- * int bpf_msg_cork_bytes(struct sk_msg_buff *msg, u32 bytes)
- * 	Description
- * 		For socket policies, prevent the execution of the verdict eBPF
- * 		program for message *msg* until *bytes* (byte number) have been
- * 		accumulated.
- *
- * 		This can be used when one needs a specific number of bytes
- * 		before a verdict can be assigned, even if the data spans
- * 		multiple **sendmsg**\ () or **sendfile**\ () calls. The extreme
- * 		case would be a user calling **sendmsg**\ () repeatedly with
- * 		1-byte long message segments. Obviously, this is bad for
- * 		performance, but it is still valid. If the eBPF program needs
- * 		*bytes* bytes to validate a header, this helper can be used to
- * 		prevent the eBPF program to be called again until *bytes* have
- * 		been accumulated.
- * 	Return
- * 		0
- *
- * int bpf_msg_pull_data(struct sk_msg_buff *msg, u32 start, u32 end, u64 flags)
- * 	Description
- * 		For socket policies, pull in non-linear data from user space
- * 		for *msg* and set pointers *msg*\ **->data** and *msg*\
- * 		**->data_end** to *start* and *end* bytes offsets into *msg*,
- * 		respectively.
- *
- * 		If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a
- * 		*msg* it can only parse data that the (**data**, **data_end**)
- * 		pointers have already consumed. For **sendmsg**\ () hooks this
- * 		is likely the first scatterlist element. But for calls relying
- * 		on the **sendpage** handler (e.g. **sendfile**\ ()) this will
- * 		be the range (**0**, **0**) because the data is shared with
- * 		user space and by default the objective is to avoid allowing
- * 		user space to modify data while (or after) eBPF verdict is
- * 		being decided. This helper can be used to pull in data and to
- * 		set the start and end pointer to given values. Data will be
- * 		copied if necessary (i.e. if data was not linear and if start
- * 		and end pointers do not point to the same chunk).
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- *
- * 		All values for *flags* are reserved for future usage, and must
- * 		be left at zero.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_bind(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len)
- * 	Description
- * 		Bind the socket associated to *ctx* to the address pointed by
- * 		*addr*, of length *addr_len*. This allows for making outgoing
- * 		connection from the desired IP address, which can be useful for
- * 		example when all processes inside a cgroup should use one
- * 		single IP address on a host that has multiple IP configured.
- *
- * 		This helper works for IPv4 and IPv6, TCP and UDP sockets. The
- * 		domain (*addr*\ **->sa_family**) must be **AF_INET** (or
- * 		**AF_INET6**). Looking for a free port to bind to can be
- * 		expensive, therefore binding to port is not permitted by the
- * 		helper: *addr*\ **->sin_port** (or **sin6_port**, respectively)
- * 		must be set to zero.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_xdp_adjust_tail(struct xdp_buff *xdp_md, int delta)
- * 	Description
- * 		Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is
- * 		only possible to shrink the packet as of this writing,
- * 		therefore *delta* must be a negative integer.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_skb_get_xfrm_state(struct sk_buff *skb, u32 index, struct bpf_xfrm_state *xfrm_state, u32 size, u64 flags)
- * 	Description
- * 		Retrieve the XFRM state (IP transform framework, see also
- * 		**ip-xfrm(8)**) at *index* in XFRM "security path" for *skb*.
- *
- * 		The retrieved value is stored in the **struct bpf_xfrm_state**
- * 		pointed by *xfrm_state* and of length *size*.
- *
- * 		All values for *flags* are reserved for future usage, and must
- * 		be left at zero.
- *
- * 		This helper is available only if the kernel was compiled with
- * 		**CONFIG_XFRM** configuration option.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_get_stack(struct pt_regs *regs, void *buf, u32 size, u64 flags)
- * 	Description
- * 		Return a user or a kernel stack in bpf program provided buffer.
- * 		To achieve this, the helper needs *ctx*, which is a pointer
- * 		to the context on which the tracing program is executed.
- * 		To store the stacktrace, the bpf program provides *buf* with
- * 		a nonnegative *size*.
- *
- * 		The last argument, *flags*, holds the number of stack frames to
- * 		skip (from 0 to 255), masked with
- * 		**BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set
- * 		the following flags:
- *
- * 		**BPF_F_USER_STACK**
- * 			Collect a user space stack instead of a kernel stack.
- * 		**BPF_F_USER_BUILD_ID**
- * 			Collect buildid+offset instead of ips for user stack,
- * 			only valid if **BPF_F_USER_STACK** is also specified.
- *
- * 		**bpf_get_stack**\ () can collect up to
- * 		**PERF_MAX_STACK_DEPTH** both kernel and user frames, subject
- * 		to sufficient large buffer size. Note that
- * 		this limit can be controlled with the **sysctl** program, and
- * 		that it should be manually increased in order to profile long
- * 		user stacks (such as stacks for Java programs). To do so, use:
- *
- * 		::
- *
- * 			# sysctl kernel.perf_event_max_stack=<new value>
- * 	Return
- * 		A non-negative value equal to or less than *size* on success,
- * 		or a negative error in case of failure.
- *
- * int bpf_skb_load_bytes_relative(const struct sk_buff *skb, u32 offset, void *to, u32 len, u32 start_header)
- * 	Description
- * 		This helper is similar to **bpf_skb_load_bytes**\ () in that
- * 		it provides an easy way to load *len* bytes from *offset*
- * 		from the packet associated to *skb*, into the buffer pointed
- * 		by *to*. The difference to **bpf_skb_load_bytes**\ () is that
- * 		a fifth argument *start_header* exists in order to select a
- * 		base offset to start from. *start_header* can be one of:
- *
- * 		**BPF_HDR_START_MAC**
- * 			Base offset to load data from is *skb*'s mac header.
- * 		**BPF_HDR_START_NET**
- * 			Base offset to load data from is *skb*'s network header.
- *
- * 		In general, "direct packet access" is the preferred method to
- * 		access packet data, however, this helper is in particular useful
- * 		in socket filters where *skb*\ **->data** does not always point
- * 		to the start of the mac header and where "direct packet access"
- * 		is not available.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_fib_lookup(void *ctx, struct bpf_fib_lookup *params, int plen, u32 flags)
- *	Description
- *		Do FIB lookup in kernel tables using parameters in *params*.
- *		If lookup is successful and result shows packet is to be
- *		forwarded, the neighbor tables are searched for the nexthop.
- *		If successful (ie., FIB lookup shows forwarding and nexthop
- *		is resolved), the nexthop address is returned in ipv4_dst
- *		or ipv6_dst based on family, smac is set to mac address of
- *		egress device, dmac is set to nexthop mac address, rt_metric
- *		is set to metric from route (IPv4/IPv6 only), and ifindex
- *		is set to the device index of the nexthop from the FIB lookup.
- *
- *		*plen* argument is the size of the passed in struct.
- *		*flags* argument can be a combination of one or more of the
- *		following values:
- *
- *		**BPF_FIB_LOOKUP_DIRECT**
- *			Do a direct table lookup vs full lookup using FIB
- *			rules.
- *		**BPF_FIB_LOOKUP_OUTPUT**
- *			Perform lookup from an egress perspective (default is
- *			ingress).
- *
- *		*ctx* is either **struct xdp_md** for XDP programs or
- *		**struct sk_buff** tc cls_act programs.
- *	Return
- *		* < 0 if any input argument is invalid
- *		*   0 on success (packet is forwarded, nexthop neighbor exists)
- *		* > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the
- *		  packet is not forwarded or needs assist from full stack
- *
- * int bpf_sock_hash_update(struct bpf_sock_ops_kern *skops, struct bpf_map *map, void *key, u64 flags)
- *	Description
- *		Add an entry to, or update a sockhash *map* referencing sockets.
- *		The *skops* is used as a new value for the entry associated to
- *		*key*. *flags* is one of:
- *
- *		**BPF_NOEXIST**
- *			The entry for *key* must not exist in the map.
- *		**BPF_EXIST**
- *			The entry for *key* must already exist in the map.
- *		**BPF_ANY**
- *			No condition on the existence of the entry for *key*.
- *
- *		If the *map* has eBPF programs (parser and verdict), those will
- *		be inherited by the socket being added. If the socket is
- *		already attached to eBPF programs, this results in an error.
- *	Return
- *		0 on success, or a negative error in case of failure.
- *
- * int bpf_msg_redirect_hash(struct sk_msg_buff *msg, struct bpf_map *map, void *key, u64 flags)
- *	Description
- *		This helper is used in programs implementing policies at the
- *		socket level. If the message *msg* is allowed to pass (i.e. if
- *		the verdict eBPF program returns **SK_PASS**), redirect it to
- *		the socket referenced by *map* (of type
- *		**BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and
- *		egress interfaces can be used for redirection. The
- *		**BPF_F_INGRESS** value in *flags* is used to make the
- *		distinction (ingress path is selected if the flag is present,
- *		egress path otherwise). This is the only flag supported for now.
- *	Return
- *		**SK_PASS** on success, or **SK_DROP** on error.
- *
- * int bpf_sk_redirect_hash(struct sk_buff *skb, struct bpf_map *map, void *key, u64 flags)
- *	Description
- *		This helper is used in programs implementing policies at the
- *		skb socket level. If the sk_buff *skb* is allowed to pass (i.e.
- *		if the verdeict eBPF program returns **SK_PASS**), redirect it
- *		to the socket referenced by *map* (of type
- *		**BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and
- *		egress interfaces can be used for redirection. The
- *		**BPF_F_INGRESS** value in *flags* is used to make the
- *		distinction (ingress path is selected if the flag is present,
- *		egress otherwise). This is the only flag supported for now.
- *	Return
- *		**SK_PASS** on success, or **SK_DROP** on error.
- *
- * int bpf_lwt_push_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len)
- *	Description
- *		Encapsulate the packet associated to *skb* within a Layer 3
- *		protocol header. This header is provided in the buffer at
- *		address *hdr*, with *len* its size in bytes. *type* indicates
- *		the protocol of the header and can be one of:
- *
- *		**BPF_LWT_ENCAP_SEG6**
- *			IPv6 encapsulation with Segment Routing Header
- *			(**struct ipv6_sr_hdr**). *hdr* only contains the SRH,
- *			the IPv6 header is computed by the kernel.
- *		**BPF_LWT_ENCAP_SEG6_INLINE**
- *			Only works if *skb* contains an IPv6 packet. Insert a
- *			Segment Routing Header (**struct ipv6_sr_hdr**) inside
- *			the IPv6 header.
- *		**BPF_LWT_ENCAP_IP**
- *			IP encapsulation (GRE/GUE/IPIP/etc). The outer header
- *			must be IPv4 or IPv6, followed by zero or more
- *			additional headers, up to **LWT_BPF_MAX_HEADROOM**
- *			total bytes in all prepended headers. Please note that
- *			if **skb_is_gso**\ (*skb*) is true, no more than two
- *			headers can be prepended, and the inner header, if
- *			present, should be either GRE or UDP/GUE.
- *
- *		**BPF_LWT_ENCAP_SEG6**\ \* types can be called by BPF programs
- *		of type **BPF_PROG_TYPE_LWT_IN**; **BPF_LWT_ENCAP_IP** type can
- *		be called by bpf programs of types **BPF_PROG_TYPE_LWT_IN** and
- *		**BPF_PROG_TYPE_LWT_XMIT**.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- *	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_lwt_seg6_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len)
- *	Description
- *		Store *len* bytes from address *from* into the packet
- *		associated to *skb*, at *offset*. Only the flags, tag and TLVs
- *		inside the outermost IPv6 Segment Routing Header can be
- *		modified through this helper.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- *	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_lwt_seg6_adjust_srh(struct sk_buff *skb, u32 offset, s32 delta)
- *	Description
- *		Adjust the size allocated to TLVs in the outermost IPv6
- *		Segment Routing Header contained in the packet associated to
- *		*skb*, at position *offset* by *delta* bytes. Only offsets
- *		after the segments are accepted. *delta* can be as well
- *		positive (growing) as negative (shrinking).
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- *	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_lwt_seg6_action(struct sk_buff *skb, u32 action, void *param, u32 param_len)
- *	Description
- *		Apply an IPv6 Segment Routing action of type *action* to the
- *		packet associated to *skb*. Each action takes a parameter
- *		contained at address *param*, and of length *param_len* bytes.
- *		*action* can be one of:
- *
- *		**SEG6_LOCAL_ACTION_END_X**
- *			End.X action: Endpoint with Layer-3 cross-connect.
- *			Type of *param*: **struct in6_addr**.
- *		**SEG6_LOCAL_ACTION_END_T**
- *			End.T action: Endpoint with specific IPv6 table lookup.
- *			Type of *param*: **int**.
- *		**SEG6_LOCAL_ACTION_END_B6**
- *			End.B6 action: Endpoint bound to an SRv6 policy.
- *			Type of *param*: **struct ipv6_sr_hdr**.
- *		**SEG6_LOCAL_ACTION_END_B6_ENCAP**
- *			End.B6.Encap action: Endpoint bound to an SRv6
- *			encapsulation policy.
- *			Type of *param*: **struct ipv6_sr_hdr**.
- *
- * 		A call to this helper is susceptible to change the underlying
- * 		packet buffer. Therefore, at load time, all checks on pointers
- * 		previously done by the verifier are invalidated and must be
- * 		performed again, if the helper is used in combination with
- * 		direct packet access.
- *	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_rc_repeat(void *ctx)
- *	Description
- *		This helper is used in programs implementing IR decoding, to
- *		report a successfully decoded repeat key message. This delays
- *		the generation of a key up event for previously generated
- *		key down event.
- *
- *		Some IR protocols like NEC have a special IR message for
- *		repeating last button, for when a button is held down.
- *
- *		The *ctx* should point to the lirc sample as passed into
- *		the program.
- *
- *		This helper is only available is the kernel was compiled with
- *		the **CONFIG_BPF_LIRC_MODE2** configuration option set to
- *		"**y**".
- *	Return
- *		0
- *
- * int bpf_rc_keydown(void *ctx, u32 protocol, u64 scancode, u32 toggle)
- *	Description
- *		This helper is used in programs implementing IR decoding, to
- *		report a successfully decoded key press with *scancode*,
- *		*toggle* value in the given *protocol*. The scancode will be
- *		translated to a keycode using the rc keymap, and reported as
- *		an input key down event. After a period a key up event is
- *		generated. This period can be extended by calling either
- *		**bpf_rc_keydown**\ () again with the same values, or calling
- *		**bpf_rc_repeat**\ ().
- *
- *		Some protocols include a toggle bit, in case the button	was
- *		released and pressed again between consecutive scancodes.
- *
- *		The *ctx* should point to the lirc sample as passed into
- *		the program.
- *
- *		The *protocol* is the decoded protocol number (see
- *		**enum rc_proto** for some predefined values).
- *
- *		This helper is only available is the kernel was compiled with
- *		the **CONFIG_BPF_LIRC_MODE2** configuration option set to
- *		"**y**".
- *	Return
- *		0
- *
- * u64 bpf_skb_cgroup_id(struct sk_buff *skb)
- * 	Description
- * 		Return the cgroup v2 id of the socket associated with the *skb*.
- * 		This is roughly similar to the **bpf_get_cgroup_classid**\ ()
- * 		helper for cgroup v1 by providing a tag resp. identifier that
- * 		can be matched on or used for map lookups e.g. to implement
- * 		policy. The cgroup v2 id of a given path in the hierarchy is
- * 		exposed in user space through the f_handle API in order to get
- * 		to the same 64-bit id.
- *
- * 		This helper can be used on TC egress path, but not on ingress,
- * 		and is available only if the kernel was compiled with the
- * 		**CONFIG_SOCK_CGROUP_DATA** configuration option.
- * 	Return
- * 		The id is returned or 0 in case the id could not be retrieved.
- *
- * u64 bpf_get_current_cgroup_id(void)
- * 	Return
- * 		A 64-bit integer containing the current cgroup id based
- * 		on the cgroup within which the current task is running.
- *
- * void *bpf_get_local_storage(void *map, u64 flags)
- *	Description
- *		Get the pointer to the local storage area.
- *		The type and the size of the local storage is defined
- *		by the *map* argument.
- *		The *flags* meaning is specific for each map type,
- *		and has to be 0 for cgroup local storage.
- *
- *		Depending on the BPF program type, a local storage area
- *		can be shared between multiple instances of the BPF program,
- *		running simultaneously.
- *
- *		A user should care about the synchronization by himself.
- *		For example, by using the **BPF_STX_XADD** instruction to alter
- *		the shared data.
- *	Return
- *		A pointer to the local storage area.
- *
- * int bpf_sk_select_reuseport(struct sk_reuseport_md *reuse, struct bpf_map *map, void *key, u64 flags)
- *	Description
- *		Select a **SO_REUSEPORT** socket from a
- *		**BPF_MAP_TYPE_REUSEPORT_ARRAY** *map*.
- *		It checks the selected socket is matching the incoming
- *		request in the socket buffer.
- *	Return
- *		0 on success, or a negative error in case of failure.
- *
- * u64 bpf_skb_ancestor_cgroup_id(struct sk_buff *skb, int ancestor_level)
- *	Description
- *		Return id of cgroup v2 that is ancestor of cgroup associated
- *		with the *skb* at the *ancestor_level*.  The root cgroup is at
- *		*ancestor_level* zero and each step down the hierarchy
- *		increments the level. If *ancestor_level* == level of cgroup
- *		associated with *skb*, then return value will be same as that
- *		of **bpf_skb_cgroup_id**\ ().
- *
- *		The helper is useful to implement policies based on cgroups
- *		that are upper in hierarchy than immediate cgroup associated
- *		with *skb*.
- *
- *		The format of returned id and helper limitations are same as in
- *		**bpf_skb_cgroup_id**\ ().
- *	Return
- *		The id is returned or 0 in case the id could not be retrieved.
- *
- * struct bpf_sock *bpf_sk_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags)
- *	Description
- *		Look for TCP socket matching *tuple*, optionally in a child
- *		network namespace *netns*. The return value must be checked,
- *		and if non-**NULL**, released via **bpf_sk_release**\ ().
- *
- *		The *ctx* should point to the context of the program, such as
- *		the skb or socket (depending on the hook in use). This is used
- *		to determine the base network namespace for the lookup.
- *
- *		*tuple_size* must be one of:
- *
- *		**sizeof**\ (*tuple*\ **->ipv4**)
- *			Look for an IPv4 socket.
- *		**sizeof**\ (*tuple*\ **->ipv6**)
- *			Look for an IPv6 socket.
- *
- *		If the *netns* is a negative signed 32-bit integer, then the
- *		socket lookup table in the netns associated with the *ctx* will
- *		will be used. For the TC hooks, this is the netns of the device
- *		in the skb. For socket hooks, this is the netns of the socket.
- *		If *netns* is any other signed 32-bit value greater than or
- *		equal to zero then it specifies the ID of the netns relative to
- *		the netns associated with the *ctx*. *netns* values beyond the
- *		range of 32-bit integers are reserved for future use.
- *
- *		All values for *flags* are reserved for future usage, and must
- *		be left at zero.
- *
- *		This helper is available only if the kernel was compiled with
- *		**CONFIG_NET** configuration option.
- *	Return
- *		Pointer to **struct bpf_sock**, or **NULL** in case of failure.
- *		For sockets with reuseport option, the **struct bpf_sock**
- *		result is from *reuse*\ **->socks**\ [] using the hash of the
- *		tuple.
- *
- * struct bpf_sock *bpf_sk_lookup_udp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags)
- *	Description
- *		Look for UDP socket matching *tuple*, optionally in a child
- *		network namespace *netns*. The return value must be checked,
- *		and if non-**NULL**, released via **bpf_sk_release**\ ().
- *
- *		The *ctx* should point to the context of the program, such as
- *		the skb or socket (depending on the hook in use). This is used
- *		to determine the base network namespace for the lookup.
- *
- *		*tuple_size* must be one of:
- *
- *		**sizeof**\ (*tuple*\ **->ipv4**)
- *			Look for an IPv4 socket.
- *		**sizeof**\ (*tuple*\ **->ipv6**)
- *			Look for an IPv6 socket.
- *
- *		If the *netns* is a negative signed 32-bit integer, then the
- *		socket lookup table in the netns associated with the *ctx* will
- *		will be used. For the TC hooks, this is the netns of the device
- *		in the skb. For socket hooks, this is the netns of the socket.
- *		If *netns* is any other signed 32-bit value greater than or
- *		equal to zero then it specifies the ID of the netns relative to
- *		the netns associated with the *ctx*. *netns* values beyond the
- *		range of 32-bit integers are reserved for future use.
- *
- *		All values for *flags* are reserved for future usage, and must
- *		be left at zero.
- *
- *		This helper is available only if the kernel was compiled with
- *		**CONFIG_NET** configuration option.
- *	Return
- *		Pointer to **struct bpf_sock**, or **NULL** in case of failure.
- *		For sockets with reuseport option, the **struct bpf_sock**
- *		result is from *reuse*\ **->socks**\ [] using the hash of the
- *		tuple.
- *
- * int bpf_sk_release(struct bpf_sock *sock)
- *	Description
- *		Release the reference held by *sock*. *sock* must be a
- *		non-**NULL** pointer that was returned from
- *		**bpf_sk_lookup_xxx**\ ().
- *	Return
- *		0 on success, or a negative error in case of failure.
- *
- * int bpf_map_push_elem(struct bpf_map *map, const void *value, u64 flags)
- * 	Description
- * 		Push an element *value* in *map*. *flags* is one of:
- *
- * 		**BPF_EXIST**
- * 			If the queue/stack is full, the oldest element is
- * 			removed to make room for this.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_map_pop_elem(struct bpf_map *map, void *value)
- * 	Description
- * 		Pop an element from *map*.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_map_peek_elem(struct bpf_map *map, void *value)
- * 	Description
- * 		Get an element from *map* without removing it.
- * 	Return
- * 		0 on success, or a negative error in case of failure.
- *
- * int bpf_msg_push_data(struct sk_buff *skb, u32 start, u32 len, u64 flags)
- *	Description
- *		For socket policies, insert *len* bytes into *msg* at offset
- *		*start*.
- *
- *		If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a
- *		*msg* it may want to insert metadata or options into the *msg*.
- *		This can later be read and used by any of the lower layer BPF
- *		hooks.
- *
- *		This helper may fail if under memory pressure (a malloc
- *		fails) in these cases BPF programs will get an appropriate
- *		error and BPF programs will need to handle them.
- *	Return
- *		0 on success, or a negative error in case of failure.
- *
- * int bpf_msg_pop_data(struct sk_msg_buff *msg, u32 start, u32 pop, u64 flags)
- *	Description
- *		Will remove *pop* bytes from a *msg* starting at byte *start*.
- *		This may result in **ENOMEM** errors under certain situations if
- *		an allocation and copy are required due to a full ring buffer.
- *		However, the helper will try to avoid doing the allocation
- *		if possible. Other errors can occur if input parameters are
- *		invalid either due to *start* byte not being valid part of *msg*
- *		payload and/or *pop* value being to large.
- *	Return
- *		0 on success, or a negative error in case of failure.
- *
- * int bpf_rc_pointer_rel(void *ctx, s32 rel_x, s32 rel_y)
- *	Description
- *		This helper is used in programs implementing IR decoding, to
- *		report a successfully decoded pointer movement.
- *
- *		The *ctx* should point to the lirc sample as passed into
- *		the program.
- *
- *		This helper is only available is the kernel was compiled with
- *		the **CONFIG_BPF_LIRC_MODE2** configuration option set to
- *		"**y**".
- *	Return
- *		0
- *
- * int bpf_spin_lock(struct bpf_spin_lock *lock)
- *	Description
- *		Acquire a spinlock represented by the pointer *lock*, which is
- *		stored as part of a value of a map. Taking the lock allows to
- *		safely update the rest of the fields in that value. The
- *		spinlock can (and must) later be released with a call to
- *		**bpf_spin_unlock**\ (\ *lock*\ ).
- *
- *		Spinlocks in BPF programs come with a number of restrictions
- *		and constraints:
- *
- *		* **bpf_spin_lock** objects are only allowed inside maps of
- *		  types **BPF_MAP_TYPE_HASH** and **BPF_MAP_TYPE_ARRAY** (this
- *		  list could be extended in the future).
- *		* BTF description of the map is mandatory.
- *		* The BPF program can take ONE lock at a time, since taking two
- *		  or more could cause dead locks.
- *		* Only one **struct bpf_spin_lock** is allowed per map element.
- *		* When the lock is taken, calls (either BPF to BPF or helpers)
- *		  are not allowed.
- *		* The **BPF_LD_ABS** and **BPF_LD_IND** instructions are not
- *		  allowed inside a spinlock-ed region.
- *		* The BPF program MUST call **bpf_spin_unlock**\ () to release
- *		  the lock, on all execution paths, before it returns.
- *		* The BPF program can access **struct bpf_spin_lock** only via
- *		  the **bpf_spin_lock**\ () and **bpf_spin_unlock**\ ()
- *		  helpers. Loading or storing data into the **struct
- *		  bpf_spin_lock** *lock*\ **;** field of a map is not allowed.
- *		* To use the **bpf_spin_lock**\ () helper, the BTF description
- *		  of the map value must be a struct and have **struct
- *		  bpf_spin_lock** *anyname*\ **;** field at the top level.
- *		  Nested lock inside another struct is not allowed.
- *		* The **struct bpf_spin_lock** *lock* field in a map value must
- *		  be aligned on a multiple of 4 bytes in that value.
- *		* Syscall with command **BPF_MAP_LOOKUP_ELEM** does not copy
- *		  the **bpf_spin_lock** field to user space.
- *		* Syscall with command **BPF_MAP_UPDATE_ELEM**, or update from
- *		  a BPF program, do not update the **bpf_spin_lock** field.
- *		* **bpf_spin_lock** cannot be on the stack or inside a
- *		  networking packet (it can only be inside of a map values).
- *		* **bpf_spin_lock** is available to root only.
- *		* Tracing programs and socket filter programs cannot use
- *		  **bpf_spin_lock**\ () due to insufficient preemption checks
- *		  (but this may change in the future).
- *		* **bpf_spin_lock** is not allowed in inner maps of map-in-map.
- *	Return
- *		0
- *
- * int bpf_spin_unlock(struct bpf_spin_lock *lock)
- *	Description
- *		Release the *lock* previously locked by a call to
- *		**bpf_spin_lock**\ (\ *lock*\ ).
- *	Return
- *		0
- *
- * struct bpf_sock *bpf_sk_fullsock(struct bpf_sock *sk)
- *	Description
- *		This helper gets a **struct bpf_sock** pointer such
- *		that all the fields in this **bpf_sock** can be accessed.
- *	Return
- *		A **struct bpf_sock** pointer on success, or **NULL** in
- *		case of failure.
- *
- * struct bpf_tcp_sock *bpf_tcp_sock(struct bpf_sock *sk)
- *	Description
- *		This helper gets a **struct bpf_tcp_sock** pointer from a
- *		**struct bpf_sock** pointer.
- *	Return
- *		A **struct bpf_tcp_sock** pointer on success, or **NULL** in
- *		case of failure.
- *
- * int bpf_skb_ecn_set_ce(struct sk_buf *skb)
- *	Description
- *		Set ECN (Explicit Congestion Notification) field of IP header
- *		to **CE** (Congestion Encountered) if current value is **ECT**
- *		(ECN Capable Transport). Otherwise, do nothing. Works with IPv6
- *		and IPv4.
- *	Return
- *		1 if the **CE** flag is set (either by the current helper call
- *		or because it was already present), 0 if it is not set.
- *
- * struct bpf_sock *bpf_get_listener_sock(struct bpf_sock *sk)
- *	Description
- *		Return a **struct bpf_sock** pointer in **TCP_LISTEN** state.
- *		**bpf_sk_release**\ () is unnecessary and not allowed.
- *	Return
- *		A **struct bpf_sock** pointer on success, or **NULL** in
- *		case of failure.
- *
- * struct bpf_sock *bpf_skc_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags)
- *	Description
- *		Look for TCP socket matching *tuple*, optionally in a child
- *		network namespace *netns*. The return value must be checked,
- *		and if non-**NULL**, released via **bpf_sk_release**\ ().
- *
- *		This function is identical to **bpf_sk_lookup_tcp**\ (), except
- *		that it also returns timewait or request sockets. Use
- *		**bpf_sk_fullsock**\ () or **bpf_tcp_sock**\ () to access the
- *		full structure.
- *
- *		This helper is available only if the kernel was compiled with
- *		**CONFIG_NET** configuration option.
- *	Return
- *		Pointer to **struct bpf_sock**, or **NULL** in case of failure.
- *		For sockets with reuseport option, the **struct bpf_sock**
- *		result is from *reuse*\ **->socks**\ [] using the hash of the
- *		tuple.
- *
- * int bpf_tcp_check_syncookie(struct bpf_sock *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len)
- * 	Description
- * 		Check whether *iph* and *th* contain a valid SYN cookie ACK for
- * 		the listening socket in *sk*.
- *
- * 		*iph* points to the start of the IPv4 or IPv6 header, while
- * 		*iph_len* contains **sizeof**\ (**struct iphdr**) or
- * 		**sizeof**\ (**struct ip6hdr**).
- *
- * 		*th* points to the start of the TCP header, while *th_len*
- * 		contains **sizeof**\ (**struct tcphdr**).
- *
- * 	Return
- * 		0 if *iph* and *th* are a valid SYN cookie ACK, or a negative
- * 		error otherwise.
- *
- * int bpf_sysctl_get_name(struct bpf_sysctl *ctx, char *buf, size_t buf_len, u64 flags)
- *	Description
- *		Get name of sysctl in /proc/sys/ and copy it into provided by
- *		program buffer *buf* of size *buf_len*.
- *
- *		The buffer is always NUL terminated, unless it's zero-sized.
- *
- *		If *flags* is zero, full name (e.g. "net/ipv4/tcp_mem") is
- *		copied. Use **BPF_F_SYSCTL_BASE_NAME** flag to copy base name
- *		only (e.g. "tcp_mem").
- *	Return
- *		Number of character copied (not including the trailing NUL).
- *
- *		**-E2BIG** if the buffer wasn't big enough (*buf* will contain
- *		truncated name in this case).
- *
- * int bpf_sysctl_get_current_value(struct bpf_sysctl *ctx, char *buf, size_t buf_len)
- *	Description
- *		Get current value of sysctl as it is presented in /proc/sys
- *		(incl. newline, etc), and copy it as a string into provided
- *		by program buffer *buf* of size *buf_len*.
- *
- *		The whole value is copied, no matter what file position user
- *		space issued e.g. sys_read at.
- *
- *		The buffer is always NUL terminated, unless it's zero-sized.
- *	Return
- *		Number of character copied (not including the trailing NUL).
- *
- *		**-E2BIG** if the buffer wasn't big enough (*buf* will contain
- *		truncated name in this case).
- *
- *		**-EINVAL** if current value was unavailable, e.g. because
- *		sysctl is uninitialized and read returns -EIO for it.
- *
- * int bpf_sysctl_get_new_value(struct bpf_sysctl *ctx, char *buf, size_t buf_len)
- *	Description
- *		Get new value being written by user space to sysctl (before
- *		the actual write happens) and copy it as a string into
- *		provided by program buffer *buf* of size *buf_len*.
- *
- *		User space may write new value at file position > 0.
- *
- *		The buffer is always NUL terminated, unless it's zero-sized.
- *	Return
- *		Number of character copied (not including the trailing NUL).
- *
- *		**-E2BIG** if the buffer wasn't big enough (*buf* will contain
- *		truncated name in this case).
- *
- *		**-EINVAL** if sysctl is being read.
- *
- * int bpf_sysctl_set_new_value(struct bpf_sysctl *ctx, const char *buf, size_t buf_len)
- *	Description
- *		Override new value being written by user space to sysctl with
- *		value provided by program in buffer *buf* of size *buf_len*.
- *
- *		*buf* should contain a string in same form as provided by user
- *		space on sysctl write.
- *
- *		User space may write new value at file position > 0. To override
- *		the whole sysctl value file position should be set to zero.
- *	Return
- *		0 on success.
- *
- *		**-E2BIG** if the *buf_len* is too big.
- *
- *		**-EINVAL** if sysctl is being read.
- *
- * int bpf_strtol(const char *buf, size_t buf_len, u64 flags, long *res)
- *	Description
- *		Convert the initial part of the string from buffer *buf* of
- *		size *buf_len* to a long integer according to the given base
- *		and save the result in *res*.
- *
- *		The string may begin with an arbitrary amount of white space
- *		(as determined by **isspace**\ (3)) followed by a single
- *		optional '**-**' sign.
- *
- *		Five least significant bits of *flags* encode base, other bits
- *		are currently unused.
- *
- *		Base must be either 8, 10, 16 or 0 to detect it automatically
- *		similar to user space **strtol**\ (3).
- *	Return
- *		Number of characters consumed on success. Must be positive but
- *		no more than *buf_len*.
- *
- *		**-EINVAL** if no valid digits were found or unsupported base
- *		was provided.
- *
- *		**-ERANGE** if resulting value was out of range.
- *
- * int bpf_strtoul(const char *buf, size_t buf_len, u64 flags, unsigned long *res)
- *	Description
- *		Convert the initial part of the string from buffer *buf* of
- *		size *buf_len* to an unsigned long integer according to the
- *		given base and save the result in *res*.
- *
- *		The string may begin with an arbitrary amount of white space
- *		(as determined by **isspace**\ (3)).
- *
- *		Five least significant bits of *flags* encode base, other bits
- *		are currently unused.
- *
- *		Base must be either 8, 10, 16 or 0 to detect it automatically
- *		similar to user space **strtoul**\ (3).
- *	Return
- *		Number of characters consumed on success. Must be positive but
- *		no more than *buf_len*.
- *
- *		**-EINVAL** if no valid digits were found or unsupported base
- *		was provided.
- *
- *		**-ERANGE** if resulting value was out of range.
- *
- * void *bpf_sk_storage_get(struct bpf_map *map, struct bpf_sock *sk, void *value, u64 flags)
- *	Description
- *		Get a bpf-local-storage from a *sk*.
- *
- *		Logically, it could be thought of getting the value from
- *		a *map* with *sk* as the **key**.  From this
- *		perspective,  the usage is not much different from
- *		**bpf_map_lookup_elem**\ (*map*, **&**\ *sk*) except this
- *		helper enforces the key must be a full socket and the map must
- *		be a **BPF_MAP_TYPE_SK_STORAGE** also.
- *
- *		Underneath, the value is stored locally at *sk* instead of
- *		the *map*.  The *map* is used as the bpf-local-storage
- *		"type". The bpf-local-storage "type" (i.e. the *map*) is
- *		searched against all bpf-local-storages residing at *sk*.
- *
- *		An optional *flags* (**BPF_SK_STORAGE_GET_F_CREATE**) can be
- *		used such that a new bpf-local-storage will be
- *		created if one does not exist.  *value* can be used
- *		together with **BPF_SK_STORAGE_GET_F_CREATE** to specify
- *		the initial value of a bpf-local-storage.  If *value* is
- *		**NULL**, the new bpf-local-storage will be zero initialized.
- *	Return
- *		A bpf-local-storage pointer is returned on success.
- *
- *		**NULL** if not found or there was an error in adding
- *		a new bpf-local-storage.
- *
- * int bpf_sk_storage_delete(struct bpf_map *map, struct bpf_sock *sk)
- *	Description
- *		Delete a bpf-local-storage from a *sk*.
- *	Return
- *		0 on success.
- *
- *		**-ENOENT** if the bpf-local-storage cannot be found.
- *
- * int bpf_send_signal(u32 sig)
- *	Description
- *		Send signal *sig* to the current task.
- *	Return
- *		0 on success or successfully queued.
- *
- *		**-EBUSY** if work queue under nmi is full.
- *
- *		**-EINVAL** if *sig* is invalid.
- *
- *		**-EPERM** if no permission to send the *sig*.
- *
- *		**-EAGAIN** if bpf program can try again.
- *
- * s64 bpf_tcp_gen_syncookie(struct bpf_sock *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len)
- *	Description
- *		Try to issue a SYN cookie for the packet with corresponding
- *		IP/TCP headers, *iph* and *th*, on the listening socket in *sk*.
- *
- *		*iph* points to the start of the IPv4 or IPv6 header, while
- *		*iph_len* contains **sizeof**\ (**struct iphdr**) or
- *		**sizeof**\ (**struct ip6hdr**).
- *
- *		*th* points to the start of the TCP header, while *th_len*
- *		contains the length of the TCP header.
- *
- *	Return
- *		On success, lower 32 bits hold the generated SYN cookie in
- *		followed by 16 bits which hold the MSS value for that cookie,
- *		and the top 16 bits are unused.
- *
- *		On failure, the returned value is one of the following:
- *
- *		**-EINVAL** SYN cookie cannot be issued due to error
- *
- *		**-ENOENT** SYN cookie should not be issued (no SYN flood)
- *
- *		**-EOPNOTSUPP** kernel configuration does not enable SYN cookies
- *
- *		**-EPROTONOSUPPORT** IP packet version is not 4 or 6
- */
-#define __BPF_FUNC_MAPPER(FN)		\
-	FN(unspec),			\
-	FN(map_lookup_elem),		\
-	FN(map_update_elem),		\
-	FN(map_delete_elem),		\
-	FN(probe_read),			\
-	FN(ktime_get_ns),		\
-	FN(trace_printk),		\
-	FN(get_prandom_u32),		\
-	FN(get_smp_processor_id),	\
-	FN(skb_store_bytes),		\
-	FN(l3_csum_replace),		\
-	FN(l4_csum_replace),		\
-	FN(tail_call),			\
-	FN(clone_redirect),		\
-	FN(get_current_pid_tgid),	\
-	FN(get_current_uid_gid),	\
-	FN(get_current_comm),		\
-	FN(get_cgroup_classid),		\
-	FN(skb_vlan_push),		\
-	FN(skb_vlan_pop),		\
-	FN(skb_get_tunnel_key),		\
-	FN(skb_set_tunnel_key),		\
-	FN(perf_event_read),		\
-	FN(redirect),			\
-	FN(get_route_realm),		\
-	FN(perf_event_output),		\
-	FN(skb_load_bytes),		\
-	FN(get_stackid),		\
-	FN(csum_diff),			\
-	FN(skb_get_tunnel_opt),		\
-	FN(skb_set_tunnel_opt),		\
-	FN(skb_change_proto),		\
-	FN(skb_change_type),		\
-	FN(skb_under_cgroup),		\
-	FN(get_hash_recalc),		\
-	FN(get_current_task),		\
-	FN(probe_write_user),		\
-	FN(current_task_under_cgroup),	\
-	FN(skb_change_tail),		\
-	FN(skb_pull_data),		\
-	FN(csum_update),		\
-	FN(set_hash_invalid),		\
-	FN(get_numa_node_id),		\
-	FN(skb_change_head),		\
-	FN(xdp_adjust_head),		\
-	FN(probe_read_str),		\
-	FN(get_socket_cookie),		\
-	FN(get_socket_uid),		\
-	FN(set_hash),			\
-	FN(setsockopt),			\
-	FN(skb_adjust_room),		\
-	FN(redirect_map),		\
-	FN(sk_redirect_map),		\
-	FN(sock_map_update),		\
-	FN(xdp_adjust_meta),		\
-	FN(perf_event_read_value),	\
-	FN(perf_prog_read_value),	\
-	FN(getsockopt),			\
-	FN(override_return),		\
-	FN(sock_ops_cb_flags_set),	\
-	FN(msg_redirect_map),		\
-	FN(msg_apply_bytes),		\
-	FN(msg_cork_bytes),		\
-	FN(msg_pull_data),		\
-	FN(bind),			\
-	FN(xdp_adjust_tail),		\
-	FN(skb_get_xfrm_state),		\
-	FN(get_stack),			\
-	FN(skb_load_bytes_relative),	\
-	FN(fib_lookup),			\
-	FN(sock_hash_update),		\
-	FN(msg_redirect_hash),		\
-	FN(sk_redirect_hash),		\
-	FN(lwt_push_encap),		\
-	FN(lwt_seg6_store_bytes),	\
-	FN(lwt_seg6_adjust_srh),	\
-	FN(lwt_seg6_action),		\
-	FN(rc_repeat),			\
-	FN(rc_keydown),			\
-	FN(skb_cgroup_id),		\
-	FN(get_current_cgroup_id),	\
-	FN(get_local_storage),		\
-	FN(sk_select_reuseport),	\
-	FN(skb_ancestor_cgroup_id),	\
-	FN(sk_lookup_tcp),		\
-	FN(sk_lookup_udp),		\
-	FN(sk_release),			\
-	FN(map_push_elem),		\
-	FN(map_pop_elem),		\
-	FN(map_peek_elem),		\
-	FN(msg_push_data),		\
-	FN(msg_pop_data),		\
-	FN(rc_pointer_rel),		\
-	FN(spin_lock),			\
-	FN(spin_unlock),		\
-	FN(sk_fullsock),		\
-	FN(tcp_sock),			\
-	FN(skb_ecn_set_ce),		\
-	FN(get_listener_sock),		\
-	FN(skc_lookup_tcp),		\
-	FN(tcp_check_syncookie),	\
-	FN(sysctl_get_name),		\
-	FN(sysctl_get_current_value),	\
-	FN(sysctl_get_new_value),	\
-	FN(sysctl_set_new_value),	\
-	FN(strtol),			\
-	FN(strtoul),			\
-	FN(sk_storage_get),		\
-	FN(sk_storage_delete),		\
-	FN(send_signal),		\
-	FN(tcp_gen_syncookie),
-
-/* integer value in 'imm' field of BPF_CALL instruction selects which helper
- * function eBPF program intends to call
- */
-#define __BPF_ENUM_FN(x) BPF_FUNC_ ## x
-enum bpf_func_id {
-	__BPF_FUNC_MAPPER(__BPF_ENUM_FN)
-	__BPF_FUNC_MAX_ID,
-};
-#undef __BPF_ENUM_FN
-
-/* All flags used by eBPF helper functions, placed here. */
-
-/* BPF_FUNC_skb_store_bytes flags. */
-#define BPF_F_RECOMPUTE_CSUM		(1ULL << 0)
-#define BPF_F_INVALIDATE_HASH		(1ULL << 1)
-
-/* BPF_FUNC_l3_csum_replace and BPF_FUNC_l4_csum_replace flags.
- * First 4 bits are for passing the header field size.
- */
-#define BPF_F_HDR_FIELD_MASK		0xfULL
-
-/* BPF_FUNC_l4_csum_replace flags. */
-#define BPF_F_PSEUDO_HDR		(1ULL << 4)
-#define BPF_F_MARK_MANGLED_0		(1ULL << 5)
-#define BPF_F_MARK_ENFORCE		(1ULL << 6)
-
-/* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */
-#define BPF_F_INGRESS			(1ULL << 0)
-
-/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */
-#define BPF_F_TUNINFO_IPV6		(1ULL << 0)
-
-/* flags for both BPF_FUNC_get_stackid and BPF_FUNC_get_stack. */
-#define BPF_F_SKIP_FIELD_MASK		0xffULL
-#define BPF_F_USER_STACK		(1ULL << 8)
-/* flags used by BPF_FUNC_get_stackid only. */
-#define BPF_F_FAST_STACK_CMP		(1ULL << 9)
-#define BPF_F_REUSE_STACKID		(1ULL << 10)
-/* flags used by BPF_FUNC_get_stack only. */
-#define BPF_F_USER_BUILD_ID		(1ULL << 11)
-
-/* BPF_FUNC_skb_set_tunnel_key flags. */
-#define BPF_F_ZERO_CSUM_TX		(1ULL << 1)
-#define BPF_F_DONT_FRAGMENT		(1ULL << 2)
-#define BPF_F_SEQ_NUMBER		(1ULL << 3)
-
-/* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and
- * BPF_FUNC_perf_event_read_value flags.
- */
-#define BPF_F_INDEX_MASK		0xffffffffULL
-#define BPF_F_CURRENT_CPU		BPF_F_INDEX_MASK
-/* BPF_FUNC_perf_event_output for sk_buff input context. */
-#define BPF_F_CTXLEN_MASK		(0xfffffULL << 32)
-
-/* Current network namespace */
-#define BPF_F_CURRENT_NETNS		(-1L)
-
-/* BPF_FUNC_skb_adjust_room flags. */
-#define BPF_F_ADJ_ROOM_FIXED_GSO	(1ULL << 0)
-
-#define BPF_ADJ_ROOM_ENCAP_L2_MASK	0xff
-#define BPF_ADJ_ROOM_ENCAP_L2_SHIFT	56
-
-#define BPF_F_ADJ_ROOM_ENCAP_L3_IPV4	(1ULL << 1)
-#define BPF_F_ADJ_ROOM_ENCAP_L3_IPV6	(1ULL << 2)
-#define BPF_F_ADJ_ROOM_ENCAP_L4_GRE	(1ULL << 3)
-#define BPF_F_ADJ_ROOM_ENCAP_L4_UDP	(1ULL << 4)
-#define BPF_F_ADJ_ROOM_ENCAP_L2(len)	(((__u64)len & \
-					  BPF_ADJ_ROOM_ENCAP_L2_MASK) \
-					 << BPF_ADJ_ROOM_ENCAP_L2_SHIFT)
-
-/* BPF_FUNC_sysctl_get_name flags. */
-#define BPF_F_SYSCTL_BASE_NAME		(1ULL << 0)
-
-/* BPF_FUNC_sk_storage_get flags */
-#define BPF_SK_STORAGE_GET_F_CREATE	(1ULL << 0)
-
-/* Mode for BPF_FUNC_skb_adjust_room helper. */
-enum bpf_adj_room_mode {
-	BPF_ADJ_ROOM_NET,
-	BPF_ADJ_ROOM_MAC,
-};
-
-/* Mode for BPF_FUNC_skb_load_bytes_relative helper. */
-enum bpf_hdr_start_off {
-	BPF_HDR_START_MAC,
-	BPF_HDR_START_NET,
-};
-
-/* Encapsulation type for BPF_FUNC_lwt_push_encap helper. */
-enum bpf_lwt_encap_mode {
-	BPF_LWT_ENCAP_SEG6,
-	BPF_LWT_ENCAP_SEG6_INLINE,
-	BPF_LWT_ENCAP_IP,
-};
-
-#define __bpf_md_ptr(type, name)	\
-union {					\
-	type name;			\
-	__u64 :64;			\
-} __attribute__((aligned(8)))
-
-/* user accessible mirror of in-kernel sk_buff.
- * new fields can only be added to the end of this structure
- */
-struct __sk_buff {
-	__u32 len;
-	__u32 pkt_type;
-	__u32 mark;
-	__u32 queue_mapping;
-	__u32 protocol;
-	__u32 vlan_present;
-	__u32 vlan_tci;
-	__u32 vlan_proto;
-	__u32 priority;
-	__u32 ingress_ifindex;
-	__u32 ifindex;
-	__u32 tc_index;
-	__u32 cb[5];
-	__u32 hash;
-	__u32 tc_classid;
-	__u32 data;
-	__u32 data_end;
-	__u32 napi_id;
-
-	/* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */
-	__u32 family;
-	__u32 remote_ip4;	/* Stored in network byte order */
-	__u32 local_ip4;	/* Stored in network byte order */
-	__u32 remote_ip6[4];	/* Stored in network byte order */
-	__u32 local_ip6[4];	/* Stored in network byte order */
-	__u32 remote_port;	/* Stored in network byte order */
-	__u32 local_port;	/* stored in host byte order */
-	/* ... here. */
-
-	__u32 data_meta;
-	__bpf_md_ptr(struct bpf_flow_keys *, flow_keys);
-	__u64 tstamp;
-	__u32 wire_len;
-	__u32 gso_segs;
-	__bpf_md_ptr(struct bpf_sock *, sk);
-};
-
-struct bpf_tunnel_key {
-	__u32 tunnel_id;
-	union {
-		__u32 remote_ipv4;
-		__u32 remote_ipv6[4];
-	};
-	__u8 tunnel_tos;
-	__u8 tunnel_ttl;
-	__u16 tunnel_ext;	/* Padding, future use. */
-	__u32 tunnel_label;
-};
-
-/* user accessible mirror of in-kernel xfrm_state.
- * new fields can only be added to the end of this structure
- */
-struct bpf_xfrm_state {
-	__u32 reqid;
-	__u32 spi;	/* Stored in network byte order */
-	__u16 family;
-	__u16 ext;	/* Padding, future use. */
-	union {
-		__u32 remote_ipv4;	/* Stored in network byte order */
-		__u32 remote_ipv6[4];	/* Stored in network byte order */
-	};
-};
-
-/* Generic BPF return codes which all BPF program types may support.
- * The values are binary compatible with their TC_ACT_* counter-part to
- * provide backwards compatibility with existing SCHED_CLS and SCHED_ACT
- * programs.
- *
- * XDP is handled seprately, see XDP_*.
- */
-enum bpf_ret_code {
-	BPF_OK = 0,
-	/* 1 reserved */
-	BPF_DROP = 2,
-	/* 3-6 reserved */
-	BPF_REDIRECT = 7,
-	/* >127 are reserved for prog type specific return codes.
-	 *
-	 * BPF_LWT_REROUTE: used by BPF_PROG_TYPE_LWT_IN and
-	 *    BPF_PROG_TYPE_LWT_XMIT to indicate that skb had been
-	 *    changed and should be routed based on its new L3 header.
-	 *    (This is an L3 redirect, as opposed to L2 redirect
-	 *    represented by BPF_REDIRECT above).
-	 */
-	BPF_LWT_REROUTE = 128,
-};
-
-struct bpf_sock {
-	__u32 bound_dev_if;
-	__u32 family;
-	__u32 type;
-	__u32 protocol;
-	__u32 mark;
-	__u32 priority;
-	/* IP address also allows 1 and 2 bytes access */
-	__u32 src_ip4;
-	__u32 src_ip6[4];
-	__u32 src_port;		/* host byte order */
-	__u32 dst_port;		/* network byte order */
-	__u32 dst_ip4;
-	__u32 dst_ip6[4];
-	__u32 state;
-};
-
-struct bpf_tcp_sock {
-	__u32 snd_cwnd;		/* Sending congestion window		*/
-	__u32 srtt_us;		/* smoothed round trip time << 3 in usecs */
-	__u32 rtt_min;
-	__u32 snd_ssthresh;	/* Slow start size threshold		*/
-	__u32 rcv_nxt;		/* What we want to receive next		*/
-	__u32 snd_nxt;		/* Next sequence we send		*/
-	__u32 snd_una;		/* First byte we want an ack for	*/
-	__u32 mss_cache;	/* Cached effective mss, not including SACKS */
-	__u32 ecn_flags;	/* ECN status bits.			*/
-	__u32 rate_delivered;	/* saved rate sample: packets delivered */
-	__u32 rate_interval_us;	/* saved rate sample: time elapsed */
-	__u32 packets_out;	/* Packets which are "in flight"	*/
-	__u32 retrans_out;	/* Retransmitted packets out		*/
-	__u32 total_retrans;	/* Total retransmits for entire connection */
-	__u32 segs_in;		/* RFC4898 tcpEStatsPerfSegsIn
-				 * total number of segments in.
-				 */
-	__u32 data_segs_in;	/* RFC4898 tcpEStatsPerfDataSegsIn
-				 * total number of data segments in.
-				 */
-	__u32 segs_out;		/* RFC4898 tcpEStatsPerfSegsOut
-				 * The total number of segments sent.
-				 */
-	__u32 data_segs_out;	/* RFC4898 tcpEStatsPerfDataSegsOut
-				 * total number of data segments sent.
-				 */
-	__u32 lost_out;		/* Lost packets			*/
-	__u32 sacked_out;	/* SACK'd packets			*/
-	__u64 bytes_received;	/* RFC4898 tcpEStatsAppHCThruOctetsReceived
-				 * sum(delta(rcv_nxt)), or how many bytes
-				 * were acked.
-				 */
-	__u64 bytes_acked;	/* RFC4898 tcpEStatsAppHCThruOctetsAcked
-				 * sum(delta(snd_una)), or how many bytes
-				 * were acked.
-				 */
-	__u32 dsack_dups;	/* RFC4898 tcpEStatsStackDSACKDups
-				 * total number of DSACK blocks received
-				 */
-	__u32 delivered;	/* Total data packets delivered incl. rexmits */
-	__u32 delivered_ce;	/* Like the above but only ECE marked packets */
-	__u32 icsk_retransmits;	/* Number of unrecovered [RTO] timeouts */
-};
-
-struct bpf_sock_tuple {
-	union {
-		struct {
-			__be32 saddr;
-			__be32 daddr;
-			__be16 sport;
-			__be16 dport;
-		} ipv4;
-		struct {
-			__be32 saddr[4];
-			__be32 daddr[4];
-			__be16 sport;
-			__be16 dport;
-		} ipv6;
-	};
-};
-
-struct bpf_xdp_sock {
-	__u32 queue_id;
-};
-
-#define XDP_PACKET_HEADROOM 256
-
-/* User return codes for XDP prog type.
- * A valid XDP program must return one of these defined values. All other
- * return codes are reserved for future use. Unknown return codes will
- * result in packet drops and a warning via bpf_warn_invalid_xdp_action().
- */
-enum xdp_action {
-	XDP_ABORTED = 0,
-	XDP_DROP,
-	XDP_PASS,
-	XDP_TX,
-	XDP_REDIRECT,
-};
-
-/* user accessible metadata for XDP packet hook
- * new fields must be added to the end of this structure
- */
-struct xdp_md {
-	__u32 data;
-	__u32 data_end;
-	__u32 data_meta;
-	/* Below access go through struct xdp_rxq_info */
-	__u32 ingress_ifindex; /* rxq->dev->ifindex */
-	__u32 rx_queue_index;  /* rxq->queue_index  */
-};
-
-enum sk_action {
-	SK_DROP = 0,
-	SK_PASS,
-};
-
-/* user accessible metadata for SK_MSG packet hook, new fields must
- * be added to the end of this structure
- */
-struct sk_msg_md {
-	__bpf_md_ptr(void *, data);
-	__bpf_md_ptr(void *, data_end);
-
-	__u32 family;
-	__u32 remote_ip4;	/* Stored in network byte order */
-	__u32 local_ip4;	/* Stored in network byte order */
-	__u32 remote_ip6[4];	/* Stored in network byte order */
-	__u32 local_ip6[4];	/* Stored in network byte order */
-	__u32 remote_port;	/* Stored in network byte order */
-	__u32 local_port;	/* stored in host byte order */
-	__u32 size;		/* Total size of sk_msg */
-};
-
-struct sk_reuseport_md {
-	/*
-	 * Start of directly accessible data. It begins from
-	 * the tcp/udp header.
-	 */
-	__bpf_md_ptr(void *, data);
-	/* End of directly accessible data */
-	__bpf_md_ptr(void *, data_end);
-	/*
-	 * Total length of packet (starting from the tcp/udp header).
-	 * Note that the directly accessible bytes (data_end - data)
-	 * could be less than this "len".  Those bytes could be
-	 * indirectly read by a helper "bpf_skb_load_bytes()".
-	 */
-	__u32 len;
-	/*
-	 * Eth protocol in the mac header (network byte order). e.g.
-	 * ETH_P_IP(0x0800) and ETH_P_IPV6(0x86DD)
-	 */
-	__u32 eth_protocol;
-	__u32 ip_protocol;	/* IP protocol. e.g. IPPROTO_TCP, IPPROTO_UDP */
-	__u32 bind_inany;	/* Is sock bound to an INANY address? */
-	__u32 hash;		/* A hash of the packet 4 tuples */
-};
-
-#define BPF_TAG_SIZE	8
-
-struct bpf_prog_info {
-	__u32 type;
-	__u32 id;
-	__u8  tag[BPF_TAG_SIZE];
-	__u32 jited_prog_len;
-	__u32 xlated_prog_len;
-	__aligned_u64 jited_prog_insns;
-	__aligned_u64 xlated_prog_insns;
-	__u64 load_time;	/* ns since boottime */
-	__u32 created_by_uid;
-	__u32 nr_map_ids;
-	__aligned_u64 map_ids;
-	char name[BPF_OBJ_NAME_LEN];
-	__u32 ifindex;
-	__u32 gpl_compatible:1;
-	__u32 :31; /* alignment pad */
-	__u64 netns_dev;
-	__u64 netns_ino;
-	__u32 nr_jited_ksyms;
-	__u32 nr_jited_func_lens;
-	__aligned_u64 jited_ksyms;
-	__aligned_u64 jited_func_lens;
-	__u32 btf_id;
-	__u32 func_info_rec_size;
-	__aligned_u64 func_info;
-	__u32 nr_func_info;
-	__u32 nr_line_info;
-	__aligned_u64 line_info;
-	__aligned_u64 jited_line_info;
-	__u32 nr_jited_line_info;
-	__u32 line_info_rec_size;
-	__u32 jited_line_info_rec_size;
-	__u32 nr_prog_tags;
-	__aligned_u64 prog_tags;
-	__u64 run_time_ns;
-	__u64 run_cnt;
-} __attribute__((aligned(8)));
-
-struct bpf_map_info {
-	__u32 type;
-	__u32 id;
-	__u32 key_size;
-	__u32 value_size;
-	__u32 max_entries;
-	__u32 map_flags;
-	char  name[BPF_OBJ_NAME_LEN];
-	__u32 ifindex;
-	__u32 :32;
-	__u64 netns_dev;
-	__u64 netns_ino;
-	__u32 btf_id;
-	__u32 btf_key_type_id;
-	__u32 btf_value_type_id;
-} __attribute__((aligned(8)));
-
-struct bpf_btf_info {
-	__aligned_u64 btf;
-	__u32 btf_size;
-	__u32 id;
-} __attribute__((aligned(8)));
-
-/* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
- * by user and intended to be used by socket (e.g. to bind to, depends on
- * attach attach type).
- */
-struct bpf_sock_addr {
-	__u32 user_family;	/* Allows 4-byte read, but no write. */
-	__u32 user_ip4;		/* Allows 1,2,4-byte read and 4-byte write.
-				 * Stored in network byte order.
-				 */
-	__u32 user_ip6[4];	/* Allows 1,2,4,8-byte read and 4,8-byte write.
-				 * Stored in network byte order.
-				 */
-	__u32 user_port;	/* Allows 4-byte read and write.
-				 * Stored in network byte order
-				 */
-	__u32 family;		/* Allows 4-byte read, but no write */
-	__u32 type;		/* Allows 4-byte read, but no write */
-	__u32 protocol;		/* Allows 4-byte read, but no write */
-	__u32 msg_src_ip4;	/* Allows 1,2,4-byte read and 4-byte write.
-				 * Stored in network byte order.
-				 */
-	__u32 msg_src_ip6[4];	/* Allows 1,2,4,8-byte read and 4,8-byte write.
-				 * Stored in network byte order.
-				 */
-	__bpf_md_ptr(struct bpf_sock *, sk);
-};
-
-/* User bpf_sock_ops struct to access socket values and specify request ops
- * and their replies.
- * Some of this fields are in network (bigendian) byte order and may need
- * to be converted before use (bpf_ntohl() defined in samples/bpf/bpf_endian.h).
- * New fields can only be added at the end of this structure
- */
-struct bpf_sock_ops {
-	__u32 op;
-	union {
-		__u32 args[4];		/* Optionally passed to bpf program */
-		__u32 reply;		/* Returned by bpf program	    */
-		__u32 replylong[4];	/* Optionally returned by bpf prog  */
-	};
-	__u32 family;
-	__u32 remote_ip4;	/* Stored in network byte order */
-	__u32 local_ip4;	/* Stored in network byte order */
-	__u32 remote_ip6[4];	/* Stored in network byte order */
-	__u32 local_ip6[4];	/* Stored in network byte order */
-	__u32 remote_port;	/* Stored in network byte order */
-	__u32 local_port;	/* stored in host byte order */
-	__u32 is_fullsock;	/* Some TCP fields are only valid if
-				 * there is a full socket. If not, the
-				 * fields read as zero.
-				 */
-	__u32 snd_cwnd;
-	__u32 srtt_us;		/* Averaged RTT << 3 in usecs */
-	__u32 bpf_sock_ops_cb_flags; /* flags defined in uapi/linux/tcp.h */
-	__u32 state;
-	__u32 rtt_min;
-	__u32 snd_ssthresh;
-	__u32 rcv_nxt;
-	__u32 snd_nxt;
-	__u32 snd_una;
-	__u32 mss_cache;
-	__u32 ecn_flags;
-	__u32 rate_delivered;
-	__u32 rate_interval_us;
-	__u32 packets_out;
-	__u32 retrans_out;
-	__u32 total_retrans;
-	__u32 segs_in;
-	__u32 data_segs_in;
-	__u32 segs_out;
-	__u32 data_segs_out;
-	__u32 lost_out;
-	__u32 sacked_out;
-	__u32 sk_txhash;
-	__u64 bytes_received;
-	__u64 bytes_acked;
-	__bpf_md_ptr(struct bpf_sock *, sk);
-};
-
-/* Definitions for bpf_sock_ops_cb_flags */
-#define BPF_SOCK_OPS_RTO_CB_FLAG	(1<<0)
-#define BPF_SOCK_OPS_RETRANS_CB_FLAG	(1<<1)
-#define BPF_SOCK_OPS_STATE_CB_FLAG	(1<<2)
-#define BPF_SOCK_OPS_RTT_CB_FLAG	(1<<3)
-#define BPF_SOCK_OPS_ALL_CB_FLAGS       0xF		/* Mask of all currently
-							 * supported cb flags
-							 */
-
-/* List of known BPF sock_ops operators.
- * New entries can only be added at the end
- */
-enum {
-	BPF_SOCK_OPS_VOID,
-	BPF_SOCK_OPS_TIMEOUT_INIT,	/* Should return SYN-RTO value to use or
-					 * -1 if default value should be used
-					 */
-	BPF_SOCK_OPS_RWND_INIT,		/* Should return initial advertized
-					 * window (in packets) or -1 if default
-					 * value should be used
-					 */
-	BPF_SOCK_OPS_TCP_CONNECT_CB,	/* Calls BPF program right before an
-					 * active connection is initialized
-					 */
-	BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB,	/* Calls BPF program when an
-						 * active connection is
-						 * established
-						 */
-	BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB,	/* Calls BPF program when a
-						 * passive connection is
-						 * established
-						 */
-	BPF_SOCK_OPS_NEEDS_ECN,		/* If connection's congestion control
-					 * needs ECN
-					 */
-	BPF_SOCK_OPS_BASE_RTT,		/* Get base RTT. The correct value is
-					 * based on the path and may be
-					 * dependent on the congestion control
-					 * algorithm. In general it indicates
-					 * a congestion threshold. RTTs above
-					 * this indicate congestion
-					 */
-	BPF_SOCK_OPS_RTO_CB,		/* Called when an RTO has triggered.
-					 * Arg1: value of icsk_retransmits
-					 * Arg2: value of icsk_rto
-					 * Arg3: whether RTO has expired
-					 */
-	BPF_SOCK_OPS_RETRANS_CB,	/* Called when skb is retransmitted.
-					 * Arg1: sequence number of 1st byte
-					 * Arg2: # segments
-					 * Arg3: return value of
-					 *       tcp_transmit_skb (0 => success)
-					 */
-	BPF_SOCK_OPS_STATE_CB,		/* Called when TCP changes state.
-					 * Arg1: old_state
-					 * Arg2: new_state
-					 */
-	BPF_SOCK_OPS_TCP_LISTEN_CB,	/* Called on listen(2), right after
-					 * socket transition to LISTEN state.
-					 */
-	BPF_SOCK_OPS_RTT_CB,		/* Called on every RTT.
-					 */
-};
-
-/* List of TCP states. There is a build check in net/ipv4/tcp.c to detect
- * changes between the TCP and BPF versions. Ideally this should never happen.
- * If it does, we need to add code to convert them before calling
- * the BPF sock_ops function.
- */
-enum {
-	BPF_TCP_ESTABLISHED = 1,
-	BPF_TCP_SYN_SENT,
-	BPF_TCP_SYN_RECV,
-	BPF_TCP_FIN_WAIT1,
-	BPF_TCP_FIN_WAIT2,
-	BPF_TCP_TIME_WAIT,
-	BPF_TCP_CLOSE,
-	BPF_TCP_CLOSE_WAIT,
-	BPF_TCP_LAST_ACK,
-	BPF_TCP_LISTEN,
-	BPF_TCP_CLOSING,	/* Now a valid state */
-	BPF_TCP_NEW_SYN_RECV,
-
-	BPF_TCP_MAX_STATES	/* Leave at the end! */
-};
-
-#define TCP_BPF_IW		1001	/* Set TCP initial congestion window */
-#define TCP_BPF_SNDCWND_CLAMP	1002	/* Set sndcwnd_clamp */
-
-struct bpf_perf_event_value {
-	__u64 counter;
-	__u64 enabled;
-	__u64 running;
-};
-
-#define BPF_DEVCG_ACC_MKNOD	(1ULL << 0)
-#define BPF_DEVCG_ACC_READ	(1ULL << 1)
-#define BPF_DEVCG_ACC_WRITE	(1ULL << 2)
-
-#define BPF_DEVCG_DEV_BLOCK	(1ULL << 0)
-#define BPF_DEVCG_DEV_CHAR	(1ULL << 1)
-
-struct bpf_cgroup_dev_ctx {
-	/* access_type encoded as (BPF_DEVCG_ACC_* << 16) | BPF_DEVCG_DEV_* */
-	__u32 access_type;
-	__u32 major;
-	__u32 minor;
-};
-
-struct bpf_raw_tracepoint_args {
-	__u64 args[0];
-};
-
-/* DIRECT:  Skip the FIB rules and go to FIB table associated with device
- * OUTPUT:  Do lookup from egress perspective; default is ingress
- */
-#define BPF_FIB_LOOKUP_DIRECT  (1U << 0)
-#define BPF_FIB_LOOKUP_OUTPUT  (1U << 1)
-
-enum {
-	BPF_FIB_LKUP_RET_SUCCESS,      /* lookup successful */
-	BPF_FIB_LKUP_RET_BLACKHOLE,    /* dest is blackholed; can be dropped */
-	BPF_FIB_LKUP_RET_UNREACHABLE,  /* dest is unreachable; can be dropped */
-	BPF_FIB_LKUP_RET_PROHIBIT,     /* dest not allowed; can be dropped */
-	BPF_FIB_LKUP_RET_NOT_FWDED,    /* packet is not forwarded */
-	BPF_FIB_LKUP_RET_FWD_DISABLED, /* fwding is not enabled on ingress */
-	BPF_FIB_LKUP_RET_UNSUPP_LWT,   /* fwd requires encapsulation */
-	BPF_FIB_LKUP_RET_NO_NEIGH,     /* no neighbor entry for nh */
-	BPF_FIB_LKUP_RET_FRAG_NEEDED,  /* fragmentation required to fwd */
-};
-
-struct bpf_fib_lookup {
-	/* input:  network family for lookup (AF_INET, AF_INET6)
-	 * output: network family of egress nexthop
-	 */
-	__u8	family;
-
-	/* set if lookup is to consider L4 data - e.g., FIB rules */
-	__u8	l4_protocol;
-	__be16	sport;
-	__be16	dport;
-
-	/* total length of packet from network header - used for MTU check */
-	__u16	tot_len;
-
-	/* input: L3 device index for lookup
-	 * output: device index from FIB lookup
-	 */
-	__u32	ifindex;
-
-	union {
-		/* inputs to lookup */
-		__u8	tos;		/* AF_INET  */
-		__be32	flowinfo;	/* AF_INET6, flow_label + priority */
-
-		/* output: metric of fib result (IPv4/IPv6 only) */
-		__u32	rt_metric;
-	};
-
-	union {
-		__be32		ipv4_src;
-		__u32		ipv6_src[4];  /* in6_addr; network order */
-	};
-
-	/* input to bpf_fib_lookup, ipv{4,6}_dst is destination address in
-	 * network header. output: bpf_fib_lookup sets to gateway address
-	 * if FIB lookup returns gateway route
-	 */
-	union {
-		__be32		ipv4_dst;
-		__u32		ipv6_dst[4];  /* in6_addr; network order */
-	};
-
-	/* output */
-	__be16	h_vlan_proto;
-	__be16	h_vlan_TCI;
-	__u8	smac[6];     /* ETH_ALEN */
-	__u8	dmac[6];     /* ETH_ALEN */
-};
-
-enum bpf_task_fd_type {
-	BPF_FD_TYPE_RAW_TRACEPOINT,	/* tp name */
-	BPF_FD_TYPE_TRACEPOINT,		/* tp name */
-	BPF_FD_TYPE_KPROBE,		/* (symbol + offset) or addr */
-	BPF_FD_TYPE_KRETPROBE,		/* (symbol + offset) or addr */
-	BPF_FD_TYPE_UPROBE,		/* filename + offset */
-	BPF_FD_TYPE_URETPROBE,		/* filename + offset */
-};
-
-#define BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG		(1U << 0)
-#define BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL		(1U << 1)
-#define BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP		(1U << 2)
-
-struct bpf_flow_keys {
-	__u16	nhoff;
-	__u16	thoff;
-	__u16	addr_proto;			/* ETH_P_* of valid addrs */
-	__u8	is_frag;
-	__u8	is_first_frag;
-	__u8	is_encap;
-	__u8	ip_proto;
-	__be16	n_proto;
-	__be16	sport;
-	__be16	dport;
-	union {
-		struct {
-			__be32	ipv4_src;
-			__be32	ipv4_dst;
-		};
-		struct {
-			__u32	ipv6_src[4];	/* in6_addr; network order */
-			__u32	ipv6_dst[4];	/* in6_addr; network order */
-		};
-	};
-	__u32	flags;
-	__be32	flow_label;
-};
-
-struct bpf_func_info {
-	__u32	insn_off;
-	__u32	type_id;
-};
-
-#define BPF_LINE_INFO_LINE_NUM(line_col)	((line_col) >> 10)
-#define BPF_LINE_INFO_LINE_COL(line_col)	((line_col) & 0x3ff)
-
-struct bpf_line_info {
-	__u32	insn_off;
-	__u32	file_name_off;
-	__u32	line_off;
-	__u32	line_col;
-};
-
-struct bpf_spin_lock {
-	__u32	val;
-};
-
-struct bpf_sysctl {
-	__u32	write;		/* Sysctl is being read (= 0) or written (= 1).
-				 * Allows 1,2,4-byte read, but no write.
-				 */
-	__u32	file_pos;	/* Sysctl file position to read from, write to.
-				 * Allows 1,2,4-byte read an 4-byte write.
-				 */
-};
-
-struct bpf_sockopt {
-	__bpf_md_ptr(struct bpf_sock *, sk);
-	__bpf_md_ptr(void *, optval);
-	__bpf_md_ptr(void *, optval_end);
-
-	__s32	level;
-	__s32	optname;
-	__s32	optlen;
-	__s32	retval;
-};
-
-#endif /* __LINUX_BPF_H__ */
diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
deleted file mode 100644
index d28dd89..0000000
--- a/include/uapi/linux/btf.h
+++ /dev/null
@@ -1,165 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* Copyright (c) 2018 Facebook */
-#ifndef __LINUX_BTF_H__
-#define __LINUX_BTF_H__
-
-#include <linux/types.h>
-
-#define BTF_MAGIC	0xeB9F
-#define BTF_VERSION	1
-
-struct btf_header {
-	__u16	magic;
-	__u8	version;
-	__u8	flags;
-	__u32	hdr_len;
-
-	/* All offsets are in bytes relative to the end of this header */
-	__u32	type_off;	/* offset of type section	*/
-	__u32	type_len;	/* length of type section	*/
-	__u32	str_off;	/* offset of string section	*/
-	__u32	str_len;	/* length of string section	*/
-};
-
-/* Max # of type identifier */
-#define BTF_MAX_TYPE	0x000fffff
-/* Max offset into the string section */
-#define BTF_MAX_NAME_OFFSET	0x00ffffff
-/* Max # of struct/union/enum members or func args */
-#define BTF_MAX_VLEN	0xffff
-
-struct btf_type {
-	__u32 name_off;
-	/* "info" bits arrangement
-	 * bits  0-15: vlen (e.g. # of struct's members)
-	 * bits 16-23: unused
-	 * bits 24-27: kind (e.g. int, ptr, array...etc)
-	 * bits 28-30: unused
-	 * bit     31: kind_flag, currently used by
-	 *             struct, union and fwd
-	 */
-	__u32 info;
-	/* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC.
-	 * "size" tells the size of the type it is describing.
-	 *
-	 * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
-	 * FUNC, FUNC_PROTO and VAR.
-	 * "type" is a type_id referring to another type.
-	 */
-	union {
-		__u32 size;
-		__u32 type;
-	};
-};
-
-#define BTF_INFO_KIND(info)	(((info) >> 24) & 0x0f)
-#define BTF_INFO_VLEN(info)	((info) & 0xffff)
-#define BTF_INFO_KFLAG(info)	((info) >> 31)
-
-#define BTF_KIND_UNKN		0	/* Unknown	*/
-#define BTF_KIND_INT		1	/* Integer	*/
-#define BTF_KIND_PTR		2	/* Pointer	*/
-#define BTF_KIND_ARRAY		3	/* Array	*/
-#define BTF_KIND_STRUCT		4	/* Struct	*/
-#define BTF_KIND_UNION		5	/* Union	*/
-#define BTF_KIND_ENUM		6	/* Enumeration	*/
-#define BTF_KIND_FWD		7	/* Forward	*/
-#define BTF_KIND_TYPEDEF	8	/* Typedef	*/
-#define BTF_KIND_VOLATILE	9	/* Volatile	*/
-#define BTF_KIND_CONST		10	/* Const	*/
-#define BTF_KIND_RESTRICT	11	/* Restrict	*/
-#define BTF_KIND_FUNC		12	/* Function	*/
-#define BTF_KIND_FUNC_PROTO	13	/* Function Proto	*/
-#define BTF_KIND_VAR		14	/* Variable	*/
-#define BTF_KIND_DATASEC	15	/* Section	*/
-#define BTF_KIND_MAX		BTF_KIND_DATASEC
-#define NR_BTF_KINDS		(BTF_KIND_MAX + 1)
-
-/* For some specific BTF_KIND, "struct btf_type" is immediately
- * followed by extra data.
- */
-
-/* BTF_KIND_INT is followed by a u32 and the following
- * is the 32 bits arrangement:
- */
-#define BTF_INT_ENCODING(VAL)	(((VAL) & 0x0f000000) >> 24)
-#define BTF_INT_OFFSET(VAL)	(((VAL) & 0x00ff0000) >> 16)
-#define BTF_INT_BITS(VAL)	((VAL)  & 0x000000ff)
-
-/* Attributes stored in the BTF_INT_ENCODING */
-#define BTF_INT_SIGNED	(1 << 0)
-#define BTF_INT_CHAR	(1 << 1)
-#define BTF_INT_BOOL	(1 << 2)
-
-/* BTF_KIND_ENUM is followed by multiple "struct btf_enum".
- * The exact number of btf_enum is stored in the vlen (of the
- * info in "struct btf_type").
- */
-struct btf_enum {
-	__u32	name_off;
-	__s32	val;
-};
-
-/* BTF_KIND_ARRAY is followed by one "struct btf_array" */
-struct btf_array {
-	__u32	type;
-	__u32	index_type;
-	__u32	nelems;
-};
-
-/* BTF_KIND_STRUCT and BTF_KIND_UNION are followed
- * by multiple "struct btf_member".  The exact number
- * of btf_member is stored in the vlen (of the info in
- * "struct btf_type").
- */
-struct btf_member {
-	__u32	name_off;
-	__u32	type;
-	/* If the type info kind_flag is set, the btf_member offset
-	 * contains both member bitfield size and bit offset. The
-	 * bitfield size is set for bitfield members. If the type
-	 * info kind_flag is not set, the offset contains only bit
-	 * offset.
-	 */
-	__u32	offset;
-};
-
-/* If the struct/union type info kind_flag is set, the
- * following two macros are used to access bitfield_size
- * and bit_offset from btf_member.offset.
- */
-#define BTF_MEMBER_BITFIELD_SIZE(val)	((val) >> 24)
-#define BTF_MEMBER_BIT_OFFSET(val)	((val) & 0xffffff)
-
-/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
- * The exact number of btf_param is stored in the vlen (of the
- * info in "struct btf_type").
- */
-struct btf_param {
-	__u32	name_off;
-	__u32	type;
-};
-
-enum {
-	BTF_VAR_STATIC = 0,
-	BTF_VAR_GLOBAL_ALLOCATED,
-};
-
-/* BTF_KIND_VAR is followed by a single "struct btf_var" to describe
- * additional information related to the variable such as its linkage.
- */
-struct btf_var {
-	__u32	linkage;
-};
-
-/* BTF_KIND_DATASEC is followed by multiple "struct btf_var_secinfo"
- * to describe all BTF_KIND_VAR types it contains along with it's
- * in-section offset as well as size.
- */
-struct btf_var_secinfo {
-	__u32	type;
-	__u32	offset;
-	__u32	size;
-};
-
-#endif /* __LINUX_BTF_H__ */
diff --git a/include/uapi/linux/can/vxcan.h b/include/uapi/linux/can/vxcan.h
deleted file mode 100644
index 3e3d2eb..0000000
--- a/include/uapi/linux/can/vxcan.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
-#ifndef _CAN_VXCAN_H
-#define _CAN_VXCAN_H
-
-enum {
-	VXCAN_INFO_UNSPEC,
-	VXCAN_INFO_PEER,
-
-	__VXCAN_INFO_MAX
-#define VXCAN_INFO_MAX	(__VXCAN_INFO_MAX - 1)
-};
-
-#endif
diff --git a/include/uapi/linux/const.h b/include/uapi/linux/const.h
deleted file mode 100644
index fd885c7..0000000
--- a/include/uapi/linux/const.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* const.h: Macros for dealing with constants.  */
-
-#ifndef _LINUX_CONST_H
-#define _LINUX_CONST_H
-
-/* Some constant macros are used in both assembler and
- * C code.  Therefore we cannot annotate them always with
- * 'UL' and other type specifiers unilaterally.  We
- * use the following macros to deal with this.
- *
- * Similarly, _AT() will cast an expression with a type in C, but
- * leave it unchanged in asm.
- */
-
-#ifdef __ASSEMBLY__
-#define _AC(X,Y)	X
-#define _AT(T,X)	X
-#else
-#define __AC(X,Y)	(X##Y)
-#define _AC(X,Y)	__AC(X,Y)
-#define _AT(T,X)	((T)(X))
-#endif
-
-#define _UL(x)		(_AC(x, UL))
-#define _ULL(x)		(_AC(x, ULL))
-
-#define _BITUL(x)	(_UL(1) << (x))
-#define _BITULL(x)	(_ULL(1) << (x))
-
-#endif /* _LINUX_CONST_H */
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
deleted file mode 100644
index 84c0caf..0000000
--- a/include/uapi/linux/devlink.h
+++ /dev/null
@@ -1,471 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
-/*
- * include/uapi/linux/devlink.h - Network physical device Netlink interface
- * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
- * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef _LINUX_DEVLINK_H_
-#define _LINUX_DEVLINK_H_
-
-#define DEVLINK_GENL_NAME "devlink"
-#define DEVLINK_GENL_VERSION 0x1
-#define DEVLINK_GENL_MCGRP_CONFIG_NAME "config"
-
-enum devlink_command {
-	/* don't change the order or add anything between, this is ABI! */
-	DEVLINK_CMD_UNSPEC,
-
-	DEVLINK_CMD_GET,		/* can dump */
-	DEVLINK_CMD_SET,
-	DEVLINK_CMD_NEW,
-	DEVLINK_CMD_DEL,
-
-	DEVLINK_CMD_PORT_GET,		/* can dump */
-	DEVLINK_CMD_PORT_SET,
-	DEVLINK_CMD_PORT_NEW,
-	DEVLINK_CMD_PORT_DEL,
-
-	DEVLINK_CMD_PORT_SPLIT,
-	DEVLINK_CMD_PORT_UNSPLIT,
-
-	DEVLINK_CMD_SB_GET,		/* can dump */
-	DEVLINK_CMD_SB_SET,
-	DEVLINK_CMD_SB_NEW,
-	DEVLINK_CMD_SB_DEL,
-
-	DEVLINK_CMD_SB_POOL_GET,	/* can dump */
-	DEVLINK_CMD_SB_POOL_SET,
-	DEVLINK_CMD_SB_POOL_NEW,
-	DEVLINK_CMD_SB_POOL_DEL,
-
-	DEVLINK_CMD_SB_PORT_POOL_GET,	/* can dump */
-	DEVLINK_CMD_SB_PORT_POOL_SET,
-	DEVLINK_CMD_SB_PORT_POOL_NEW,
-	DEVLINK_CMD_SB_PORT_POOL_DEL,
-
-	DEVLINK_CMD_SB_TC_POOL_BIND_GET,	/* can dump */
-	DEVLINK_CMD_SB_TC_POOL_BIND_SET,
-	DEVLINK_CMD_SB_TC_POOL_BIND_NEW,
-	DEVLINK_CMD_SB_TC_POOL_BIND_DEL,
-
-	/* Shared buffer occupancy monitoring commands */
-	DEVLINK_CMD_SB_OCC_SNAPSHOT,
-	DEVLINK_CMD_SB_OCC_MAX_CLEAR,
-
-	DEVLINK_CMD_ESWITCH_GET,
-#define DEVLINK_CMD_ESWITCH_MODE_GET /* obsolete, never use this! */ \
-	DEVLINK_CMD_ESWITCH_GET
-
-	DEVLINK_CMD_ESWITCH_SET,
-#define DEVLINK_CMD_ESWITCH_MODE_SET /* obsolete, never use this! */ \
-	DEVLINK_CMD_ESWITCH_SET
-
-	DEVLINK_CMD_DPIPE_TABLE_GET,
-	DEVLINK_CMD_DPIPE_ENTRIES_GET,
-	DEVLINK_CMD_DPIPE_HEADERS_GET,
-	DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET,
-	DEVLINK_CMD_RESOURCE_SET,
-	DEVLINK_CMD_RESOURCE_DUMP,
-
-	/* Hot driver reload, makes configuration changes take place. The
-	 * devlink instance is not released during the process.
-	 */
-	DEVLINK_CMD_RELOAD,
-
-	DEVLINK_CMD_PARAM_GET,		/* can dump */
-	DEVLINK_CMD_PARAM_SET,
-	DEVLINK_CMD_PARAM_NEW,
-	DEVLINK_CMD_PARAM_DEL,
-
-	DEVLINK_CMD_REGION_GET,
-	DEVLINK_CMD_REGION_SET,
-	DEVLINK_CMD_REGION_NEW,
-	DEVLINK_CMD_REGION_DEL,
-	DEVLINK_CMD_REGION_READ,
-
-	DEVLINK_CMD_PORT_PARAM_GET,	/* can dump */
-	DEVLINK_CMD_PORT_PARAM_SET,
-	DEVLINK_CMD_PORT_PARAM_NEW,
-	DEVLINK_CMD_PORT_PARAM_DEL,
-
-	DEVLINK_CMD_INFO_GET,		/* can dump */
-
-	DEVLINK_CMD_HEALTH_REPORTER_GET,
-	DEVLINK_CMD_HEALTH_REPORTER_SET,
-	DEVLINK_CMD_HEALTH_REPORTER_RECOVER,
-	DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE,
-	DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET,
-	DEVLINK_CMD_HEALTH_REPORTER_DUMP_CLEAR,
-
-	DEVLINK_CMD_FLASH_UPDATE,
-	DEVLINK_CMD_FLASH_UPDATE_END,		/* notification only */
-	DEVLINK_CMD_FLASH_UPDATE_STATUS,	/* notification only */
-
-	DEVLINK_CMD_TRAP_GET,		/* can dump */
-	DEVLINK_CMD_TRAP_SET,
-	DEVLINK_CMD_TRAP_NEW,
-	DEVLINK_CMD_TRAP_DEL,
-
-	DEVLINK_CMD_TRAP_GROUP_GET,	/* can dump */
-	DEVLINK_CMD_TRAP_GROUP_SET,
-	DEVLINK_CMD_TRAP_GROUP_NEW,
-	DEVLINK_CMD_TRAP_GROUP_DEL,
-
-	/* add new commands above here */
-	__DEVLINK_CMD_MAX,
-	DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
-};
-
-enum devlink_port_type {
-	DEVLINK_PORT_TYPE_NOTSET,
-	DEVLINK_PORT_TYPE_AUTO,
-	DEVLINK_PORT_TYPE_ETH,
-	DEVLINK_PORT_TYPE_IB,
-};
-
-enum devlink_sb_pool_type {
-	DEVLINK_SB_POOL_TYPE_INGRESS,
-	DEVLINK_SB_POOL_TYPE_EGRESS,
-};
-
-/* static threshold - limiting the maximum number of bytes.
- * dynamic threshold - limiting the maximum number of bytes
- *   based on the currently available free space in the shared buffer pool.
- *   In this mode, the maximum quota is calculated based
- *   on the following formula:
- *     max_quota = alpha / (1 + alpha) * Free_Buffer
- *   While Free_Buffer is the amount of none-occupied buffer associated to
- *   the relevant pool.
- *   The value range which can be passed is 0-20 and serves
- *   for computation of alpha by following formula:
- *     alpha = 2 ^ (passed_value - 10)
- */
-
-enum devlink_sb_threshold_type {
-	DEVLINK_SB_THRESHOLD_TYPE_STATIC,
-	DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC,
-};
-
-#define DEVLINK_SB_THRESHOLD_TO_ALPHA_MAX 20
-
-enum devlink_eswitch_mode {
-	DEVLINK_ESWITCH_MODE_LEGACY,
-	DEVLINK_ESWITCH_MODE_SWITCHDEV,
-};
-
-enum devlink_eswitch_inline_mode {
-	DEVLINK_ESWITCH_INLINE_MODE_NONE,
-	DEVLINK_ESWITCH_INLINE_MODE_LINK,
-	DEVLINK_ESWITCH_INLINE_MODE_NETWORK,
-	DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT,
-};
-
-enum devlink_eswitch_encap_mode {
-	DEVLINK_ESWITCH_ENCAP_MODE_NONE,
-	DEVLINK_ESWITCH_ENCAP_MODE_BASIC,
-};
-
-enum devlink_port_flavour {
-	DEVLINK_PORT_FLAVOUR_PHYSICAL, /* Any kind of a port physically
-					* facing the user.
-					*/
-	DEVLINK_PORT_FLAVOUR_CPU, /* CPU port */
-	DEVLINK_PORT_FLAVOUR_DSA, /* Distributed switch architecture
-				   * interconnect port.
-				   */
-	DEVLINK_PORT_FLAVOUR_PCI_PF, /* Represents eswitch port for
-				      * the PCI PF. It is an internal
-				      * port that faces the PCI PF.
-				      */
-	DEVLINK_PORT_FLAVOUR_PCI_VF, /* Represents eswitch port
-				      * for the PCI VF. It is an internal
-				      * port that faces the PCI VF.
-				      */
-};
-
-enum devlink_param_cmode {
-	DEVLINK_PARAM_CMODE_RUNTIME,
-	DEVLINK_PARAM_CMODE_DRIVERINIT,
-	DEVLINK_PARAM_CMODE_PERMANENT,
-
-	/* Add new configuration modes above */
-	__DEVLINK_PARAM_CMODE_MAX,
-	DEVLINK_PARAM_CMODE_MAX = __DEVLINK_PARAM_CMODE_MAX - 1
-};
-
-enum devlink_param_fw_load_policy_value {
-	DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER,
-	DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH,
-	DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DISK,
-	DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_UNKNOWN,
-};
-
-enum devlink_param_reset_dev_on_drv_probe_value {
-	DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_UNKNOWN,
-	DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_ALWAYS,
-	DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_NEVER,
-	DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_DISK,
-};
-
-enum {
-	DEVLINK_ATTR_STATS_RX_PACKETS,		/* u64 */
-	DEVLINK_ATTR_STATS_RX_BYTES,		/* u64 */
-
-	__DEVLINK_ATTR_STATS_MAX,
-	DEVLINK_ATTR_STATS_MAX = __DEVLINK_ATTR_STATS_MAX - 1
-};
-
-/**
- * enum devlink_trap_action - Packet trap action.
- * @DEVLINK_TRAP_ACTION_DROP: Packet is dropped by the device and a copy is not
- *                            sent to the CPU.
- * @DEVLINK_TRAP_ACTION_TRAP: The sole copy of the packet is sent to the CPU.
- */
-enum devlink_trap_action {
-	DEVLINK_TRAP_ACTION_DROP,
-	DEVLINK_TRAP_ACTION_TRAP,
-};
-
-/**
- * enum devlink_trap_type - Packet trap type.
- * @DEVLINK_TRAP_TYPE_DROP: Trap reason is a drop. Trapped packets are only
- *                          processed by devlink and not injected to the
- *                          kernel's Rx path.
- * @DEVLINK_TRAP_TYPE_EXCEPTION: Trap reason is an exception. Packet was not
- *                               forwarded as intended due to an exception
- *                               (e.g., missing neighbour entry) and trapped to
- *                               control plane for resolution. Trapped packets
- *                               are processed by devlink and injected to
- *                               the kernel's Rx path.
- */
-enum devlink_trap_type {
-	DEVLINK_TRAP_TYPE_DROP,
-	DEVLINK_TRAP_TYPE_EXCEPTION,
-};
-
-enum {
-	/* Trap can report input port as metadata */
-	DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT,
-};
-
-enum devlink_attr {
-	/* don't change the order or add anything between, this is ABI! */
-	DEVLINK_ATTR_UNSPEC,
-
-	/* bus name + dev name together are a handle for devlink entity */
-	DEVLINK_ATTR_BUS_NAME,			/* string */
-	DEVLINK_ATTR_DEV_NAME,			/* string */
-
-	DEVLINK_ATTR_PORT_INDEX,		/* u32 */
-	DEVLINK_ATTR_PORT_TYPE,			/* u16 */
-	DEVLINK_ATTR_PORT_DESIRED_TYPE,		/* u16 */
-	DEVLINK_ATTR_PORT_NETDEV_IFINDEX,	/* u32 */
-	DEVLINK_ATTR_PORT_NETDEV_NAME,		/* string */
-	DEVLINK_ATTR_PORT_IBDEV_NAME,		/* string */
-	DEVLINK_ATTR_PORT_SPLIT_COUNT,		/* u32 */
-	DEVLINK_ATTR_PORT_SPLIT_GROUP,		/* u32 */
-	DEVLINK_ATTR_SB_INDEX,			/* u32 */
-	DEVLINK_ATTR_SB_SIZE,			/* u32 */
-	DEVLINK_ATTR_SB_INGRESS_POOL_COUNT,	/* u16 */
-	DEVLINK_ATTR_SB_EGRESS_POOL_COUNT,	/* u16 */
-	DEVLINK_ATTR_SB_INGRESS_TC_COUNT,	/* u16 */
-	DEVLINK_ATTR_SB_EGRESS_TC_COUNT,	/* u16 */
-	DEVLINK_ATTR_SB_POOL_INDEX,		/* u16 */
-	DEVLINK_ATTR_SB_POOL_TYPE,		/* u8 */
-	DEVLINK_ATTR_SB_POOL_SIZE,		/* u32 */
-	DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE,	/* u8 */
-	DEVLINK_ATTR_SB_THRESHOLD,		/* u32 */
-	DEVLINK_ATTR_SB_TC_INDEX,		/* u16 */
-	DEVLINK_ATTR_SB_OCC_CUR,		/* u32 */
-	DEVLINK_ATTR_SB_OCC_MAX,		/* u32 */
-	DEVLINK_ATTR_ESWITCH_MODE,		/* u16 */
-	DEVLINK_ATTR_ESWITCH_INLINE_MODE,	/* u8 */
-
-	DEVLINK_ATTR_DPIPE_TABLES,		/* nested */
-	DEVLINK_ATTR_DPIPE_TABLE,		/* nested */
-	DEVLINK_ATTR_DPIPE_TABLE_NAME,		/* string */
-	DEVLINK_ATTR_DPIPE_TABLE_SIZE,		/* u64 */
-	DEVLINK_ATTR_DPIPE_TABLE_MATCHES,	/* nested */
-	DEVLINK_ATTR_DPIPE_TABLE_ACTIONS,	/* nested */
-	DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED,	/* u8 */
-
-	DEVLINK_ATTR_DPIPE_ENTRIES,		/* nested */
-	DEVLINK_ATTR_DPIPE_ENTRY,		/* nested */
-	DEVLINK_ATTR_DPIPE_ENTRY_INDEX,		/* u64 */
-	DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES,	/* nested */
-	DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES,	/* nested */
-	DEVLINK_ATTR_DPIPE_ENTRY_COUNTER,	/* u64 */
-
-	DEVLINK_ATTR_DPIPE_MATCH,		/* nested */
-	DEVLINK_ATTR_DPIPE_MATCH_VALUE,		/* nested */
-	DEVLINK_ATTR_DPIPE_MATCH_TYPE,		/* u32 */
-
-	DEVLINK_ATTR_DPIPE_ACTION,		/* nested */
-	DEVLINK_ATTR_DPIPE_ACTION_VALUE,	/* nested */
-	DEVLINK_ATTR_DPIPE_ACTION_TYPE,		/* u32 */
-
-	DEVLINK_ATTR_DPIPE_VALUE,
-	DEVLINK_ATTR_DPIPE_VALUE_MASK,
-	DEVLINK_ATTR_DPIPE_VALUE_MAPPING,	/* u32 */
-
-	DEVLINK_ATTR_DPIPE_HEADERS,		/* nested */
-	DEVLINK_ATTR_DPIPE_HEADER,		/* nested */
-	DEVLINK_ATTR_DPIPE_HEADER_NAME,		/* string */
-	DEVLINK_ATTR_DPIPE_HEADER_ID,		/* u32 */
-	DEVLINK_ATTR_DPIPE_HEADER_FIELDS,	/* nested */
-	DEVLINK_ATTR_DPIPE_HEADER_GLOBAL,	/* u8 */
-	DEVLINK_ATTR_DPIPE_HEADER_INDEX,	/* u32 */
-
-	DEVLINK_ATTR_DPIPE_FIELD,		/* nested */
-	DEVLINK_ATTR_DPIPE_FIELD_NAME,		/* string */
-	DEVLINK_ATTR_DPIPE_FIELD_ID,		/* u32 */
-	DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH,	/* u32 */
-	DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE,	/* u32 */
-
-	DEVLINK_ATTR_PAD,
-
-	DEVLINK_ATTR_ESWITCH_ENCAP_MODE,	/* u8 */
-	DEVLINK_ATTR_RESOURCE_LIST,		/* nested */
-	DEVLINK_ATTR_RESOURCE,			/* nested */
-	DEVLINK_ATTR_RESOURCE_NAME,		/* string */
-	DEVLINK_ATTR_RESOURCE_ID,		/* u64 */
-	DEVLINK_ATTR_RESOURCE_SIZE,		/* u64 */
-	DEVLINK_ATTR_RESOURCE_SIZE_NEW,		/* u64 */
-	DEVLINK_ATTR_RESOURCE_SIZE_VALID,	/* u8 */
-	DEVLINK_ATTR_RESOURCE_SIZE_MIN,		/* u64 */
-	DEVLINK_ATTR_RESOURCE_SIZE_MAX,		/* u64 */
-	DEVLINK_ATTR_RESOURCE_SIZE_GRAN,        /* u64 */
-	DEVLINK_ATTR_RESOURCE_UNIT,		/* u8 */
-	DEVLINK_ATTR_RESOURCE_OCC,		/* u64 */
-	DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID,	/* u64 */
-	DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS,/* u64 */
-
-	DEVLINK_ATTR_PORT_FLAVOUR,		/* u16 */
-	DEVLINK_ATTR_PORT_NUMBER,		/* u32 */
-	DEVLINK_ATTR_PORT_SPLIT_SUBPORT_NUMBER,	/* u32 */
-
-	DEVLINK_ATTR_PARAM,			/* nested */
-	DEVLINK_ATTR_PARAM_NAME,		/* string */
-	DEVLINK_ATTR_PARAM_GENERIC,		/* flag */
-	DEVLINK_ATTR_PARAM_TYPE,		/* u8 */
-	DEVLINK_ATTR_PARAM_VALUES_LIST,		/* nested */
-	DEVLINK_ATTR_PARAM_VALUE,		/* nested */
-	DEVLINK_ATTR_PARAM_VALUE_DATA,		/* dynamic */
-	DEVLINK_ATTR_PARAM_VALUE_CMODE,		/* u8 */
-
-	DEVLINK_ATTR_REGION_NAME,               /* string */
-	DEVLINK_ATTR_REGION_SIZE,               /* u64 */
-	DEVLINK_ATTR_REGION_SNAPSHOTS,          /* nested */
-	DEVLINK_ATTR_REGION_SNAPSHOT,           /* nested */
-	DEVLINK_ATTR_REGION_SNAPSHOT_ID,        /* u32 */
-
-	DEVLINK_ATTR_REGION_CHUNKS,             /* nested */
-	DEVLINK_ATTR_REGION_CHUNK,              /* nested */
-	DEVLINK_ATTR_REGION_CHUNK_DATA,         /* binary */
-	DEVLINK_ATTR_REGION_CHUNK_ADDR,         /* u64 */
-	DEVLINK_ATTR_REGION_CHUNK_LEN,          /* u64 */
-
-	DEVLINK_ATTR_INFO_DRIVER_NAME,		/* string */
-	DEVLINK_ATTR_INFO_SERIAL_NUMBER,	/* string */
-	DEVLINK_ATTR_INFO_VERSION_FIXED,	/* nested */
-	DEVLINK_ATTR_INFO_VERSION_RUNNING,	/* nested */
-	DEVLINK_ATTR_INFO_VERSION_STORED,	/* nested */
-	DEVLINK_ATTR_INFO_VERSION_NAME,		/* string */
-	DEVLINK_ATTR_INFO_VERSION_VALUE,	/* string */
-
-	DEVLINK_ATTR_SB_POOL_CELL_SIZE,		/* u32 */
-
-	DEVLINK_ATTR_FMSG,			/* nested */
-	DEVLINK_ATTR_FMSG_OBJ_NEST_START,	/* flag */
-	DEVLINK_ATTR_FMSG_PAIR_NEST_START,	/* flag */
-	DEVLINK_ATTR_FMSG_ARR_NEST_START,	/* flag */
-	DEVLINK_ATTR_FMSG_NEST_END,		/* flag */
-	DEVLINK_ATTR_FMSG_OBJ_NAME,		/* string */
-	DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE,	/* u8 */
-	DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA,	/* dynamic */
-
-	DEVLINK_ATTR_HEALTH_REPORTER,			/* nested */
-	DEVLINK_ATTR_HEALTH_REPORTER_NAME,		/* string */
-	DEVLINK_ATTR_HEALTH_REPORTER_STATE,		/* u8 */
-	DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT,		/* u64 */
-	DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT,	/* u64 */
-	DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS,		/* u64 */
-	DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,	/* u64 */
-	DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,	/* u8 */
-
-	DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME,	/* string */
-	DEVLINK_ATTR_FLASH_UPDATE_COMPONENT,	/* string */
-	DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG,	/* string */
-	DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE,	/* u64 */
-	DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL,	/* u64 */
-
-	DEVLINK_ATTR_PORT_PCI_PF_NUMBER,	/* u16 */
-	DEVLINK_ATTR_PORT_PCI_VF_NUMBER,	/* u16 */
-
-	DEVLINK_ATTR_STATS,				/* nested */
-
-	DEVLINK_ATTR_TRAP_NAME,				/* string */
-	/* enum devlink_trap_action */
-	DEVLINK_ATTR_TRAP_ACTION,			/* u8 */
-	/* enum devlink_trap_type */
-	DEVLINK_ATTR_TRAP_TYPE,				/* u8 */
-	DEVLINK_ATTR_TRAP_GENERIC,			/* flag */
-	DEVLINK_ATTR_TRAP_METADATA,			/* nested */
-	DEVLINK_ATTR_TRAP_GROUP_NAME,			/* string */
-
-	DEVLINK_ATTR_RELOAD_FAILED,			/* u8 0 or 1 */
-
-	DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS,	/* u64 */
-	/* add new attributes above here, update the policy in devlink.c */
-
-	__DEVLINK_ATTR_MAX,
-	DEVLINK_ATTR_MAX = __DEVLINK_ATTR_MAX - 1
-};
-
-/* Mapping between internal resource described by the field and system
- * structure
- */
-enum devlink_dpipe_field_mapping_type {
-	DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE,
-	DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX,
-};
-
-/* Match type - specify the type of the match */
-enum devlink_dpipe_match_type {
-	DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT,
-};
-
-/* Action type - specify the action type */
-enum devlink_dpipe_action_type {
-	DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY,
-};
-
-enum devlink_dpipe_field_ethernet_id {
-	DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC,
-};
-
-enum devlink_dpipe_field_ipv4_id {
-	DEVLINK_DPIPE_FIELD_IPV4_DST_IP,
-};
-
-enum devlink_dpipe_field_ipv6_id {
-	DEVLINK_DPIPE_FIELD_IPV6_DST_IP,
-};
-
-enum devlink_dpipe_header_id {
-	DEVLINK_DPIPE_HEADER_ETHERNET,
-	DEVLINK_DPIPE_HEADER_IPV4,
-	DEVLINK_DPIPE_HEADER_IPV6,
-};
-
-enum devlink_resource_unit {
-	DEVLINK_RESOURCE_UNIT_ENTRY,
-};
-
-#endif /* _LINUX_DEVLINK_H_ */
diff --git a/include/uapi/linux/elf-em.h b/include/uapi/linux/elf-em.h
deleted file mode 100644
index f47e853..0000000
--- a/include/uapi/linux/elf-em.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_ELF_EM_H
-#define _LINUX_ELF_EM_H
-
-/* These constants define the various ELF target machines */
-#define EM_NONE		0
-#define EM_M32		1
-#define EM_SPARC	2
-#define EM_386		3
-#define EM_68K		4
-#define EM_88K		5
-#define EM_486		6	/* Perhaps disused */
-#define EM_860		7
-#define EM_MIPS		8	/* MIPS R3000 (officially, big-endian only) */
-				/* Next two are historical and binaries and
-				   modules of these types will be rejected by
-				   Linux.  */
-#define EM_MIPS_RS3_LE	10	/* MIPS R3000 little-endian */
-#define EM_MIPS_RS4_BE	10	/* MIPS R4000 big-endian */
-
-#define EM_PARISC	15	/* HPPA */
-#define EM_SPARC32PLUS	18	/* Sun's "v8plus" */
-#define EM_PPC		20	/* PowerPC */
-#define EM_PPC64	21	 /* PowerPC64 */
-#define EM_SPU		23	/* Cell BE SPU */
-#define EM_ARM		40	/* ARM 32 bit */
-#define EM_SH		42	/* SuperH */
-#define EM_SPARCV9	43	/* SPARC v9 64-bit */
-#define EM_H8_300	46	/* Renesas H8/300 */
-#define EM_IA_64	50	/* HP/Intel IA-64 */
-#define EM_X86_64	62	/* AMD x86-64 */
-#define EM_S390		22	/* IBM S/390 */
-#define EM_CRIS		76	/* Axis Communications 32-bit embedded processor */
-#define EM_M32R		88	/* Renesas M32R */
-#define EM_MN10300	89	/* Panasonic/MEI MN10300, AM33 */
-#define EM_OPENRISC     92     /* OpenRISC 32-bit embedded processor */
-#define EM_ARCOMPACT	93	/* ARCompact processor */
-#define EM_XTENSA	94	/* Tensilica Xtensa Architecture */
-#define EM_BLACKFIN     106     /* ADI Blackfin Processor */
-#define EM_UNICORE	110	/* UniCore-32 */
-#define EM_ALTERA_NIOS2	113	/* Altera Nios II soft-core processor */
-#define EM_TI_C6000	140	/* TI C6X DSPs */
-#define EM_HEXAGON	164	/* QUALCOMM Hexagon */
-#define EM_NDS32	167	/* Andes Technology compact code size
-				   embedded RISC processor family */
-#define EM_AARCH64	183	/* ARM 64 bit */
-#define EM_TILEPRO	188	/* Tilera TILEPro */
-#define EM_MICROBLAZE	189	/* Xilinx MicroBlaze */
-#define EM_TILEGX	191	/* Tilera TILE-Gx */
-#define EM_ARCV2	195	/* ARCv2 Cores */
-#define EM_RISCV	243	/* RISC-V */
-#define EM_BPF		247	/* Linux BPF - in-kernel virtual machine */
-#define EM_CSKY		252	/* C-SKY */
-#define EM_FRV		0x5441	/* Fujitsu FR-V */
-
-/*
- * This is an interim value that we will use until the committee comes
- * up with a final number.
- */
-#define EM_ALPHA	0x9026
-
-/* Bogus old m32r magic number, used by old tools. */
-#define EM_CYGNUS_M32R	0x9041
-/* This is the old interim value for S/390 architecture */
-#define EM_S390_OLD	0xA390
-/* Also Panasonic/MEI MN10300, AM33 */
-#define EM_CYGNUS_MN10300 0xbeef
-
-
-#endif /* _LINUX_ELF_EM_H */
diff --git a/include/uapi/linux/icmpv6.h b/include/uapi/linux/icmpv6.h
deleted file mode 100644
index 1dc7cc6..0000000
--- a/include/uapi/linux/icmpv6.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_ICMPV6_H
-#define _LINUX_ICMPV6_H
-
-#include <linux/types.h>
-#include <asm/byteorder.h>
-
-struct icmp6hdr {
-
-	__u8		icmp6_type;
-	__u8		icmp6_code;
-	__sum16		icmp6_cksum;
-
-
-	union {
-		__be32			un_data32[1];
-		__be16			un_data16[2];
-		__u8			un_data8[4];
-
-		struct icmpv6_echo {
-			__be16		identifier;
-			__be16		sequence;
-		} u_echo;
-
-                struct icmpv6_nd_advt {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-                        __u32		reserved:5,
-                        		override:1,
-                        		solicited:1,
-                        		router:1,
-					reserved2:24;
-#elif defined(__BIG_ENDIAN_BITFIELD)
-                        __u32		router:1,
-					solicited:1,
-                        		override:1,
-                        		reserved:29;
-#else
-#error	"Please fix <asm/byteorder.h>"
-#endif						
-                } u_nd_advt;
-
-                struct icmpv6_nd_ra {
-			__u8		hop_limit;
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-			__u8		reserved:3,
-					router_pref:2,
-					home_agent:1,
-					other:1,
-					managed:1;
-
-#elif defined(__BIG_ENDIAN_BITFIELD)
-			__u8		managed:1,
-					other:1,
-					home_agent:1,
-					router_pref:2,
-					reserved:3;
-#else
-#error	"Please fix <asm/byteorder.h>"
-#endif
-			__be16		rt_lifetime;
-                } u_nd_ra;
-
-	} icmp6_dataun;
-
-#define icmp6_identifier	icmp6_dataun.u_echo.identifier
-#define icmp6_sequence		icmp6_dataun.u_echo.sequence
-#define icmp6_pointer		icmp6_dataun.un_data32[0]
-#define icmp6_mtu		icmp6_dataun.un_data32[0]
-#define icmp6_unused		icmp6_dataun.un_data32[0]
-#define icmp6_maxdelay		icmp6_dataun.un_data16[0]
-#define icmp6_router		icmp6_dataun.u_nd_advt.router
-#define icmp6_solicited		icmp6_dataun.u_nd_advt.solicited
-#define icmp6_override		icmp6_dataun.u_nd_advt.override
-#define icmp6_ndiscreserved	icmp6_dataun.u_nd_advt.reserved
-#define icmp6_hop_limit		icmp6_dataun.u_nd_ra.hop_limit
-#define icmp6_addrconf_managed	icmp6_dataun.u_nd_ra.managed
-#define icmp6_addrconf_other	icmp6_dataun.u_nd_ra.other
-#define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
-#define icmp6_router_pref	icmp6_dataun.u_nd_ra.router_pref
-};
-
-
-#define ICMPV6_ROUTER_PREF_LOW		0x3
-#define ICMPV6_ROUTER_PREF_MEDIUM	0x0
-#define ICMPV6_ROUTER_PREF_HIGH		0x1
-#define ICMPV6_ROUTER_PREF_INVALID	0x2
-
-#define ICMPV6_DEST_UNREACH		1
-#define ICMPV6_PKT_TOOBIG		2
-#define ICMPV6_TIME_EXCEED		3
-#define ICMPV6_PARAMPROB		4
-
-#define ICMPV6_ERRMSG_MAX       127
-
-#define ICMPV6_INFOMSG_MASK		0x80
-
-#define ICMPV6_ECHO_REQUEST		128
-#define ICMPV6_ECHO_REPLY		129
-#define ICMPV6_MGM_QUERY		130
-#define ICMPV6_MGM_REPORT       	131
-#define ICMPV6_MGM_REDUCTION    	132
-
-#define ICMPV6_NI_QUERY			139
-#define ICMPV6_NI_REPLY			140
-
-#define ICMPV6_MLD2_REPORT		143
-
-#define ICMPV6_DHAAD_REQUEST		144
-#define ICMPV6_DHAAD_REPLY		145
-#define ICMPV6_MOBILE_PREFIX_SOL	146
-#define ICMPV6_MOBILE_PREFIX_ADV	147
-
-#define ICMPV6_MRDISC_ADV		151
-
-#define ICMPV6_MSG_MAX          255
-
-/*
- *	Codes for Destination Unreachable
- */
-#define ICMPV6_NOROUTE			0
-#define ICMPV6_ADM_PROHIBITED		1
-#define ICMPV6_NOT_NEIGHBOUR		2
-#define ICMPV6_ADDR_UNREACH		3
-#define ICMPV6_PORT_UNREACH		4
-#define ICMPV6_POLICY_FAIL		5
-#define ICMPV6_REJECT_ROUTE		6
-
-/*
- *	Codes for Time Exceeded
- */
-#define ICMPV6_EXC_HOPLIMIT		0
-#define ICMPV6_EXC_FRAGTIME		1
-
-/*
- *	Codes for Parameter Problem
- */
-#define ICMPV6_HDR_FIELD		0
-#define ICMPV6_UNK_NEXTHDR		1
-#define ICMPV6_UNK_OPTION		2
-
-/*
- *	constants for (set|get)sockopt
- */
-
-#define ICMPV6_FILTER			1
-
-/*
- *	ICMPV6 filter
- */
-
-#define ICMPV6_FILTER_BLOCK		1
-#define ICMPV6_FILTER_PASS		2
-#define ICMPV6_FILTER_BLOCKOTHERS	3
-#define ICMPV6_FILTER_PASSONLY		4
-
-struct icmp6_filter {
-	__u32		data[8];
-};
-
-/*
- *	Definitions for MLDv2
- */
-#define MLD2_MODE_IS_INCLUDE	1
-#define MLD2_MODE_IS_EXCLUDE	2
-#define MLD2_CHANGE_TO_INCLUDE	3
-#define MLD2_CHANGE_TO_EXCLUDE	4
-#define MLD2_ALLOW_NEW_SOURCES	5
-#define MLD2_BLOCK_OLD_SOURCES	6
-
-#define MLD2_ALL_MCR_INIT { { { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,0x16 } } }
-
-
-#endif /* _LINUX_ICMPV6_H */
diff --git a/include/uapi/linux/if_alg.h b/include/uapi/linux/if_alg.h
deleted file mode 100644
index bc2bcde..0000000
--- a/include/uapi/linux/if_alg.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
-/*
- * if_alg: User-space algorithm interface
- *
- * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- */
-
-#ifndef _LINUX_IF_ALG_H
-#define _LINUX_IF_ALG_H
-
-#include <linux/types.h>
-
-struct sockaddr_alg {
-	__u16	salg_family;
-	__u8	salg_type[14];
-	__u32	salg_feat;
-	__u32	salg_mask;
-	__u8	salg_name[64];
-};
-
-struct af_alg_iv {
-	__u32	ivlen;
-	__u8	iv[0];
-};
-
-/* Socket options */
-#define ALG_SET_KEY			1
-#define ALG_SET_IV			2
-#define ALG_SET_OP			3
-#define ALG_SET_AEAD_ASSOCLEN		4
-#define ALG_SET_AEAD_AUTHSIZE		5
-
-/* Operations */
-#define ALG_OP_DECRYPT			0
-#define ALG_OP_ENCRYPT			1
-
-#endif	/* _LINUX_IF_ALG_H */
diff --git a/include/uapi/linux/if_bonding.h b/include/uapi/linux/if_bonding.h
deleted file mode 100644
index 790585f..0000000
--- a/include/uapi/linux/if_bonding.h
+++ /dev/null
@@ -1,155 +0,0 @@
-/* SPDX-License-Identifier: GPL-1.0+ WITH Linux-syscall-note */
-/*
- * Bond several ethernet interfaces into a Cisco, running 'Etherchannel'.
- *
- *
- * Portions are (c) Copyright 1995 Simon "Guru Aleph-Null" Janes
- * NCM: Network and Communications Management, Inc.
- *
- * BUT, I'm the one who modified it for ethernet, so:
- * (c) Copyright 1999, Thomas Davis, tadavis@lbl.gov
- *
- *	This software may be used and distributed according to the terms
- *	of the GNU Public License, incorporated herein by reference.
- *
- * 2003/03/18 - Amir Noam <amir.noam at intel dot com>
- *	- Added support for getting slave's speed and duplex via ethtool.
- *	  Needed for 802.3ad and other future modes.
- *
- * 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and
- *		Shmulik Hen <shmulik.hen at intel dot com>
- *	- Enable support of modes that need to use the unique mac address of
- *	  each slave.
- *
- * 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and
- *		Amir Noam <amir.noam at intel dot com>
- *	- Moved driver's private data types to bonding.h
- *
- * 2003/03/18 - Amir Noam <amir.noam at intel dot com>,
- *		Tsippy Mendelson <tsippy.mendelson at intel dot com> and
- *		Shmulik Hen <shmulik.hen at intel dot com>
- *	- Added support for IEEE 802.3ad Dynamic link aggregation mode.
- *
- * 2003/05/01 - Amir Noam <amir.noam at intel dot com>
- *	- Added ABI version control to restore compatibility between
- *	  new/old ifenslave and new/old bonding.
- *
- * 2003/12/01 - Shmulik Hen <shmulik.hen at intel dot com>
- *	- Code cleanup and style changes
- *
- * 2005/05/05 - Jason Gabler <jygabler at lbl dot gov>
- *      - added definitions for various XOR hashing policies
- */
-
-#ifndef _LINUX_IF_BONDING_H
-#define _LINUX_IF_BONDING_H
-
-#include <linux/if.h>
-#include <linux/types.h>
-#include <linux/if_ether.h>
-
-/* userland - kernel ABI version (2003/05/08) */
-#define BOND_ABI_VERSION 2
-
-/*
- * We can remove these ioctl definitions in 2.5.  People should use the
- * SIOC*** versions of them instead
- */
-#define BOND_ENSLAVE_OLD		(SIOCDEVPRIVATE)
-#define BOND_RELEASE_OLD		(SIOCDEVPRIVATE + 1)
-#define BOND_SETHWADDR_OLD		(SIOCDEVPRIVATE + 2)
-#define BOND_SLAVE_INFO_QUERY_OLD	(SIOCDEVPRIVATE + 11)
-#define BOND_INFO_QUERY_OLD		(SIOCDEVPRIVATE + 12)
-#define BOND_CHANGE_ACTIVE_OLD		(SIOCDEVPRIVATE + 13)
-
-#define BOND_CHECK_MII_STATUS	(SIOCGMIIPHY)
-
-#define BOND_MODE_ROUNDROBIN	0
-#define BOND_MODE_ACTIVEBACKUP	1
-#define BOND_MODE_XOR		2
-#define BOND_MODE_BROADCAST	3
-#define BOND_MODE_8023AD        4
-#define BOND_MODE_TLB           5
-#define BOND_MODE_ALB		6 /* TLB + RLB (receive load balancing) */
-
-/* each slave's link has 4 states */
-#define BOND_LINK_UP    0           /* link is up and running */
-#define BOND_LINK_FAIL  1           /* link has just gone down */
-#define BOND_LINK_DOWN  2           /* link has been down for too long time */
-#define BOND_LINK_BACK  3           /* link is going back */
-
-/* each slave has several states */
-#define BOND_STATE_ACTIVE       0   /* link is active */
-#define BOND_STATE_BACKUP       1   /* link is backup */
-
-#define BOND_DEFAULT_MAX_BONDS  1   /* Default maximum number of devices to support */
-
-#define BOND_DEFAULT_TX_QUEUES 16   /* Default number of tx queues per device */
-
-#define BOND_DEFAULT_RESEND_IGMP	1 /* Default number of IGMP membership reports */
-
-/* hashing types */
-#define BOND_XMIT_POLICY_LAYER2		0 /* layer 2 (MAC only), default */
-#define BOND_XMIT_POLICY_LAYER34	1 /* layer 3+4 (IP ^ (TCP || UDP)) */
-#define BOND_XMIT_POLICY_LAYER23	2 /* layer 2+3 (IP ^ MAC) */
-#define BOND_XMIT_POLICY_ENCAP23	3 /* encapsulated layer 2+3 */
-#define BOND_XMIT_POLICY_ENCAP34	4 /* encapsulated layer 3+4 */
-
-typedef struct ifbond {
-	__s32 bond_mode;
-	__s32 num_slaves;
-	__s32 miimon;
-} ifbond;
-
-typedef struct ifslave {
-	__s32 slave_id; /* Used as an IN param to the BOND_SLAVE_INFO_QUERY ioctl */
-	char slave_name[IFNAMSIZ];
-	__s8 link;
-	__s8 state;
-	__u32  link_failure_count;
-} ifslave;
-
-struct ad_info {
-	__u16 aggregator_id;
-	__u16 ports;
-	__u16 actor_key;
-	__u16 partner_key;
-	__u8 partner_system[ETH_ALEN];
-};
-
-/* Embedded inside LINK_XSTATS_TYPE_BOND */
-enum {
-	BOND_XSTATS_UNSPEC,
-	BOND_XSTATS_3AD,
-	__BOND_XSTATS_MAX
-};
-#define BOND_XSTATS_MAX (__BOND_XSTATS_MAX - 1)
-
-/* Embedded inside BOND_XSTATS_3AD */
-enum {
-	BOND_3AD_STAT_LACPDU_RX,
-	BOND_3AD_STAT_LACPDU_TX,
-	BOND_3AD_STAT_LACPDU_UNKNOWN_RX,
-	BOND_3AD_STAT_LACPDU_ILLEGAL_RX,
-	BOND_3AD_STAT_MARKER_RX,
-	BOND_3AD_STAT_MARKER_TX,
-	BOND_3AD_STAT_MARKER_RESP_RX,
-	BOND_3AD_STAT_MARKER_RESP_TX,
-	BOND_3AD_STAT_MARKER_UNKNOWN_RX,
-	BOND_3AD_STAT_PAD,
-	__BOND_3AD_STAT_MAX
-};
-#define BOND_3AD_STAT_MAX (__BOND_3AD_STAT_MAX - 1)
-
-#endif /* _LINUX_IF_BONDING_H */
-
-/*
- * Local variables:
- *  version-control: t
- *  kept-new-versions: 5
- *  c-indent-level: 8
- *  c-basic-offset: 8
- *  tab-width: 8
- * End:
- */
-
diff --git a/include/uapi/linux/if_infiniband.h b/include/uapi/linux/if_infiniband.h
deleted file mode 100644
index 0fc33bf..0000000
--- a/include/uapi/linux/if_infiniband.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */
-/*
- * This software is available to you under a choice of one of two
- * licenses.  You may choose to be licensed under the terms of the GNU
- * General Public License (GPL) Version 2, available at
- * <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
- * license, available in the LICENSE.TXT file accompanying this
- * software.  These details are also available at
- * <http://www.openfabrics.org/software_license.htm>.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Copyright (c) 2004 Topspin Communications.  All rights reserved.
- *
- * $Id$
- */
-
-#ifndef _LINUX_IF_INFINIBAND_H
-#define _LINUX_IF_INFINIBAND_H
-
-#define INFINIBAND_ALEN		20	/* Octets in IPoIB HW addr	*/
-
-#endif /* _LINUX_IF_INFINIBAND_H */
diff --git a/include/uapi/linux/if_macsec.h b/include/uapi/linux/if_macsec.h
deleted file mode 100644
index 7743993..0000000
--- a/include/uapi/linux/if_macsec.h
+++ /dev/null
@@ -1,177 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
-/*
- * include/uapi/linux/if_macsec.h - MACsec device
- *
- * Copyright (c) 2015 Sabrina Dubroca <sd@queasysnail.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef _MACSEC_H
-#define _MACSEC_H
-
-#include <linux/types.h>
-
-#define MACSEC_GENL_NAME "macsec"
-#define MACSEC_GENL_VERSION 1
-
-#define MACSEC_MAX_KEY_LEN 128
-
-#define MACSEC_KEYID_LEN 16
-
-/* cipher IDs as per IEEE802.1AEbn-2011 */
-#define MACSEC_CIPHER_ID_GCM_AES_128 0x0080C20001000001ULL
-#define MACSEC_CIPHER_ID_GCM_AES_256 0x0080C20001000002ULL
-
-/* deprecated cipher ID for GCM-AES-128 */
-#define MACSEC_DEFAULT_CIPHER_ID     0x0080020001000001ULL
-#define MACSEC_DEFAULT_CIPHER_ALT    MACSEC_CIPHER_ID_GCM_AES_128
-
-#define MACSEC_MIN_ICV_LEN 8
-#define MACSEC_MAX_ICV_LEN 32
-/* upper limit for ICV length as recommended by IEEE802.1AE-2006 */
-#define MACSEC_STD_ICV_LEN 16
-
-enum macsec_attrs {
-	MACSEC_ATTR_UNSPEC,
-	MACSEC_ATTR_IFINDEX,     /* u32, ifindex of the MACsec netdevice */
-	MACSEC_ATTR_RXSC_CONFIG, /* config, nested macsec_rxsc_attrs */
-	MACSEC_ATTR_SA_CONFIG,   /* config, nested macsec_sa_attrs */
-	MACSEC_ATTR_SECY,        /* dump, nested macsec_secy_attrs */
-	MACSEC_ATTR_TXSA_LIST,   /* dump, nested, macsec_sa_attrs for each TXSA */
-	MACSEC_ATTR_RXSC_LIST,   /* dump, nested, macsec_rxsc_attrs for each RXSC */
-	MACSEC_ATTR_TXSC_STATS,  /* dump, nested, macsec_txsc_stats_attr */
-	MACSEC_ATTR_SECY_STATS,  /* dump, nested, macsec_secy_stats_attr */
-	__MACSEC_ATTR_END,
-	NUM_MACSEC_ATTR = __MACSEC_ATTR_END,
-	MACSEC_ATTR_MAX = __MACSEC_ATTR_END - 1,
-};
-
-enum macsec_secy_attrs {
-	MACSEC_SECY_ATTR_UNSPEC,
-	MACSEC_SECY_ATTR_SCI,
-	MACSEC_SECY_ATTR_ENCODING_SA,
-	MACSEC_SECY_ATTR_WINDOW,
-	MACSEC_SECY_ATTR_CIPHER_SUITE,
-	MACSEC_SECY_ATTR_ICV_LEN,
-	MACSEC_SECY_ATTR_PROTECT,
-	MACSEC_SECY_ATTR_REPLAY,
-	MACSEC_SECY_ATTR_OPER,
-	MACSEC_SECY_ATTR_VALIDATE,
-	MACSEC_SECY_ATTR_ENCRYPT,
-	MACSEC_SECY_ATTR_INC_SCI,
-	MACSEC_SECY_ATTR_ES,
-	MACSEC_SECY_ATTR_SCB,
-	MACSEC_SECY_ATTR_PAD,
-	__MACSEC_SECY_ATTR_END,
-	NUM_MACSEC_SECY_ATTR = __MACSEC_SECY_ATTR_END,
-	MACSEC_SECY_ATTR_MAX = __MACSEC_SECY_ATTR_END - 1,
-};
-
-enum macsec_rxsc_attrs {
-	MACSEC_RXSC_ATTR_UNSPEC,
-	MACSEC_RXSC_ATTR_SCI,     /* config/dump, u64 */
-	MACSEC_RXSC_ATTR_ACTIVE,  /* config/dump, u8 0..1 */
-	MACSEC_RXSC_ATTR_SA_LIST, /* dump, nested */
-	MACSEC_RXSC_ATTR_STATS,   /* dump, nested, macsec_rxsc_stats_attr */
-	MACSEC_RXSC_ATTR_PAD,
-	__MACSEC_RXSC_ATTR_END,
-	NUM_MACSEC_RXSC_ATTR = __MACSEC_RXSC_ATTR_END,
-	MACSEC_RXSC_ATTR_MAX = __MACSEC_RXSC_ATTR_END - 1,
-};
-
-enum macsec_sa_attrs {
-	MACSEC_SA_ATTR_UNSPEC,
-	MACSEC_SA_ATTR_AN,     /* config/dump, u8 0..3 */
-	MACSEC_SA_ATTR_ACTIVE, /* config/dump, u8 0..1 */
-	MACSEC_SA_ATTR_PN,     /* config/dump, u32 */
-	MACSEC_SA_ATTR_KEY,    /* config, data */
-	MACSEC_SA_ATTR_KEYID,  /* config/dump, 128-bit */
-	MACSEC_SA_ATTR_STATS,  /* dump, nested, macsec_sa_stats_attr */
-	MACSEC_SA_ATTR_PAD,
-	__MACSEC_SA_ATTR_END,
-	NUM_MACSEC_SA_ATTR = __MACSEC_SA_ATTR_END,
-	MACSEC_SA_ATTR_MAX = __MACSEC_SA_ATTR_END - 1,
-};
-
-enum macsec_nl_commands {
-	MACSEC_CMD_GET_TXSC,
-	MACSEC_CMD_ADD_RXSC,
-	MACSEC_CMD_DEL_RXSC,
-	MACSEC_CMD_UPD_RXSC,
-	MACSEC_CMD_ADD_TXSA,
-	MACSEC_CMD_DEL_TXSA,
-	MACSEC_CMD_UPD_TXSA,
-	MACSEC_CMD_ADD_RXSA,
-	MACSEC_CMD_DEL_RXSA,
-	MACSEC_CMD_UPD_RXSA,
-};
-
-/* u64 per-RXSC stats */
-enum macsec_rxsc_stats_attr {
-	MACSEC_RXSC_STATS_ATTR_UNSPEC,
-	MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED,
-	MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED,
-	MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED,
-	MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED,
-	MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK,
-	MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID,
-	MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE,
-	MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID,
-	MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA,
-	MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA,
-	MACSEC_RXSC_STATS_ATTR_PAD,
-	__MACSEC_RXSC_STATS_ATTR_END,
-	NUM_MACSEC_RXSC_STATS_ATTR = __MACSEC_RXSC_STATS_ATTR_END,
-	MACSEC_RXSC_STATS_ATTR_MAX = __MACSEC_RXSC_STATS_ATTR_END - 1,
-};
-
-/* u32 per-{RX,TX}SA stats */
-enum macsec_sa_stats_attr {
-	MACSEC_SA_STATS_ATTR_UNSPEC,
-	MACSEC_SA_STATS_ATTR_IN_PKTS_OK,
-	MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID,
-	MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID,
-	MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA,
-	MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA,
-	MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED,
-	MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED,
-	__MACSEC_SA_STATS_ATTR_END,
-	NUM_MACSEC_SA_STATS_ATTR = __MACSEC_SA_STATS_ATTR_END,
-	MACSEC_SA_STATS_ATTR_MAX = __MACSEC_SA_STATS_ATTR_END - 1,
-};
-
-/* u64 per-TXSC stats */
-enum macsec_txsc_stats_attr {
-	MACSEC_TXSC_STATS_ATTR_UNSPEC,
-	MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED,
-	MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED,
-	MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED,
-	MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED,
-	MACSEC_TXSC_STATS_ATTR_PAD,
-	__MACSEC_TXSC_STATS_ATTR_END,
-	NUM_MACSEC_TXSC_STATS_ATTR = __MACSEC_TXSC_STATS_ATTR_END,
-	MACSEC_TXSC_STATS_ATTR_MAX = __MACSEC_TXSC_STATS_ATTR_END - 1,
-};
-
-/* u64 per-SecY stats */
-enum macsec_secy_stats_attr {
-	MACSEC_SECY_STATS_ATTR_UNSPEC,
-	MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED,
-	MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED,
-	MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG,
-	MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG,
-	MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG,
-	MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI,
-	MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI,
-	MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN,
-	MACSEC_SECY_STATS_ATTR_PAD,
-	__MACSEC_SECY_STATS_ATTR_END,
-	NUM_MACSEC_SECY_STATS_ATTR = __MACSEC_SECY_STATS_ATTR_END,
-	MACSEC_SECY_STATS_ATTR_MAX = __MACSEC_SECY_STATS_ATTR_END - 1,
-};
-
-#endif /* _MACSEC_H */
diff --git a/include/uapi/linux/if_packet.h b/include/uapi/linux/if_packet.h
deleted file mode 100644
index 3d884d6..0000000
--- a/include/uapi/linux/if_packet.h
+++ /dev/null
@@ -1,304 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef __LINUX_IF_PACKET_H
-#define __LINUX_IF_PACKET_H
-
-#include <linux/types.h>
-
-struct sockaddr_pkt {
-	unsigned short spkt_family;
-	unsigned char spkt_device[14];
-	__be16 spkt_protocol;
-};
-
-struct sockaddr_ll {
-	unsigned short	sll_family;
-	__be16		sll_protocol;
-	int		sll_ifindex;
-	unsigned short	sll_hatype;
-	unsigned char	sll_pkttype;
-	unsigned char	sll_halen;
-	unsigned char	sll_addr[8];
-};
-
-/* Packet types */
-
-#define PACKET_HOST		0		/* To us		*/
-#define PACKET_BROADCAST	1		/* To all		*/
-#define PACKET_MULTICAST	2		/* To group		*/
-#define PACKET_OTHERHOST	3		/* To someone else 	*/
-#define PACKET_OUTGOING		4		/* Outgoing of any type */
-#define PACKET_LOOPBACK		5		/* MC/BRD frame looped back */
-#define PACKET_USER		6		/* To user space	*/
-#define PACKET_KERNEL		7		/* To kernel space	*/
-/* Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space */
-#define PACKET_FASTROUTE	6		/* Fastrouted frame	*/
-
-/* Packet socket options */
-
-#define PACKET_ADD_MEMBERSHIP		1
-#define PACKET_DROP_MEMBERSHIP		2
-#define PACKET_RECV_OUTPUT		3
-/* Value 4 is still used by obsolete turbo-packet. */
-#define PACKET_RX_RING			5
-#define PACKET_STATISTICS		6
-#define PACKET_COPY_THRESH		7
-#define PACKET_AUXDATA			8
-#define PACKET_ORIGDEV			9
-#define PACKET_VERSION			10
-#define PACKET_HDRLEN			11
-#define PACKET_RESERVE			12
-#define PACKET_TX_RING			13
-#define PACKET_LOSS			14
-#define PACKET_VNET_HDR			15
-#define PACKET_TX_TIMESTAMP		16
-#define PACKET_TIMESTAMP		17
-#define PACKET_FANOUT			18
-#define PACKET_TX_HAS_OFF		19
-#define PACKET_QDISC_BYPASS		20
-#define PACKET_ROLLOVER_STATS		21
-#define PACKET_FANOUT_DATA		22
-#define PACKET_IGNORE_OUTGOING		23
-
-#define PACKET_FANOUT_HASH		0
-#define PACKET_FANOUT_LB		1
-#define PACKET_FANOUT_CPU		2
-#define PACKET_FANOUT_ROLLOVER		3
-#define PACKET_FANOUT_RND		4
-#define PACKET_FANOUT_QM		5
-#define PACKET_FANOUT_CBPF		6
-#define PACKET_FANOUT_EBPF		7
-#define PACKET_FANOUT_FLAG_ROLLOVER	0x1000
-#define PACKET_FANOUT_FLAG_UNIQUEID	0x2000
-#define PACKET_FANOUT_FLAG_DEFRAG	0x8000
-
-struct tpacket_stats {
-	unsigned int	tp_packets;
-	unsigned int	tp_drops;
-};
-
-struct tpacket_stats_v3 {
-	unsigned int	tp_packets;
-	unsigned int	tp_drops;
-	unsigned int	tp_freeze_q_cnt;
-};
-
-struct tpacket_rollover_stats {
-	__aligned_u64	tp_all;
-	__aligned_u64	tp_huge;
-	__aligned_u64	tp_failed;
-};
-
-union tpacket_stats_u {
-	struct tpacket_stats stats1;
-	struct tpacket_stats_v3 stats3;
-};
-
-struct tpacket_auxdata {
-	__u32		tp_status;
-	__u32		tp_len;
-	__u32		tp_snaplen;
-	__u16		tp_mac;
-	__u16		tp_net;
-	__u16		tp_vlan_tci;
-	__u16		tp_vlan_tpid;
-};
-
-/* Rx ring - header status */
-#define TP_STATUS_KERNEL		      0
-#define TP_STATUS_USER			(1 << 0)
-#define TP_STATUS_COPY			(1 << 1)
-#define TP_STATUS_LOSING		(1 << 2)
-#define TP_STATUS_CSUMNOTREADY		(1 << 3)
-#define TP_STATUS_VLAN_VALID		(1 << 4) /* auxdata has valid tp_vlan_tci */
-#define TP_STATUS_BLK_TMO		(1 << 5)
-#define TP_STATUS_VLAN_TPID_VALID	(1 << 6) /* auxdata has valid tp_vlan_tpid */
-#define TP_STATUS_CSUM_VALID		(1 << 7)
-
-/* Tx ring - header status */
-#define TP_STATUS_AVAILABLE	      0
-#define TP_STATUS_SEND_REQUEST	(1 << 0)
-#define TP_STATUS_SENDING	(1 << 1)
-#define TP_STATUS_WRONG_FORMAT	(1 << 2)
-
-/* Rx and Tx ring - header status */
-#define TP_STATUS_TS_SOFTWARE		(1 << 29)
-#define TP_STATUS_TS_SYS_HARDWARE	(1 << 30) /* deprecated, never set */
-#define TP_STATUS_TS_RAW_HARDWARE	(1U << 31)
-
-/* Rx ring - feature request bits */
-#define TP_FT_REQ_FILL_RXHASH	0x1
-
-struct tpacket_hdr {
-	unsigned long	tp_status;
-	unsigned int	tp_len;
-	unsigned int	tp_snaplen;
-	unsigned short	tp_mac;
-	unsigned short	tp_net;
-	unsigned int	tp_sec;
-	unsigned int	tp_usec;
-};
-
-#define TPACKET_ALIGNMENT	16
-#define TPACKET_ALIGN(x)	(((x)+TPACKET_ALIGNMENT-1)&~(TPACKET_ALIGNMENT-1))
-#define TPACKET_HDRLEN		(TPACKET_ALIGN(sizeof(struct tpacket_hdr)) + sizeof(struct sockaddr_ll))
-
-struct tpacket2_hdr {
-	__u32		tp_status;
-	__u32		tp_len;
-	__u32		tp_snaplen;
-	__u16		tp_mac;
-	__u16		tp_net;
-	__u32		tp_sec;
-	__u32		tp_nsec;
-	__u16		tp_vlan_tci;
-	__u16		tp_vlan_tpid;
-	__u8		tp_padding[4];
-};
-
-struct tpacket_hdr_variant1 {
-	__u32	tp_rxhash;
-	__u32	tp_vlan_tci;
-	__u16	tp_vlan_tpid;
-	__u16	tp_padding;
-};
-
-struct tpacket3_hdr {
-	__u32		tp_next_offset;
-	__u32		tp_sec;
-	__u32		tp_nsec;
-	__u32		tp_snaplen;
-	__u32		tp_len;
-	__u32		tp_status;
-	__u16		tp_mac;
-	__u16		tp_net;
-	/* pkt_hdr variants */
-	union {
-		struct tpacket_hdr_variant1 hv1;
-	};
-	__u8		tp_padding[8];
-};
-
-struct tpacket_bd_ts {
-	unsigned int ts_sec;
-	union {
-		unsigned int ts_usec;
-		unsigned int ts_nsec;
-	};
-};
-
-struct tpacket_hdr_v1 {
-	__u32	block_status;
-	__u32	num_pkts;
-	__u32	offset_to_first_pkt;
-
-	/* Number of valid bytes (including padding)
-	 * blk_len <= tp_block_size
-	 */
-	__u32	blk_len;
-
-	/*
-	 * Quite a few uses of sequence number:
-	 * 1. Make sure cache flush etc worked.
-	 *    Well, one can argue - why not use the increasing ts below?
-	 *    But look at 2. below first.
-	 * 2. When you pass around blocks to other user space decoders,
-	 *    you can see which blk[s] is[are] outstanding etc.
-	 * 3. Validate kernel code.
-	 */
-	__aligned_u64	seq_num;
-
-	/*
-	 * ts_last_pkt:
-	 *
-	 * Case 1.	Block has 'N'(N >=1) packets and TMO'd(timed out)
-	 *		ts_last_pkt == 'time-stamp of last packet' and NOT the
-	 *		time when the timer fired and the block was closed.
-	 *		By providing the ts of the last packet we can absolutely
-	 *		guarantee that time-stamp wise, the first packet in the
-	 *		next block will never precede the last packet of the
-	 *		previous block.
-	 * Case 2.	Block has zero packets and TMO'd
-	 *		ts_last_pkt = time when the timer fired and the block
-	 *		was closed.
-	 * Case 3.	Block has 'N' packets and NO TMO.
-	 *		ts_last_pkt = time-stamp of the last pkt in the block.
-	 *
-	 * ts_first_pkt:
-	 *		Is always the time-stamp when the block was opened.
-	 *		Case a)	ZERO packets
-	 *			No packets to deal with but atleast you know the
-	 *			time-interval of this block.
-	 *		Case b) Non-zero packets
-	 *			Use the ts of the first packet in the block.
-	 *
-	 */
-	struct tpacket_bd_ts	ts_first_pkt, ts_last_pkt;
-};
-
-union tpacket_bd_header_u {
-	struct tpacket_hdr_v1 bh1;
-};
-
-struct tpacket_block_desc {
-	__u32 version;
-	__u32 offset_to_priv;
-	union tpacket_bd_header_u hdr;
-};
-
-#define TPACKET2_HDRLEN		(TPACKET_ALIGN(sizeof(struct tpacket2_hdr)) + sizeof(struct sockaddr_ll))
-#define TPACKET3_HDRLEN		(TPACKET_ALIGN(sizeof(struct tpacket3_hdr)) + sizeof(struct sockaddr_ll))
-
-enum tpacket_versions {
-	TPACKET_V1,
-	TPACKET_V2,
-	TPACKET_V3
-};
-
-/*
-   Frame structure:
-
-   - Start. Frame must be aligned to TPACKET_ALIGNMENT=16
-   - struct tpacket_hdr
-   - pad to TPACKET_ALIGNMENT=16
-   - struct sockaddr_ll
-   - Gap, chosen so that packet data (Start+tp_net) alignes to TPACKET_ALIGNMENT=16
-   - Start+tp_mac: [ Optional MAC header ]
-   - Start+tp_net: Packet data, aligned to TPACKET_ALIGNMENT=16.
-   - Pad to align to TPACKET_ALIGNMENT=16
- */
-
-struct tpacket_req {
-	unsigned int	tp_block_size;	/* Minimal size of contiguous block */
-	unsigned int	tp_block_nr;	/* Number of blocks */
-	unsigned int	tp_frame_size;	/* Size of frame */
-	unsigned int	tp_frame_nr;	/* Total number of frames */
-};
-
-struct tpacket_req3 {
-	unsigned int	tp_block_size;	/* Minimal size of contiguous block */
-	unsigned int	tp_block_nr;	/* Number of blocks */
-	unsigned int	tp_frame_size;	/* Size of frame */
-	unsigned int	tp_frame_nr;	/* Total number of frames */
-	unsigned int	tp_retire_blk_tov; /* timeout in msecs */
-	unsigned int	tp_sizeof_priv; /* offset to private data area */
-	unsigned int	tp_feature_req_word;
-};
-
-union tpacket_req_u {
-	struct tpacket_req	req;
-	struct tpacket_req3	req3;
-};
-
-struct packet_mreq {
-	int		mr_ifindex;
-	unsigned short	mr_type;
-	unsigned short	mr_alen;
-	unsigned char	mr_address[8];
-};
-
-#define PACKET_MR_MULTICAST	0
-#define PACKET_MR_PROMISC	1
-#define PACKET_MR_ALLMULTI	2
-#define PACKET_MR_UNICAST	3
-
-#endif
diff --git a/include/uapi/linux/ife.h b/include/uapi/linux/ife.h
deleted file mode 100644
index bdd953c..0000000
--- a/include/uapi/linux/ife.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef __UAPI_IFE_H
-#define __UAPI_IFE_H
-
-#define IFE_METAHDRLEN 2
-
-enum {
-	IFE_META_SKBMARK = 1,
-	IFE_META_HASHID,
-	IFE_META_PRIO,
-	IFE_META_QMAP,
-	IFE_META_TCINDEX,
-	__IFE_META_MAX
-};
-
-/*Can be overridden at runtime by module option*/
-#define IFE_META_MAX (__IFE_META_MAX - 1)
-
-#endif
diff --git a/include/uapi/linux/ila.h b/include/uapi/linux/ila.h
deleted file mode 100644
index 6a6c97c..0000000
--- a/include/uapi/linux/ila.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* ila.h - ILA Interface */
-
-#ifndef _LINUX_ILA_H
-#define _LINUX_ILA_H
-
-/* NETLINK_GENERIC related info */
-#define ILA_GENL_NAME		"ila"
-#define ILA_GENL_VERSION	0x1
-
-enum {
-	ILA_ATTR_UNSPEC,
-	ILA_ATTR_LOCATOR,			/* u64 */
-	ILA_ATTR_IDENTIFIER,			/* u64 */
-	ILA_ATTR_LOCATOR_MATCH,			/* u64 */
-	ILA_ATTR_IFINDEX,			/* s32 */
-	ILA_ATTR_DIR,				/* u32 */
-	ILA_ATTR_PAD,
-	ILA_ATTR_CSUM_MODE,			/* u8 */
-	ILA_ATTR_IDENT_TYPE,			/* u8 */
-	ILA_ATTR_HOOK_TYPE,			/* u8 */
-
-	__ILA_ATTR_MAX,
-};
-
-#define ILA_ATTR_MAX		(__ILA_ATTR_MAX - 1)
-
-enum {
-	ILA_CMD_UNSPEC,
-	ILA_CMD_ADD,
-	ILA_CMD_DEL,
-	ILA_CMD_GET,
-	ILA_CMD_FLUSH,
-
-	__ILA_CMD_MAX,
-};
-
-#define ILA_CMD_MAX	(__ILA_CMD_MAX - 1)
-
-#define ILA_DIR_IN	(1 << 0)
-#define ILA_DIR_OUT	(1 << 1)
-
-enum {
-	ILA_CSUM_ADJUST_TRANSPORT,
-	ILA_CSUM_NEUTRAL_MAP,
-	ILA_CSUM_NO_ACTION,
-	ILA_CSUM_NEUTRAL_MAP_AUTO,
-};
-
-enum {
-	ILA_ATYPE_IID = 0,
-	ILA_ATYPE_LUID,
-	ILA_ATYPE_VIRT_V4,
-	ILA_ATYPE_VIRT_UNI_V6,
-	ILA_ATYPE_VIRT_MULTI_V6,
-	ILA_ATYPE_NONLOCAL_ADDR,
-	ILA_ATYPE_RSVD_1,
-	ILA_ATYPE_RSVD_2,
-
-	ILA_ATYPE_USE_FORMAT = 32, /* Get type from type field in identifier */
-};
-
-enum {
-	ILA_HOOK_ROUTE_OUTPUT,
-	ILA_HOOK_ROUTE_INPUT,
-};
-
-#endif /* _LINUX_ILA_H */
diff --git a/include/uapi/linux/in_route.h b/include/uapi/linux/in_route.h
deleted file mode 100644
index 0cc2c23..0000000
--- a/include/uapi/linux/in_route.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_IN_ROUTE_H
-#define _LINUX_IN_ROUTE_H
-
-/* IPv4 routing cache flags */
-
-#define RTCF_DEAD	RTNH_F_DEAD
-#define RTCF_ONLINK	RTNH_F_ONLINK
-
-/* Obsolete flag. About to be deleted */
-#define RTCF_NOPMTUDISC RTM_F_NOPMTUDISC
-
-#define RTCF_NOTIFY	0x00010000
-#define RTCF_DIRECTDST	0x00020000 /* unused */
-#define RTCF_REDIRECTED	0x00040000
-#define RTCF_TPROXY	0x00080000 /* unused */
-
-#define RTCF_FAST	0x00200000 /* unused */
-#define RTCF_MASQ	0x00400000 /* unused */
-#define RTCF_SNAT	0x00800000 /* unused */
-#define RTCF_DOREDIRECT 0x01000000
-#define RTCF_DIRECTSRC	0x04000000
-#define RTCF_DNAT	0x08000000
-#define RTCF_BROADCAST	0x10000000
-#define RTCF_MULTICAST	0x20000000
-#define RTCF_REJECT	0x40000000 /* unused */
-#define RTCF_LOCAL	0x80000000
-
-#define RTCF_NAT	(RTCF_DNAT|RTCF_SNAT)
-
-#define RT_TOS(tos)	((tos)&IPTOS_TOS_MASK)
-
-#endif /* _LINUX_IN_ROUTE_H */
diff --git a/include/uapi/linux/ip.h b/include/uapi/linux/ip.h
deleted file mode 100644
index f4ecd2f..0000000
--- a/include/uapi/linux/ip.h
+++ /dev/null
@@ -1,177 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
-/*
- * INET		An implementation of the TCP/IP protocol suite for the LINUX
- *		operating system.  INET is implemented using the  BSD Socket
- *		interface as the means of communication with the user level.
- *
- *		Definitions for the IP protocol.
- *
- * Version:	@(#)ip.h	1.0.2	04/28/93
- *
- * Authors:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- *
- *		This program is free software; you can redistribute it and/or
- *		modify it under the terms of the GNU General Public License
- *		as published by the Free Software Foundation; either version
- *		2 of the License, or (at your option) any later version.
- */
-#ifndef _LINUX_IP_H
-#define _LINUX_IP_H
-#include <linux/types.h>
-#include <asm/byteorder.h>
-
-#define IPTOS_TOS_MASK		0x1E
-#define IPTOS_TOS(tos)		((tos)&IPTOS_TOS_MASK)
-#define	IPTOS_LOWDELAY		0x10
-#define	IPTOS_THROUGHPUT	0x08
-#define	IPTOS_RELIABILITY	0x04
-#define	IPTOS_MINCOST		0x02
-
-#define IPTOS_PREC_MASK		0xE0
-#define IPTOS_PREC(tos)		((tos)&IPTOS_PREC_MASK)
-#define IPTOS_PREC_NETCONTROL           0xe0
-#define IPTOS_PREC_INTERNETCONTROL      0xc0
-#define IPTOS_PREC_CRITIC_ECP           0xa0
-#define IPTOS_PREC_FLASHOVERRIDE        0x80
-#define IPTOS_PREC_FLASH                0x60
-#define IPTOS_PREC_IMMEDIATE            0x40
-#define IPTOS_PREC_PRIORITY             0x20
-#define IPTOS_PREC_ROUTINE              0x00
-
-
-/* IP options */
-#define IPOPT_COPY		0x80
-#define IPOPT_CLASS_MASK	0x60
-#define IPOPT_NUMBER_MASK	0x1f
-
-#define	IPOPT_COPIED(o)		((o)&IPOPT_COPY)
-#define	IPOPT_CLASS(o)		((o)&IPOPT_CLASS_MASK)
-#define	IPOPT_NUMBER(o)		((o)&IPOPT_NUMBER_MASK)
-
-#define	IPOPT_CONTROL		0x00
-#define	IPOPT_RESERVED1		0x20
-#define	IPOPT_MEASUREMENT	0x40
-#define	IPOPT_RESERVED2		0x60
-
-#define IPOPT_END	(0 |IPOPT_CONTROL)
-#define IPOPT_NOOP	(1 |IPOPT_CONTROL)
-#define IPOPT_SEC	(2 |IPOPT_CONTROL|IPOPT_COPY)
-#define IPOPT_LSRR	(3 |IPOPT_CONTROL|IPOPT_COPY)
-#define IPOPT_TIMESTAMP	(4 |IPOPT_MEASUREMENT)
-#define IPOPT_CIPSO	(6 |IPOPT_CONTROL|IPOPT_COPY)
-#define IPOPT_RR	(7 |IPOPT_CONTROL)
-#define IPOPT_SID	(8 |IPOPT_CONTROL|IPOPT_COPY)
-#define IPOPT_SSRR	(9 |IPOPT_CONTROL|IPOPT_COPY)
-#define IPOPT_RA	(20|IPOPT_CONTROL|IPOPT_COPY)
-
-#define IPVERSION	4
-#define MAXTTL		255
-#define IPDEFTTL	64
-
-#define IPOPT_OPTVAL 0
-#define IPOPT_OLEN   1
-#define IPOPT_OFFSET 2
-#define IPOPT_MINOFF 4
-#define MAX_IPOPTLEN 40
-#define IPOPT_NOP IPOPT_NOOP
-#define IPOPT_EOL IPOPT_END
-#define IPOPT_TS  IPOPT_TIMESTAMP
-
-#define	IPOPT_TS_TSONLY		0		/* timestamps only */
-#define	IPOPT_TS_TSANDADDR	1		/* timestamps and addresses */
-#define	IPOPT_TS_PRESPEC	3		/* specified modules only */
-
-#define IPV4_BEET_PHMAXLEN 8
-
-struct iphdr {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-	__u8	ihl:4,
-		version:4;
-#elif defined (__BIG_ENDIAN_BITFIELD)
-	__u8	version:4,
-  		ihl:4;
-#else
-#error	"Please fix <asm/byteorder.h>"
-#endif
-	__u8	tos;
-	__be16	tot_len;
-	__be16	id;
-	__be16	frag_off;
-	__u8	ttl;
-	__u8	protocol;
-	__sum16	check;
-	__be32	saddr;
-	__be32	daddr;
-	/*The options start here. */
-};
-
-
-struct ip_auth_hdr {
-	__u8  nexthdr;
-	__u8  hdrlen;		/* This one is measured in 32 bit units! */
-	__be16 reserved;
-	__be32 spi;
-	__be32 seq_no;		/* Sequence number */
-	__u8  auth_data[0];	/* Variable len but >=4. Mind the 64 bit alignment! */
-};
-
-struct ip_esp_hdr {
-	__be32 spi;
-	__be32 seq_no;		/* Sequence number */
-	__u8  enc_data[0];	/* Variable len but >=8. Mind the 64 bit alignment! */
-};
-
-struct ip_comp_hdr {
-	__u8 nexthdr;
-	__u8 flags;
-	__be16 cpi;
-};
-
-struct ip_beet_phdr {
-	__u8 nexthdr;
-	__u8 hdrlen;
-	__u8 padlen;
-	__u8 reserved;
-};
-
-/* index values for the variables in ipv4_devconf */
-enum
-{
-	IPV4_DEVCONF_FORWARDING=1,
-	IPV4_DEVCONF_MC_FORWARDING,
-	IPV4_DEVCONF_PROXY_ARP,
-	IPV4_DEVCONF_ACCEPT_REDIRECTS,
-	IPV4_DEVCONF_SECURE_REDIRECTS,
-	IPV4_DEVCONF_SEND_REDIRECTS,
-	IPV4_DEVCONF_SHARED_MEDIA,
-	IPV4_DEVCONF_RP_FILTER,
-	IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE,
-	IPV4_DEVCONF_BOOTP_RELAY,
-	IPV4_DEVCONF_LOG_MARTIANS,
-	IPV4_DEVCONF_TAG,
-	IPV4_DEVCONF_ARPFILTER,
-	IPV4_DEVCONF_MEDIUM_ID,
-	IPV4_DEVCONF_NOXFRM,
-	IPV4_DEVCONF_NOPOLICY,
-	IPV4_DEVCONF_FORCE_IGMP_VERSION,
-	IPV4_DEVCONF_ARP_ANNOUNCE,
-	IPV4_DEVCONF_ARP_IGNORE,
-	IPV4_DEVCONF_PROMOTE_SECONDARIES,
-	IPV4_DEVCONF_ARP_ACCEPT,
-	IPV4_DEVCONF_ARP_NOTIFY,
-	IPV4_DEVCONF_ACCEPT_LOCAL,
-	IPV4_DEVCONF_SRC_VMARK,
-	IPV4_DEVCONF_PROXY_ARP_PVLAN,
-	IPV4_DEVCONF_ROUTE_LOCALNET,
-	IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL,
-	IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL,
-	IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN,
-	IPV4_DEVCONF_DROP_UNICAST_IN_L2_MULTICAST,
-	IPV4_DEVCONF_DROP_GRATUITOUS_ARP,
-	IPV4_DEVCONF_BC_FORWARDING,
-	__IPV4_DEVCONF_MAX
-};
-
-#define IPV4_DEVCONF_MAX (__IPV4_DEVCONF_MAX - 1)
-
-#endif /* _LINUX_IP_H */
diff --git a/include/uapi/linux/ipsec.h b/include/uapi/linux/ipsec.h
deleted file mode 100644
index 50d8ee1..0000000
--- a/include/uapi/linux/ipsec.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_IPSEC_H
-#define _LINUX_IPSEC_H
-
-/* The definitions, required to talk to KAME racoon IKE. */
-
-#include <linux/pfkeyv2.h>
-
-#define IPSEC_PORT_ANY		0
-#define IPSEC_ULPROTO_ANY	255
-#define IPSEC_PROTO_ANY		255
-
-enum {
-	IPSEC_MODE_ANY		= 0,	/* We do not support this for SA */
-	IPSEC_MODE_TRANSPORT	= 1,
-	IPSEC_MODE_TUNNEL	= 2,
-	IPSEC_MODE_BEET         = 3
-};
-
-enum {
-	IPSEC_DIR_ANY		= 0,
-	IPSEC_DIR_INBOUND	= 1,
-	IPSEC_DIR_OUTBOUND	= 2,
-	IPSEC_DIR_FWD		= 3,	/* It is our own */
-	IPSEC_DIR_MAX		= 4,
-	IPSEC_DIR_INVALID	= 5
-};
-
-enum {
-	IPSEC_POLICY_DISCARD	= 0,
-	IPSEC_POLICY_NONE	= 1,
-	IPSEC_POLICY_IPSEC	= 2,
-	IPSEC_POLICY_ENTRUST	= 3,
-	IPSEC_POLICY_BYPASS	= 4
-};
-
-enum {
-	IPSEC_LEVEL_DEFAULT	= 0,
-	IPSEC_LEVEL_USE		= 1,
-	IPSEC_LEVEL_REQUIRE	= 2,
-	IPSEC_LEVEL_UNIQUE	= 3
-};
-
-#define IPSEC_MANUAL_REQID_MAX	0x3fff
-
-#define IPSEC_REPLAYWSIZE  32
-
-#endif	/* _LINUX_IPSEC_H */
diff --git a/include/uapi/linux/kernel.h b/include/uapi/linux/kernel.h
deleted file mode 100644
index d99ffa1..0000000
--- a/include/uapi/linux/kernel.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_KERNEL_H
-#define _LINUX_KERNEL_H
-
-#include <linux/sysinfo.h>
-
-/*
- * 'kernel.h' contains some often-used function prototypes etc
- */
-#define __ALIGN_KERNEL(x, a)		__ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
-#define __ALIGN_KERNEL_MASK(x, mask)	(((x) + (mask)) & ~(mask))
-
-#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
-
-#endif /* _LINUX_KERNEL_H */
diff --git a/include/uapi/linux/libc-compat.h b/include/uapi/linux/libc-compat.h
deleted file mode 100644
index a159991..0000000
--- a/include/uapi/linux/libc-compat.h
+++ /dev/null
@@ -1,267 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/*
- * Compatibility interface for userspace libc header coordination:
- *
- * Define compatibility macros that are used to control the inclusion or
- * exclusion of UAPI structures and definitions in coordination with another
- * userspace C library.
- *
- * This header is intended to solve the problem of UAPI definitions that
- * conflict with userspace definitions. If a UAPI header has such conflicting
- * definitions then the solution is as follows:
- *
- * * Synchronize the UAPI header and the libc headers so either one can be
- *   used and such that the ABI is preserved. If this is not possible then
- *   no simple compatibility interface exists (you need to write translating
- *   wrappers and rename things) and you can't use this interface.
- *
- * Then follow this process:
- *
- * (a) Include libc-compat.h in the UAPI header.
- *      e.g. #include <linux/libc-compat.h>
- *     This include must be as early as possible.
- *
- * (b) In libc-compat.h add enough code to detect that the comflicting
- *     userspace libc header has been included first.
- *
- * (c) If the userspace libc header has been included first define a set of
- *     guard macros of the form __UAPI_DEF_FOO and set their values to 1, else
- *     set their values to 0.
- *
- * (d) Back in the UAPI header with the conflicting definitions, guard the
- *     definitions with:
- *     #if __UAPI_DEF_FOO
- *       ...
- *     #endif
- *
- * This fixes the situation where the linux headers are included *after* the
- * libc headers. To fix the problem with the inclusion in the other order the
- * userspace libc headers must be fixed like this:
- *
- * * For all definitions that conflict with kernel definitions wrap those
- *   defines in the following:
- *   #if !__UAPI_DEF_FOO
- *     ...
- *   #endif
- *
- * This prevents the redefinition of a construct already defined by the kernel.
- */
-#ifndef _LIBC_COMPAT_H
-#define _LIBC_COMPAT_H
-
-/* We have included glibc headers... */
-#if defined(__GLIBC__)
-
-/* Coordinate with glibc net/if.h header. */
-#if defined(_NET_IF_H) && defined(__USE_MISC)
-
-/* GLIBC headers included first so don't define anything
- * that would already be defined. */
-
-#define __UAPI_DEF_IF_IFCONF 0
-#define __UAPI_DEF_IF_IFMAP 0
-#define __UAPI_DEF_IF_IFNAMSIZ 0
-#define __UAPI_DEF_IF_IFREQ 0
-/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */
-#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 0
-/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */
-#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO
-#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1
-#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */
-
-#else /* _NET_IF_H */
-
-/* Linux headers included first, and we must define everything
- * we need. The expectation is that glibc will check the
- * __UAPI_DEF_* defines and adjust appropriately. */
-
-#define __UAPI_DEF_IF_IFCONF 1
-#define __UAPI_DEF_IF_IFMAP 1
-#define __UAPI_DEF_IF_IFNAMSIZ 1
-#define __UAPI_DEF_IF_IFREQ 1
-/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */
-#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1
-/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */
-#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1
-
-#endif /* _NET_IF_H */
-
-/* Coordinate with glibc netinet/in.h header. */
-#if defined(_NETINET_IN_H)
-
-/* GLIBC headers included first so don't define anything
- * that would already be defined. */
-#define __UAPI_DEF_IN_ADDR		0
-#define __UAPI_DEF_IN_IPPROTO		0
-#define __UAPI_DEF_IN_PKTINFO		0
-#define __UAPI_DEF_IP_MREQ		0
-#define __UAPI_DEF_SOCKADDR_IN		0
-#define __UAPI_DEF_IN_CLASS		0
-
-#define __UAPI_DEF_IN6_ADDR		0
-/* The exception is the in6_addr macros which must be defined
- * if the glibc code didn't define them. This guard matches
- * the guard in glibc/inet/netinet/in.h which defines the
- * additional in6_addr macros e.g. s6_addr16, and s6_addr32. */
-#if defined(__USE_MISC) || defined (__USE_GNU)
-#define __UAPI_DEF_IN6_ADDR_ALT		0
-#else
-#define __UAPI_DEF_IN6_ADDR_ALT		1
-#endif
-#define __UAPI_DEF_SOCKADDR_IN6		0
-#define __UAPI_DEF_IPV6_MREQ		0
-#define __UAPI_DEF_IPPROTO_V6		0
-#define __UAPI_DEF_IPV6_OPTIONS		0
-#define __UAPI_DEF_IN6_PKTINFO		0
-#define __UAPI_DEF_IP6_MTUINFO		0
-
-#else
-
-/* Linux headers included first, and we must define everything
- * we need. The expectation is that glibc will check the
- * __UAPI_DEF_* defines and adjust appropriately. */
-#define __UAPI_DEF_IN_ADDR		1
-#define __UAPI_DEF_IN_IPPROTO		1
-#define __UAPI_DEF_IN_PKTINFO		1
-#define __UAPI_DEF_IP_MREQ		1
-#define __UAPI_DEF_SOCKADDR_IN		1
-#define __UAPI_DEF_IN_CLASS		1
-
-#define __UAPI_DEF_IN6_ADDR		1
-/* We unconditionally define the in6_addr macros and glibc must
- * coordinate. */
-#define __UAPI_DEF_IN6_ADDR_ALT		1
-#define __UAPI_DEF_SOCKADDR_IN6		1
-#define __UAPI_DEF_IPV6_MREQ		1
-#define __UAPI_DEF_IPPROTO_V6		1
-#define __UAPI_DEF_IPV6_OPTIONS		1
-#define __UAPI_DEF_IN6_PKTINFO		1
-#define __UAPI_DEF_IP6_MTUINFO		1
-
-#endif /* _NETINET_IN_H */
-
-/* Coordinate with glibc netipx/ipx.h header. */
-#if defined(__NETIPX_IPX_H)
-
-#define __UAPI_DEF_SOCKADDR_IPX			0
-#define __UAPI_DEF_IPX_ROUTE_DEFINITION		0
-#define __UAPI_DEF_IPX_INTERFACE_DEFINITION	0
-#define __UAPI_DEF_IPX_CONFIG_DATA		0
-#define __UAPI_DEF_IPX_ROUTE_DEF		0
-
-#else /* defined(__NETIPX_IPX_H) */
-
-#define __UAPI_DEF_SOCKADDR_IPX			1
-#define __UAPI_DEF_IPX_ROUTE_DEFINITION		1
-#define __UAPI_DEF_IPX_INTERFACE_DEFINITION	1
-#define __UAPI_DEF_IPX_CONFIG_DATA		1
-#define __UAPI_DEF_IPX_ROUTE_DEF		1
-
-#endif /* defined(__NETIPX_IPX_H) */
-
-/* Definitions for xattr.h */
-#if defined(_SYS_XATTR_H)
-#define __UAPI_DEF_XATTR		0
-#else
-#define __UAPI_DEF_XATTR		1
-#endif
-
-/* If we did not see any headers from any supported C libraries,
- * or we are being included in the kernel, then define everything
- * that we need. Check for previous __UAPI_* definitions to give
- * unsupported C libraries a way to opt out of any kernel definition. */
-#else /* !defined(__GLIBC__) */
-
-/* Definitions for if.h */
-#ifndef __UAPI_DEF_IF_IFCONF
-#define __UAPI_DEF_IF_IFCONF 1
-#endif
-#ifndef __UAPI_DEF_IF_IFMAP
-#define __UAPI_DEF_IF_IFMAP 1
-#endif
-#ifndef __UAPI_DEF_IF_IFNAMSIZ
-#define __UAPI_DEF_IF_IFNAMSIZ 1
-#endif
-#ifndef __UAPI_DEF_IF_IFREQ
-#define __UAPI_DEF_IF_IFREQ 1
-#endif
-/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */
-#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS
-#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1
-#endif
-/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */
-#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO
-#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1
-#endif
-
-/* Definitions for in.h */
-#ifndef __UAPI_DEF_IN_ADDR
-#define __UAPI_DEF_IN_ADDR		1
-#endif
-#ifndef __UAPI_DEF_IN_IPPROTO
-#define __UAPI_DEF_IN_IPPROTO		1
-#endif
-#ifndef __UAPI_DEF_IN_PKTINFO
-#define __UAPI_DEF_IN_PKTINFO		1
-#endif
-#ifndef __UAPI_DEF_IP_MREQ
-#define __UAPI_DEF_IP_MREQ		1
-#endif
-#ifndef __UAPI_DEF_SOCKADDR_IN
-#define __UAPI_DEF_SOCKADDR_IN		1
-#endif
-#ifndef __UAPI_DEF_IN_CLASS
-#define __UAPI_DEF_IN_CLASS		1
-#endif
-
-/* Definitions for in6.h */
-#ifndef __UAPI_DEF_IN6_ADDR
-#define __UAPI_DEF_IN6_ADDR		1
-#endif
-#ifndef __UAPI_DEF_IN6_ADDR_ALT
-#define __UAPI_DEF_IN6_ADDR_ALT		1
-#endif
-#ifndef __UAPI_DEF_SOCKADDR_IN6
-#define __UAPI_DEF_SOCKADDR_IN6		1
-#endif
-#ifndef __UAPI_DEF_IPV6_MREQ
-#define __UAPI_DEF_IPV6_MREQ		1
-#endif
-#ifndef __UAPI_DEF_IPPROTO_V6
-#define __UAPI_DEF_IPPROTO_V6		1
-#endif
-#ifndef __UAPI_DEF_IPV6_OPTIONS
-#define __UAPI_DEF_IPV6_OPTIONS		1
-#endif
-#ifndef __UAPI_DEF_IN6_PKTINFO
-#define __UAPI_DEF_IN6_PKTINFO		1
-#endif
-#ifndef __UAPI_DEF_IP6_MTUINFO
-#define __UAPI_DEF_IP6_MTUINFO		1
-#endif
-
-/* Definitions for ipx.h */
-#ifndef __UAPI_DEF_SOCKADDR_IPX
-#define __UAPI_DEF_SOCKADDR_IPX			1
-#endif
-#ifndef __UAPI_DEF_IPX_ROUTE_DEFINITION
-#define __UAPI_DEF_IPX_ROUTE_DEFINITION		1
-#endif
-#ifndef __UAPI_DEF_IPX_INTERFACE_DEFINITION
-#define __UAPI_DEF_IPX_INTERFACE_DEFINITION	1
-#endif
-#ifndef __UAPI_DEF_IPX_CONFIG_DATA
-#define __UAPI_DEF_IPX_CONFIG_DATA		1
-#endif
-#ifndef __UAPI_DEF_IPX_ROUTE_DEF
-#define __UAPI_DEF_IPX_ROUTE_DEF		1
-#endif
-
-/* Definitions for xattr.h */
-#ifndef __UAPI_DEF_XATTR
-#define __UAPI_DEF_XATTR		1
-#endif
-
-#endif /* __GLIBC__ */
-
-#endif /* _LIBC_COMPAT_H */
diff --git a/include/uapi/linux/limits.h b/include/uapi/linux/limits.h
deleted file mode 100644
index c3547f0..0000000
--- a/include/uapi/linux/limits.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_LIMITS_H
-#define _LINUX_LIMITS_H
-
-#define NR_OPEN	        1024
-
-#define NGROUPS_MAX    65536	/* supplemental group IDs are available */
-#define ARG_MAX       131072	/* # bytes of args + environ for exec() */
-#define LINK_MAX         127	/* # links a file may have */
-#define MAX_CANON        255	/* size of the canonical input queue */
-#define MAX_INPUT        255	/* size of the type-ahead buffer */
-#define NAME_MAX         255	/* # chars in a file name */
-#define PATH_MAX        4096	/* # chars in a path name including nul */
-#define PIPE_BUF        4096	/* # bytes in atomic write to a pipe */
-#define XATTR_NAME_MAX   255	/* # chars in an extended attribute name */
-#define XATTR_SIZE_MAX 65536	/* size of an extended attribute value (64k) */
-#define XATTR_LIST_MAX 65536	/* size of extended attribute namelist (64k) */
-
-#define RTSIG_MAX	  32
-
-#endif
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
deleted file mode 100644
index 903cc2d..0000000
--- a/include/uapi/linux/magic.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef __LINUX_MAGIC_H__
-#define __LINUX_MAGIC_H__
-
-#define ADFS_SUPER_MAGIC	0xadf5
-#define AFFS_SUPER_MAGIC	0xadff
-#define AFS_SUPER_MAGIC                0x5346414F
-#define AUTOFS_SUPER_MAGIC	0x0187
-#define CODA_SUPER_MAGIC	0x73757245
-#define CRAMFS_MAGIC		0x28cd3d45	/* some random number */
-#define CRAMFS_MAGIC_WEND	0x453dcd28	/* magic number with the wrong endianess */
-#define DEBUGFS_MAGIC          0x64626720
-#define SECURITYFS_MAGIC	0x73636673
-#define SELINUX_MAGIC		0xf97cff8c
-#define SMACK_MAGIC		0x43415d53	/* "SMAC" */
-#define RAMFS_MAGIC		0x858458f6	/* some random number */
-#define TMPFS_MAGIC		0x01021994
-#define HUGETLBFS_MAGIC 	0x958458f6	/* some random number */
-#define SQUASHFS_MAGIC		0x73717368
-#define ECRYPTFS_SUPER_MAGIC	0xf15f
-#define EFS_SUPER_MAGIC		0x414A53
-#define EROFS_SUPER_MAGIC_V1	0xE0F5E1E2
-#define EXT2_SUPER_MAGIC	0xEF53
-#define EXT3_SUPER_MAGIC	0xEF53
-#define XENFS_SUPER_MAGIC	0xabba1974
-#define EXT4_SUPER_MAGIC	0xEF53
-#define BTRFS_SUPER_MAGIC	0x9123683E
-#define NILFS_SUPER_MAGIC	0x3434
-#define F2FS_SUPER_MAGIC	0xF2F52010
-#define HPFS_SUPER_MAGIC	0xf995e849
-#define ISOFS_SUPER_MAGIC	0x9660
-#define JFFS2_SUPER_MAGIC	0x72b6
-#define XFS_SUPER_MAGIC		0x58465342	/* "XFSB" */
-#define PSTOREFS_MAGIC		0x6165676C
-#define EFIVARFS_MAGIC		0xde5e81e4
-#define HOSTFS_SUPER_MAGIC	0x00c0ffee
-#define OVERLAYFS_SUPER_MAGIC	0x794c7630
-
-#define MINIX_SUPER_MAGIC	0x137F		/* minix v1 fs, 14 char names */
-#define MINIX_SUPER_MAGIC2	0x138F		/* minix v1 fs, 30 char names */
-#define MINIX2_SUPER_MAGIC	0x2468		/* minix v2 fs, 14 char names */
-#define MINIX2_SUPER_MAGIC2	0x2478		/* minix v2 fs, 30 char names */
-#define MINIX3_SUPER_MAGIC	0x4d5a		/* minix v3 fs, 60 char names */
-
-#define MSDOS_SUPER_MAGIC	0x4d44		/* MD */
-#define NCP_SUPER_MAGIC		0x564c		/* Guess, what 0x564c is :-) */
-#define NFS_SUPER_MAGIC		0x6969
-#define OCFS2_SUPER_MAGIC	0x7461636f
-#define OPENPROM_SUPER_MAGIC	0x9fa1
-#define QNX4_SUPER_MAGIC	0x002f		/* qnx4 fs detection */
-#define QNX6_SUPER_MAGIC	0x68191122	/* qnx6 fs detection */
-#define AFS_FS_MAGIC		0x6B414653
-
-#define REISERFS_SUPER_MAGIC	0x52654973	/* used by gcc */
-					/* used by file system utilities that
-	                                   look at the superblock, etc.  */
-#define REISERFS_SUPER_MAGIC_STRING	"ReIsErFs"
-#define REISER2FS_SUPER_MAGIC_STRING	"ReIsEr2Fs"
-#define REISER2FS_JR_SUPER_MAGIC_STRING	"ReIsEr3Fs"
-
-#define SMB_SUPER_MAGIC		0x517B
-#define CGROUP_SUPER_MAGIC	0x27e0eb
-#define CGROUP2_SUPER_MAGIC	0x63677270
-
-#define RDTGROUP_SUPER_MAGIC	0x7655821
-
-#define STACK_END_MAGIC		0x57AC6E9D
-
-#define TRACEFS_MAGIC          0x74726163
-
-#define V9FS_MAGIC		0x01021997
-
-#define BDEVFS_MAGIC            0x62646576
-#define DAXFS_MAGIC             0x64646178
-#define BINFMTFS_MAGIC          0x42494e4d
-#define DEVPTS_SUPER_MAGIC	0x1cd1
-#define BINDERFS_SUPER_MAGIC	0x6c6f6f70
-#define FUTEXFS_SUPER_MAGIC	0xBAD1DEA
-#define PIPEFS_MAGIC            0x50495045
-#define PROC_SUPER_MAGIC	0x9fa0
-#define SOCKFS_MAGIC		0x534F434B
-#define SYSFS_MAGIC		0x62656572
-#define USBDEVICE_SUPER_MAGIC	0x9fa2
-#define MTD_INODE_FS_MAGIC      0x11307854
-#define ANON_INODE_FS_MAGIC	0x09041934
-#define BTRFS_TEST_MAGIC	0x73727279
-#define NSFS_MAGIC		0x6e736673
-#define BPF_FS_MAGIC		0xcafe4a11
-#define AAFS_MAGIC		0x5a3c69f0
-
-/* Since UDF 2.01 is ISO 13346 based... */
-#define UDF_SUPER_MAGIC		0x15013346
-#define BALLOON_KVM_MAGIC	0x13661366
-#define ZSMALLOC_MAGIC		0x58295829
-#define DMA_BUF_MAGIC		0x444d4142	/* "DMAB" */
-#define Z3FOLD_MAGIC		0x33
-
-#endif /* __LINUX_MAGIC_H__ */
diff --git a/include/uapi/linux/net.h b/include/uapi/linux/net.h
deleted file mode 100644
index 4754f70..0000000
--- a/include/uapi/linux/net.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
-/*
- * NET		An implementation of the SOCKET network access protocol.
- *		This is the master header file for the Linux NET layer,
- *		or, in plain English: the networking handling part of the
- *		kernel.
- *
- * Version:	@(#)net.h	1.0.3	05/25/93
- *
- * Authors:	Orest Zborowski, <obz@Kodak.COM>
- *		Ross Biro
- *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- *
- *		This program is free software; you can redistribute it and/or
- *		modify it under the terms of the GNU General Public License
- *		as published by the Free Software Foundation; either version
- *		2 of the License, or (at your option) any later version.
- */
-#ifndef _LINUX_NET_H
-#define _LINUX_NET_H
-
-#include <linux/socket.h>
-#include <asm/socket.h>
-
-#define NPROTO		AF_MAX
-
-#define SYS_SOCKET	1		/* sys_socket(2)		*/
-#define SYS_BIND	2		/* sys_bind(2)			*/
-#define SYS_CONNECT	3		/* sys_connect(2)		*/
-#define SYS_LISTEN	4		/* sys_listen(2)		*/
-#define SYS_ACCEPT	5		/* sys_accept(2)		*/
-#define SYS_GETSOCKNAME	6		/* sys_getsockname(2)		*/
-#define SYS_GETPEERNAME	7		/* sys_getpeername(2)		*/
-#define SYS_SOCKETPAIR	8		/* sys_socketpair(2)		*/
-#define SYS_SEND	9		/* sys_send(2)			*/
-#define SYS_RECV	10		/* sys_recv(2)			*/
-#define SYS_SENDTO	11		/* sys_sendto(2)		*/
-#define SYS_RECVFROM	12		/* sys_recvfrom(2)		*/
-#define SYS_SHUTDOWN	13		/* sys_shutdown(2)		*/
-#define SYS_SETSOCKOPT	14		/* sys_setsockopt(2)		*/
-#define SYS_GETSOCKOPT	15		/* sys_getsockopt(2)		*/
-#define SYS_SENDMSG	16		/* sys_sendmsg(2)		*/
-#define SYS_RECVMSG	17		/* sys_recvmsg(2)		*/
-#define SYS_ACCEPT4	18		/* sys_accept4(2)		*/
-#define SYS_RECVMMSG	19		/* sys_recvmmsg(2)		*/
-#define SYS_SENDMMSG	20		/* sys_sendmmsg(2)		*/
-
-typedef enum {
-	SS_FREE = 0,			/* not allocated		*/
-	SS_UNCONNECTED,			/* unconnected to any socket	*/
-	SS_CONNECTING,			/* in process of connecting	*/
-	SS_CONNECTED,			/* connected to socket		*/
-	SS_DISCONNECTING		/* in process of disconnecting	*/
-} socket_state;
-
-#define __SO_ACCEPTCON	(1 << 16)	/* performed a listen		*/
-
-#endif /* _LINUX_NET_H */
diff --git a/include/uapi/linux/netfilter/ipset/ip_set.h b/include/uapi/linux/netfilter/ipset/ip_set.h
deleted file mode 100644
index c512003..0000000
--- a/include/uapi/linux/netfilter/ipset/ip_set.h
+++ /dev/null
@@ -1,310 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
- *                         Patrick Schaaf <bof@bof.de>
- *                         Martin Josefsson <gandalf@wlug.westbo.se>
- * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@netfilter.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#ifndef _IP_SET_H
-#define _IP_SET_H
-
-#include <linux/types.h>
-
-/* The protocol versions */
-#define IPSET_PROTOCOL		7
-#define IPSET_PROTOCOL_MIN	6
-
-/* The max length of strings including NUL: set and type identifiers */
-#define IPSET_MAXNAMELEN	32
-
-/* The maximum permissible comment length we will accept over netlink */
-#define IPSET_MAX_COMMENT_SIZE	255
-
-/* Message types and commands */
-enum ipset_cmd {
-	IPSET_CMD_NONE,
-	IPSET_CMD_PROTOCOL,	/* 1: Return protocol version */
-	IPSET_CMD_CREATE,	/* 2: Create a new (empty) set */
-	IPSET_CMD_DESTROY,	/* 3: Destroy a (empty) set */
-	IPSET_CMD_FLUSH,	/* 4: Remove all elements from a set */
-	IPSET_CMD_RENAME,	/* 5: Rename a set */
-	IPSET_CMD_SWAP,		/* 6: Swap two sets */
-	IPSET_CMD_LIST,		/* 7: List sets */
-	IPSET_CMD_SAVE,		/* 8: Save sets */
-	IPSET_CMD_ADD,		/* 9: Add an element to a set */
-	IPSET_CMD_DEL,		/* 10: Delete an element from a set */
-	IPSET_CMD_TEST,		/* 11: Test an element in a set */
-	IPSET_CMD_HEADER,	/* 12: Get set header data only */
-	IPSET_CMD_TYPE,		/* 13: Get set type */
-	IPSET_CMD_GET_BYNAME,	/* 14: Get set index by name */
-	IPSET_CMD_GET_BYINDEX,	/* 15: Get set name by index */
-	IPSET_MSG_MAX,		/* Netlink message commands */
-
-	/* Commands in userspace: */
-	IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 16: Enter restore mode */
-	IPSET_CMD_HELP,		/* 17: Get help */
-	IPSET_CMD_VERSION,	/* 18: Get program version */
-	IPSET_CMD_QUIT,		/* 19: Quit from interactive mode */
-
-	IPSET_CMD_MAX,
-
-	IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 20: Commit buffered commands */
-};
-
-/* Attributes at command level */
-enum {
-	IPSET_ATTR_UNSPEC,
-	IPSET_ATTR_PROTOCOL,	/* 1: Protocol version */
-	IPSET_ATTR_SETNAME,	/* 2: Name of the set */
-	IPSET_ATTR_TYPENAME,	/* 3: Typename */
-	IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* Setname at rename/swap */
-	IPSET_ATTR_REVISION,	/* 4: Settype revision */
-	IPSET_ATTR_FAMILY,	/* 5: Settype family */
-	IPSET_ATTR_FLAGS,	/* 6: Flags at command level */
-	IPSET_ATTR_DATA,	/* 7: Nested attributes */
-	IPSET_ATTR_ADT,		/* 8: Multiple data containers */
-	IPSET_ATTR_LINENO,	/* 9: Restore lineno */
-	IPSET_ATTR_PROTOCOL_MIN, /* 10: Minimal supported version number */
-	IPSET_ATTR_REVISION_MIN	= IPSET_ATTR_PROTOCOL_MIN, /* type rev min */
-	IPSET_ATTR_INDEX,	/* 11: Kernel index of set */
-	__IPSET_ATTR_CMD_MAX,
-};
-#define IPSET_ATTR_CMD_MAX	(__IPSET_ATTR_CMD_MAX - 1)
-
-/* CADT specific attributes */
-enum {
-	IPSET_ATTR_IP = IPSET_ATTR_UNSPEC + 1,
-	IPSET_ATTR_IP_FROM = IPSET_ATTR_IP,
-	IPSET_ATTR_IP_TO,	/* 2 */
-	IPSET_ATTR_CIDR,	/* 3 */
-	IPSET_ATTR_PORT,	/* 4 */
-	IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT,
-	IPSET_ATTR_PORT_TO,	/* 5 */
-	IPSET_ATTR_TIMEOUT,	/* 6 */
-	IPSET_ATTR_PROTO,	/* 7 */
-	IPSET_ATTR_CADT_FLAGS,	/* 8 */
-	IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO,	/* 9 */
-	IPSET_ATTR_MARK,	/* 10 */
-	IPSET_ATTR_MARKMASK,	/* 11 */
-	/* Reserve empty slots */
-	IPSET_ATTR_CADT_MAX = 16,
-	/* Create-only specific attributes */
-	IPSET_ATTR_GC,
-	IPSET_ATTR_HASHSIZE,
-	IPSET_ATTR_MAXELEM,
-	IPSET_ATTR_NETMASK,
-	IPSET_ATTR_PROBES,
-	IPSET_ATTR_RESIZE,
-	IPSET_ATTR_SIZE,
-	/* Kernel-only */
-	IPSET_ATTR_ELEMENTS,
-	IPSET_ATTR_REFERENCES,
-	IPSET_ATTR_MEMSIZE,
-
-	__IPSET_ATTR_CREATE_MAX,
-};
-#define IPSET_ATTR_CREATE_MAX	(__IPSET_ATTR_CREATE_MAX - 1)
-
-/* ADT specific attributes */
-enum {
-	IPSET_ATTR_ETHER = IPSET_ATTR_CADT_MAX + 1,
-	IPSET_ATTR_NAME,
-	IPSET_ATTR_NAMEREF,
-	IPSET_ATTR_IP2,
-	IPSET_ATTR_CIDR2,
-	IPSET_ATTR_IP2_TO,
-	IPSET_ATTR_IFACE,
-	IPSET_ATTR_BYTES,
-	IPSET_ATTR_PACKETS,
-	IPSET_ATTR_COMMENT,
-	IPSET_ATTR_SKBMARK,
-	IPSET_ATTR_SKBPRIO,
-	IPSET_ATTR_SKBQUEUE,
-	IPSET_ATTR_PAD,
-	__IPSET_ATTR_ADT_MAX,
-};
-#define IPSET_ATTR_ADT_MAX	(__IPSET_ATTR_ADT_MAX - 1)
-
-/* IP specific attributes */
-enum {
-	IPSET_ATTR_IPADDR_IPV4 = IPSET_ATTR_UNSPEC + 1,
-	IPSET_ATTR_IPADDR_IPV6,
-	__IPSET_ATTR_IPADDR_MAX,
-};
-#define IPSET_ATTR_IPADDR_MAX	(__IPSET_ATTR_IPADDR_MAX - 1)
-
-/* Error codes */
-enum ipset_errno {
-	IPSET_ERR_PRIVATE = 4096,
-	IPSET_ERR_PROTOCOL,
-	IPSET_ERR_FIND_TYPE,
-	IPSET_ERR_MAX_SETS,
-	IPSET_ERR_BUSY,
-	IPSET_ERR_EXIST_SETNAME2,
-	IPSET_ERR_TYPE_MISMATCH,
-	IPSET_ERR_EXIST,
-	IPSET_ERR_INVALID_CIDR,
-	IPSET_ERR_INVALID_NETMASK,
-	IPSET_ERR_INVALID_FAMILY,
-	IPSET_ERR_TIMEOUT,
-	IPSET_ERR_REFERENCED,
-	IPSET_ERR_IPADDR_IPV4,
-	IPSET_ERR_IPADDR_IPV6,
-	IPSET_ERR_COUNTER,
-	IPSET_ERR_COMMENT,
-	IPSET_ERR_INVALID_MARKMASK,
-	IPSET_ERR_SKBINFO,
-
-	/* Type specific error codes */
-	IPSET_ERR_TYPE_SPECIFIC = 4352,
-};
-
-/* Flags at command level or match/target flags, lower half of cmdattrs*/
-enum ipset_cmd_flags {
-	IPSET_FLAG_BIT_EXIST	= 0,
-	IPSET_FLAG_EXIST	= (1 << IPSET_FLAG_BIT_EXIST),
-	IPSET_FLAG_BIT_LIST_SETNAME = 1,
-	IPSET_FLAG_LIST_SETNAME	= (1 << IPSET_FLAG_BIT_LIST_SETNAME),
-	IPSET_FLAG_BIT_LIST_HEADER = 2,
-	IPSET_FLAG_LIST_HEADER	= (1 << IPSET_FLAG_BIT_LIST_HEADER),
-	IPSET_FLAG_BIT_SKIP_COUNTER_UPDATE = 3,
-	IPSET_FLAG_SKIP_COUNTER_UPDATE =
-		(1 << IPSET_FLAG_BIT_SKIP_COUNTER_UPDATE),
-	IPSET_FLAG_BIT_SKIP_SUBCOUNTER_UPDATE = 4,
-	IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE =
-		(1 << IPSET_FLAG_BIT_SKIP_SUBCOUNTER_UPDATE),
-	IPSET_FLAG_BIT_MATCH_COUNTERS = 5,
-	IPSET_FLAG_MATCH_COUNTERS = (1 << IPSET_FLAG_BIT_MATCH_COUNTERS),
-	IPSET_FLAG_BIT_RETURN_NOMATCH = 7,
-	IPSET_FLAG_RETURN_NOMATCH = (1 << IPSET_FLAG_BIT_RETURN_NOMATCH),
-	IPSET_FLAG_BIT_MAP_SKBMARK = 8,
-	IPSET_FLAG_MAP_SKBMARK = (1 << IPSET_FLAG_BIT_MAP_SKBMARK),
-	IPSET_FLAG_BIT_MAP_SKBPRIO = 9,
-	IPSET_FLAG_MAP_SKBPRIO = (1 << IPSET_FLAG_BIT_MAP_SKBPRIO),
-	IPSET_FLAG_BIT_MAP_SKBQUEUE = 10,
-	IPSET_FLAG_MAP_SKBQUEUE = (1 << IPSET_FLAG_BIT_MAP_SKBQUEUE),
-	IPSET_FLAG_CMD_MAX = 15,
-};
-
-/* Flags at CADT attribute level, upper half of cmdattrs */
-enum ipset_cadt_flags {
-	IPSET_FLAG_BIT_BEFORE	= 0,
-	IPSET_FLAG_BEFORE	= (1 << IPSET_FLAG_BIT_BEFORE),
-	IPSET_FLAG_BIT_PHYSDEV	= 1,
-	IPSET_FLAG_PHYSDEV	= (1 << IPSET_FLAG_BIT_PHYSDEV),
-	IPSET_FLAG_BIT_NOMATCH	= 2,
-	IPSET_FLAG_NOMATCH	= (1 << IPSET_FLAG_BIT_NOMATCH),
-	IPSET_FLAG_BIT_WITH_COUNTERS = 3,
-	IPSET_FLAG_WITH_COUNTERS = (1 << IPSET_FLAG_BIT_WITH_COUNTERS),
-	IPSET_FLAG_BIT_WITH_COMMENT = 4,
-	IPSET_FLAG_WITH_COMMENT = (1 << IPSET_FLAG_BIT_WITH_COMMENT),
-	IPSET_FLAG_BIT_WITH_FORCEADD = 5,
-	IPSET_FLAG_WITH_FORCEADD = (1 << IPSET_FLAG_BIT_WITH_FORCEADD),
-	IPSET_FLAG_BIT_WITH_SKBINFO = 6,
-	IPSET_FLAG_WITH_SKBINFO = (1 << IPSET_FLAG_BIT_WITH_SKBINFO),
-	IPSET_FLAG_CADT_MAX	= 15,
-};
-
-/* The flag bits which correspond to the non-extension create flags */
-enum ipset_create_flags {
-	IPSET_CREATE_FLAG_BIT_FORCEADD = 0,
-	IPSET_CREATE_FLAG_FORCEADD = (1 << IPSET_CREATE_FLAG_BIT_FORCEADD),
-	IPSET_CREATE_FLAG_BIT_MAX = 7,
-};
-
-/* Commands with settype-specific attributes */
-enum ipset_adt {
-	IPSET_ADD,
-	IPSET_DEL,
-	IPSET_TEST,
-	IPSET_ADT_MAX,
-	IPSET_CREATE = IPSET_ADT_MAX,
-	IPSET_CADT_MAX,
-};
-
-/* Sets are identified by an index in kernel space. Tweak with ip_set_id_t
- * and IPSET_INVALID_ID if you want to increase the max number of sets.
- * Also, IPSET_ATTR_INDEX must be changed.
- */
-typedef __u16 ip_set_id_t;
-
-#define IPSET_INVALID_ID		65535
-
-enum ip_set_dim {
-	IPSET_DIM_ZERO = 0,
-	IPSET_DIM_ONE,
-	IPSET_DIM_TWO,
-	IPSET_DIM_THREE,
-	/* Max dimension in elements.
-	 * If changed, new revision of iptables match/target is required.
-	 */
-	IPSET_DIM_MAX = 6,
-	/* Backward compatibility: set match revision 2 */
-	IPSET_BIT_RETURN_NOMATCH = 7,
-};
-
-/* Option flags for kernel operations */
-enum ip_set_kopt {
-	IPSET_INV_MATCH = (1 << IPSET_DIM_ZERO),
-	IPSET_DIM_ONE_SRC = (1 << IPSET_DIM_ONE),
-	IPSET_DIM_TWO_SRC = (1 << IPSET_DIM_TWO),
-	IPSET_DIM_THREE_SRC = (1 << IPSET_DIM_THREE),
-	IPSET_RETURN_NOMATCH = (1 << IPSET_BIT_RETURN_NOMATCH),
-};
-
-enum {
-	IPSET_COUNTER_NONE = 0,
-	IPSET_COUNTER_EQ,
-	IPSET_COUNTER_NE,
-	IPSET_COUNTER_LT,
-	IPSET_COUNTER_GT,
-};
-
-/* Backward compatibility for set match v3 */
-struct ip_set_counter_match0 {
-	__u8 op;
-	__u64 value;
-};
-
-struct ip_set_counter_match {
-	__aligned_u64 value;
-	__u8 op;
-};
-
-/* Interface to iptables/ip6tables */
-
-#define SO_IP_SET		83
-
-union ip_set_name_index {
-	char name[IPSET_MAXNAMELEN];
-	ip_set_id_t index;
-};
-
-#define IP_SET_OP_GET_BYNAME	0x00000006	/* Get set index by name */
-struct ip_set_req_get_set {
-	unsigned int op;
-	unsigned int version;
-	union ip_set_name_index set;
-};
-
-#define IP_SET_OP_GET_BYINDEX	0x00000007	/* Get set name by index */
-/* Uses ip_set_req_get_set */
-
-#define IP_SET_OP_GET_FNAME	0x00000008	/* Get set index and family */
-struct ip_set_req_get_set_family {
-	unsigned int op;
-	unsigned int version;
-	unsigned int family;
-	union ip_set_name_index set;
-};
-
-#define IP_SET_OP_VERSION	0x00000100	/* Ask kernel version */
-struct ip_set_req_version {
-	unsigned int op;
-	unsigned int version;
-};
-
-#endif /* _IP_SET_H */
diff --git a/include/uapi/linux/netfilter/xt_set.h b/include/uapi/linux/netfilter/xt_set.h
deleted file mode 100644
index 8c1ca66..0000000
--- a/include/uapi/linux/netfilter/xt_set.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _XT_SET_H
-#define _XT_SET_H
-
-#include <linux/types.h>
-#include <linux/netfilter/ipset/ip_set.h>
-
-/* Revision 0 interface: backward compatible with netfilter/iptables */
-
-/*
- * Option flags for kernel operations (xt_set_info_v0)
- */
-#define IPSET_SRC		0x01	/* Source match/add */
-#define IPSET_DST		0x02	/* Destination match/add */
-#define IPSET_MATCH_INV		0x04	/* Inverse matching */
-
-struct xt_set_info_v0 {
-	ip_set_id_t index;
-	union {
-		__u32 flags[IPSET_DIM_MAX + 1];
-		struct {
-			__u32 __flags[IPSET_DIM_MAX];
-			__u8 dim;
-			__u8 flags;
-		} compat;
-	} u;
-};
-
-/* match and target infos */
-struct xt_set_info_match_v0 {
-	struct xt_set_info_v0 match_set;
-};
-
-struct xt_set_info_target_v0 {
-	struct xt_set_info_v0 add_set;
-	struct xt_set_info_v0 del_set;
-};
-
-/* Revision 1  match and target */
-
-struct xt_set_info {
-	ip_set_id_t index;
-	__u8 dim;
-	__u8 flags;
-};
-
-/* match and target infos */
-struct xt_set_info_match_v1 {
-	struct xt_set_info match_set;
-};
-
-struct xt_set_info_target_v1 {
-	struct xt_set_info add_set;
-	struct xt_set_info del_set;
-};
-
-/* Revision 2 target */
-
-struct xt_set_info_target_v2 {
-	struct xt_set_info add_set;
-	struct xt_set_info del_set;
-	__u32 flags;
-	__u32 timeout;
-};
-
-/* Revision 3 match */
-
-struct xt_set_info_match_v3 {
-	struct xt_set_info match_set;
-	struct ip_set_counter_match0 packets;
-	struct ip_set_counter_match0 bytes;
-	__u32 flags;
-};
-
-/* Revision 3 target */
-
-struct xt_set_info_target_v3 {
-	struct xt_set_info add_set;
-	struct xt_set_info del_set;
-	struct xt_set_info map_set;
-	__u32 flags;
-	__u32 timeout;
-};
-
-/* Revision 4 match */
-
-struct xt_set_info_match_v4 {
-	struct xt_set_info match_set;
-	struct ip_set_counter_match packets;
-	struct ip_set_counter_match bytes;
-	__u32 flags;
-};
-
-#endif /*_XT_SET_H*/
diff --git a/include/uapi/linux/netfilter_ipv6.h b/include/uapi/linux/netfilter_ipv6.h
deleted file mode 100644
index eedf7a2..0000000
--- a/include/uapi/linux/netfilter_ipv6.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* IPv6-specific defines for netfilter. 
- * (C)1998 Rusty Russell -- This code is GPL.
- * (C)1999 David Jeffery
- *   this header was blatantly ripped from netfilter_ipv4.h 
- *   it's amazing what adding a bunch of 6s can do =8^)
- */
-#ifndef __LINUX_IP6_NETFILTER_H
-#define __LINUX_IP6_NETFILTER_H
-
-
-#include <linux/netfilter.h>
-
-/* only for userspace compatibility */
-
-#include <limits.h> /* for INT_MIN, INT_MAX */
-
-/* IP6 Hooks */
-/* After promisc drops, checksum checks. */
-#define NF_IP6_PRE_ROUTING	0
-/* If the packet is destined for this box. */
-#define NF_IP6_LOCAL_IN		1
-/* If the packet is destined for another interface. */
-#define NF_IP6_FORWARD		2
-/* Packets coming from a local process. */
-#define NF_IP6_LOCAL_OUT		3
-/* Packets about to hit the wire. */
-#define NF_IP6_POST_ROUTING	4
-#define NF_IP6_NUMHOOKS		5
-
-
-enum nf_ip6_hook_priorities {
-	NF_IP6_PRI_FIRST = INT_MIN,
-	NF_IP6_PRI_RAW_BEFORE_DEFRAG = -450,
-	NF_IP6_PRI_CONNTRACK_DEFRAG = -400,
-	NF_IP6_PRI_RAW = -300,
-	NF_IP6_PRI_SELINUX_FIRST = -225,
-	NF_IP6_PRI_CONNTRACK = -200,
-	NF_IP6_PRI_MANGLE = -150,
-	NF_IP6_PRI_NAT_DST = -100,
-	NF_IP6_PRI_FILTER = 0,
-	NF_IP6_PRI_SECURITY = 50,
-	NF_IP6_PRI_NAT_SRC = 100,
-	NF_IP6_PRI_SELINUX_LAST = 225,
-	NF_IP6_PRI_CONNTRACK_HELPER = 300,
-	NF_IP6_PRI_LAST = INT_MAX,
-};
-
-
-#endif /* __LINUX_IP6_NETFILTER_H */
diff --git a/include/uapi/linux/netfilter_ipv6/ip6_tables.h b/include/uapi/linux/netfilter_ipv6/ip6_tables.h
deleted file mode 100644
index 7ae314b..0000000
--- a/include/uapi/linux/netfilter_ipv6/ip6_tables.h
+++ /dev/null
@@ -1,270 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/*
- * 25-Jul-1998 Major changes to allow for ip chain table
- *
- * 3-Jan-2000 Named tables to allow packet selection for different uses.
- */
-
-/*
- * 	Format of an IP6 firewall descriptor
- *
- * 	src, dst, src_mask, dst_mask are always stored in network byte order.
- * 	flags are stored in host byte order (of course).
- * 	Port numbers are stored in HOST byte order.
- */
-
-#ifndef _IP6_TABLES_H
-#define _IP6_TABLES_H
-
-#include <linux/types.h>
-
-#include <linux/if.h>
-#include <linux/netfilter_ipv6.h>
-
-#include <linux/netfilter/x_tables.h>
-
-#define IP6T_FUNCTION_MAXNAMELEN XT_FUNCTION_MAXNAMELEN
-#define IP6T_TABLE_MAXNAMELEN XT_TABLE_MAXNAMELEN
-#define ip6t_match xt_match
-#define ip6t_target xt_target
-#define ip6t_table xt_table
-#define ip6t_get_revision xt_get_revision
-#define ip6t_entry_match xt_entry_match
-#define ip6t_entry_target xt_entry_target
-#define ip6t_standard_target xt_standard_target
-#define ip6t_error_target xt_error_target
-#define ip6t_counters xt_counters
-#define IP6T_CONTINUE XT_CONTINUE
-#define IP6T_RETURN XT_RETURN
-
-/* Pre-iptables-1.4.0 */
-#include <linux/netfilter/xt_tcpudp.h>
-#define ip6t_tcp xt_tcp
-#define ip6t_udp xt_udp
-#define IP6T_TCP_INV_SRCPT	XT_TCP_INV_SRCPT
-#define IP6T_TCP_INV_DSTPT	XT_TCP_INV_DSTPT
-#define IP6T_TCP_INV_FLAGS	XT_TCP_INV_FLAGS
-#define IP6T_TCP_INV_OPTION	XT_TCP_INV_OPTION
-#define IP6T_TCP_INV_MASK	XT_TCP_INV_MASK
-#define IP6T_UDP_INV_SRCPT	XT_UDP_INV_SRCPT
-#define IP6T_UDP_INV_DSTPT	XT_UDP_INV_DSTPT
-#define IP6T_UDP_INV_MASK	XT_UDP_INV_MASK
-
-#define ip6t_counters_info xt_counters_info
-#define IP6T_STANDARD_TARGET XT_STANDARD_TARGET
-#define IP6T_ERROR_TARGET XT_ERROR_TARGET
-#define IP6T_MATCH_ITERATE(e, fn, args...) \
-	XT_MATCH_ITERATE(struct ip6t_entry, e, fn, ## args)
-#define IP6T_ENTRY_ITERATE(entries, size, fn, args...) \
-	XT_ENTRY_ITERATE(struct ip6t_entry, entries, size, fn, ## args)
-
-/* Yes, Virginia, you have to zero the padding. */
-struct ip6t_ip6 {
-	/* Source and destination IP6 addr */
-	struct in6_addr src, dst;		
-	/* Mask for src and dest IP6 addr */
-	struct in6_addr smsk, dmsk;
-	char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
-	unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
-
-	/* Upper protocol number
-	 * - The allowed value is 0 (any) or protocol number of last parsable
-	 *   header, which is 50 (ESP), 59 (No Next Header), 135 (MH), or
-	 *   the non IPv6 extension headers.
-	 * - The protocol numbers of IPv6 extension headers except of ESP and
-	 *   MH do not match any packets.
-	 * - You also need to set IP6T_FLAGS_PROTO to "flags" to check protocol.
-	 */
-	__u16 proto;
-	/* TOS to match iff flags & IP6T_F_TOS */
-	__u8 tos;
-
-	/* Flags word */
-	__u8 flags;
-	/* Inverse flags */
-	__u8 invflags;
-};
-
-/* Values for "flag" field in struct ip6t_ip6 (general ip6 structure). */
-#define IP6T_F_PROTO		0x01	/* Set if rule cares about upper 
-					   protocols */
-#define IP6T_F_TOS		0x02	/* Match the TOS. */
-#define IP6T_F_GOTO		0x04	/* Set if jump is a goto */
-#define IP6T_F_MASK		0x07	/* All possible flag bits mask. */
-
-/* Values for "inv" field in struct ip6t_ip6. */
-#define IP6T_INV_VIA_IN		0x01	/* Invert the sense of IN IFACE. */
-#define IP6T_INV_VIA_OUT		0x02	/* Invert the sense of OUT IFACE */
-#define IP6T_INV_TOS		0x04	/* Invert the sense of TOS. */
-#define IP6T_INV_SRCIP		0x08	/* Invert the sense of SRC IP. */
-#define IP6T_INV_DSTIP		0x10	/* Invert the sense of DST OP. */
-#define IP6T_INV_FRAG		0x20	/* Invert the sense of FRAG. */
-#define IP6T_INV_PROTO		XT_INV_PROTO
-#define IP6T_INV_MASK		0x7F	/* All possible flag bits mask. */
-
-/* This structure defines each of the firewall rules.  Consists of 3
-   parts which are 1) general IP header stuff 2) match specific
-   stuff 3) the target to perform if the rule matches */
-struct ip6t_entry {
-	struct ip6t_ip6 ipv6;
-
-	/* Mark with fields that we care about. */
-	unsigned int nfcache;
-
-	/* Size of ipt_entry + matches */
-	__u16 target_offset;
-	/* Size of ipt_entry + matches + target */
-	__u16 next_offset;
-
-	/* Back pointer */
-	unsigned int comefrom;
-
-	/* Packet and byte counters. */
-	struct xt_counters counters;
-
-	/* The matches (if any), then the target. */
-	unsigned char elems[0];
-};
-
-/* Standard entry */
-struct ip6t_standard {
-	struct ip6t_entry entry;
-	struct xt_standard_target target;
-};
-
-struct ip6t_error {
-	struct ip6t_entry entry;
-	struct xt_error_target target;
-};
-
-#define IP6T_ENTRY_INIT(__size)						       \
-{									       \
-	.target_offset	= sizeof(struct ip6t_entry),			       \
-	.next_offset	= (__size),					       \
-}
-
-#define IP6T_STANDARD_INIT(__verdict)					       \
-{									       \
-	.entry		= IP6T_ENTRY_INIT(sizeof(struct ip6t_standard)),       \
-	.target		= XT_TARGET_INIT(XT_STANDARD_TARGET,		       \
-					 sizeof(struct xt_standard_target)),   \
-	.target.verdict	= -(__verdict) - 1,				       \
-}
-
-#define IP6T_ERROR_INIT							       \
-{									       \
-	.entry		= IP6T_ENTRY_INIT(sizeof(struct ip6t_error)),	       \
-	.target		= XT_TARGET_INIT(XT_ERROR_TARGET,		       \
-					 sizeof(struct xt_error_target)),      \
-	.target.errorname = "ERROR",					       \
-}
-
-/*
- * New IP firewall options for [gs]etsockopt at the RAW IP level.
- * Unlike BSD Linux inherits IP options so you don't have to use
- * a raw socket for this. Instead we check rights in the calls.
- *
- * ATTENTION: check linux/in6.h before adding new number here.
- */
-#define IP6T_BASE_CTL			64
-
-#define IP6T_SO_SET_REPLACE		(IP6T_BASE_CTL)
-#define IP6T_SO_SET_ADD_COUNTERS	(IP6T_BASE_CTL + 1)
-#define IP6T_SO_SET_MAX			IP6T_SO_SET_ADD_COUNTERS
-
-#define IP6T_SO_GET_INFO		(IP6T_BASE_CTL)
-#define IP6T_SO_GET_ENTRIES		(IP6T_BASE_CTL + 1)
-#define IP6T_SO_GET_REVISION_MATCH	(IP6T_BASE_CTL + 4)
-#define IP6T_SO_GET_REVISION_TARGET	(IP6T_BASE_CTL + 5)
-#define IP6T_SO_GET_MAX			IP6T_SO_GET_REVISION_TARGET
-
-/* obtain original address if REDIRECT'd connection */
-#define IP6T_SO_ORIGINAL_DST            80
-
-/* ICMP matching stuff */
-struct ip6t_icmp {
-	__u8 type;				/* type to match */
-	__u8 code[2];				/* range of code */
-	__u8 invflags;				/* Inverse flags */
-};
-
-/* Values for "inv" field for struct ipt_icmp. */
-#define IP6T_ICMP_INV	0x01	/* Invert the sense of type/code test */
-
-/* The argument to IP6T_SO_GET_INFO */
-struct ip6t_getinfo {
-	/* Which table: caller fills this in. */
-	char name[XT_TABLE_MAXNAMELEN];
-
-	/* Kernel fills these in. */
-	/* Which hook entry points are valid: bitmask */
-	unsigned int valid_hooks;
-
-	/* Hook entry points: one per netfilter hook. */
-	unsigned int hook_entry[NF_INET_NUMHOOKS];
-
-	/* Underflow points. */
-	unsigned int underflow[NF_INET_NUMHOOKS];
-
-	/* Number of entries */
-	unsigned int num_entries;
-
-	/* Size of entries. */
-	unsigned int size;
-};
-
-/* The argument to IP6T_SO_SET_REPLACE. */
-struct ip6t_replace {
-	/* Which table. */
-	char name[XT_TABLE_MAXNAMELEN];
-
-	/* Which hook entry points are valid: bitmask.  You can't
-           change this. */
-	unsigned int valid_hooks;
-
-	/* Number of entries */
-	unsigned int num_entries;
-
-	/* Total size of new entries */
-	unsigned int size;
-
-	/* Hook entry points. */
-	unsigned int hook_entry[NF_INET_NUMHOOKS];
-
-	/* Underflow points. */
-	unsigned int underflow[NF_INET_NUMHOOKS];
-
-	/* Information about old entries: */
-	/* Number of counters (must be equal to current number of entries). */
-	unsigned int num_counters;
-	/* The old entries' counters. */
-	struct xt_counters *counters;
-
-	/* The entries (hang off end: not really an array). */
-	struct ip6t_entry entries[0];
-};
-
-/* The argument to IP6T_SO_GET_ENTRIES. */
-struct ip6t_get_entries {
-	/* Which table: user fills this in. */
-	char name[XT_TABLE_MAXNAMELEN];
-
-	/* User fills this in: total entry size. */
-	unsigned int size;
-
-	/* The entries. */
-	struct ip6t_entry entrytable[0];
-};
-
-/* Helper functions */
-static __inline__ struct xt_entry_target *
-ip6t_get_target(struct ip6t_entry *e)
-{
-	return (void *)e + e->target_offset;
-}
-
-/*
- *	Main firewall chains definitions and global var's definitions.
- */
-
-#endif /* _IP6_TABLES_H */
diff --git a/include/uapi/linux/nexthop.h b/include/uapi/linux/nexthop.h
deleted file mode 100644
index b56c5b8..0000000
--- a/include/uapi/linux/nexthop.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_NEXTHOP_H
-#define _LINUX_NEXTHOP_H
-
-#include <linux/types.h>
-
-struct nhmsg {
-	unsigned char	nh_family;
-	unsigned char	nh_scope;     /* return only */
-	unsigned char	nh_protocol;  /* Routing protocol that installed nh */
-	unsigned char	resvd;
-	unsigned int	nh_flags;     /* RTNH_F flags */
-};
-
-/* entry in a nexthop group */
-struct nexthop_grp {
-	__u32	id;	  /* nexthop id - must exist */
-	__u8	weight;   /* weight of this nexthop */
-	__u8	resvd1;
-	__u16	resvd2;
-};
-
-enum {
-	NEXTHOP_GRP_TYPE_MPATH,  /* default type if not specified */
-	__NEXTHOP_GRP_TYPE_MAX,
-};
-
-#define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1)
-
-enum {
-	NHA_UNSPEC,
-	NHA_ID,		/* u32; id for nexthop. id == 0 means auto-assign */
-
-	NHA_GROUP,	/* array of nexthop_grp */
-	NHA_GROUP_TYPE,	/* u16 one of NEXTHOP_GRP_TYPE */
-	/* if NHA_GROUP attribute is added, no other attributes can be set */
-
-	NHA_BLACKHOLE,	/* flag; nexthop used to blackhole packets */
-	/* if NHA_BLACKHOLE is added, OIF, GATEWAY, ENCAP can not be set */
-
-	NHA_OIF,	/* u32; nexthop device */
-	NHA_GATEWAY,	/* be32 (IPv4) or in6_addr (IPv6) gw address */
-	NHA_ENCAP_TYPE, /* u16; lwt encap type */
-	NHA_ENCAP,	/* lwt encap data */
-
-	/* NHA_OIF can be appended to dump request to return only
-	 * nexthops using given device
-	 */
-	NHA_GROUPS,	/* flag; only return nexthop groups in dump */
-	NHA_MASTER,	/* u32;  only return nexthops with given master dev */
-
-	__NHA_MAX,
-};
-
-#define NHA_MAX	(__NHA_MAX - 1)
-#endif
diff --git a/include/uapi/linux/param.h b/include/uapi/linux/param.h
deleted file mode 100644
index 94e0c57..0000000
--- a/include/uapi/linux/param.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_PARAM_H
-#define _LINUX_PARAM_H
-
-#include <asm/param.h>
-
-#endif
diff --git a/include/uapi/linux/pfkeyv2.h b/include/uapi/linux/pfkeyv2.h
deleted file mode 100644
index d65b117..0000000
--- a/include/uapi/linux/pfkeyv2.h
+++ /dev/null
@@ -1,384 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* PF_KEY user interface, this is defined by rfc2367 so
- * do not make arbitrary modifications or else this header
- * file will not be compliant.
- */
-
-#ifndef _LINUX_PFKEY2_H
-#define _LINUX_PFKEY2_H
-
-#include <linux/types.h>
-
-#define PF_KEY_V2		2
-#define PFKEYV2_REVISION	199806L
-
-struct sadb_msg {
-	__u8		sadb_msg_version;
-	__u8		sadb_msg_type;
-	__u8		sadb_msg_errno;
-	__u8		sadb_msg_satype;
-	__u16	sadb_msg_len;
-	__u16	sadb_msg_reserved;
-	__u32	sadb_msg_seq;
-	__u32	sadb_msg_pid;
-} __attribute__((packed));
-/* sizeof(struct sadb_msg) == 16 */
-
-struct sadb_ext {
-	__u16	sadb_ext_len;
-	__u16	sadb_ext_type;
-} __attribute__((packed));
-/* sizeof(struct sadb_ext) == 4 */
-
-struct sadb_sa {
-	__u16	sadb_sa_len;
-	__u16	sadb_sa_exttype;
-	__be32		sadb_sa_spi;
-	__u8		sadb_sa_replay;
-	__u8		sadb_sa_state;
-	__u8		sadb_sa_auth;
-	__u8		sadb_sa_encrypt;
-	__u32	sadb_sa_flags;
-} __attribute__((packed));
-/* sizeof(struct sadb_sa) == 16 */
-
-struct sadb_lifetime {
-	__u16	sadb_lifetime_len;
-	__u16	sadb_lifetime_exttype;
-	__u32	sadb_lifetime_allocations;
-	__u64	sadb_lifetime_bytes;
-	__u64	sadb_lifetime_addtime;
-	__u64	sadb_lifetime_usetime;
-} __attribute__((packed));
-/* sizeof(struct sadb_lifetime) == 32 */
-
-struct sadb_address {
-	__u16	sadb_address_len;
-	__u16	sadb_address_exttype;
-	__u8		sadb_address_proto;
-	__u8		sadb_address_prefixlen;
-	__u16	sadb_address_reserved;
-} __attribute__((packed));
-/* sizeof(struct sadb_address) == 8 */
-
-struct sadb_key {
-	__u16	sadb_key_len;
-	__u16	sadb_key_exttype;
-	__u16	sadb_key_bits;
-	__u16	sadb_key_reserved;
-} __attribute__((packed));
-/* sizeof(struct sadb_key) == 8 */
-
-struct sadb_ident {
-	__u16	sadb_ident_len;
-	__u16	sadb_ident_exttype;
-	__u16	sadb_ident_type;
-	__u16	sadb_ident_reserved;
-	__u64	sadb_ident_id;
-} __attribute__((packed));
-/* sizeof(struct sadb_ident) == 16 */
-
-struct sadb_sens {
-	__u16	sadb_sens_len;
-	__u16	sadb_sens_exttype;
-	__u32	sadb_sens_dpd;
-	__u8		sadb_sens_sens_level;
-	__u8		sadb_sens_sens_len;
-	__u8		sadb_sens_integ_level;
-	__u8		sadb_sens_integ_len;
-	__u32	sadb_sens_reserved;
-} __attribute__((packed));
-/* sizeof(struct sadb_sens) == 16 */
-
-/* followed by:
-	__u64	sadb_sens_bitmap[sens_len];
-	__u64	sadb_integ_bitmap[integ_len];  */
-
-struct sadb_prop {
-	__u16	sadb_prop_len;
-	__u16	sadb_prop_exttype;
-	__u8		sadb_prop_replay;
-	__u8		sadb_prop_reserved[3];
-} __attribute__((packed));
-/* sizeof(struct sadb_prop) == 8 */
-
-/* followed by:
-	struct sadb_comb sadb_combs[(sadb_prop_len +
-		sizeof(__u64) - sizeof(struct sadb_prop)) /
-		sizeof(struct sadb_comb)]; */
-
-struct sadb_comb {
-	__u8		sadb_comb_auth;
-	__u8		sadb_comb_encrypt;
-	__u16	sadb_comb_flags;
-	__u16	sadb_comb_auth_minbits;
-	__u16	sadb_comb_auth_maxbits;
-	__u16	sadb_comb_encrypt_minbits;
-	__u16	sadb_comb_encrypt_maxbits;
-	__u32	sadb_comb_reserved;
-	__u32	sadb_comb_soft_allocations;
-	__u32	sadb_comb_hard_allocations;
-	__u64	sadb_comb_soft_bytes;
-	__u64	sadb_comb_hard_bytes;
-	__u64	sadb_comb_soft_addtime;
-	__u64	sadb_comb_hard_addtime;
-	__u64	sadb_comb_soft_usetime;
-	__u64	sadb_comb_hard_usetime;
-} __attribute__((packed));
-/* sizeof(struct sadb_comb) == 72 */
-
-struct sadb_supported {
-	__u16	sadb_supported_len;
-	__u16	sadb_supported_exttype;
-	__u32	sadb_supported_reserved;
-} __attribute__((packed));
-/* sizeof(struct sadb_supported) == 8 */
-
-/* followed by:
-	struct sadb_alg sadb_algs[(sadb_supported_len +
-		sizeof(__u64) - sizeof(struct sadb_supported)) /
-		sizeof(struct sadb_alg)]; */
-
-struct sadb_alg {
-	__u8		sadb_alg_id;
-	__u8		sadb_alg_ivlen;
-	__u16	sadb_alg_minbits;
-	__u16	sadb_alg_maxbits;
-	__u16	sadb_alg_reserved;
-} __attribute__((packed));
-/* sizeof(struct sadb_alg) == 8 */
-
-struct sadb_spirange {
-	__u16	sadb_spirange_len;
-	__u16	sadb_spirange_exttype;
-	__u32	sadb_spirange_min;
-	__u32	sadb_spirange_max;
-	__u32	sadb_spirange_reserved;
-} __attribute__((packed));
-/* sizeof(struct sadb_spirange) == 16 */
-
-struct sadb_x_kmprivate {
-	__u16	sadb_x_kmprivate_len;
-	__u16	sadb_x_kmprivate_exttype;
-	__u32	sadb_x_kmprivate_reserved;
-} __attribute__((packed));
-/* sizeof(struct sadb_x_kmprivate) == 8 */
-
-struct sadb_x_sa2 {
-	__u16	sadb_x_sa2_len;
-	__u16	sadb_x_sa2_exttype;
-	__u8		sadb_x_sa2_mode;
-	__u8		sadb_x_sa2_reserved1;
-	__u16	sadb_x_sa2_reserved2;
-	__u32	sadb_x_sa2_sequence;
-	__u32	sadb_x_sa2_reqid;
-} __attribute__((packed));
-/* sizeof(struct sadb_x_sa2) == 16 */
-
-struct sadb_x_policy {
-	__u16	sadb_x_policy_len;
-	__u16	sadb_x_policy_exttype;
-	__u16	sadb_x_policy_type;
-	__u8		sadb_x_policy_dir;
-	__u8		sadb_x_policy_reserved;
-	__u32	sadb_x_policy_id;
-	__u32	sadb_x_policy_priority;
-} __attribute__((packed));
-/* sizeof(struct sadb_x_policy) == 16 */
-
-struct sadb_x_ipsecrequest {
-	__u16	sadb_x_ipsecrequest_len;
-	__u16	sadb_x_ipsecrequest_proto;
-	__u8		sadb_x_ipsecrequest_mode;
-	__u8		sadb_x_ipsecrequest_level;
-	__u16	sadb_x_ipsecrequest_reserved1;
-	__u32	sadb_x_ipsecrequest_reqid;
-	__u32	sadb_x_ipsecrequest_reserved2;
-} __attribute__((packed));
-/* sizeof(struct sadb_x_ipsecrequest) == 16 */
-
-/* This defines the TYPE of Nat Traversal in use.  Currently only one
- * type of NAT-T is supported, draft-ietf-ipsec-udp-encaps-06
- */
-struct sadb_x_nat_t_type {
-	__u16	sadb_x_nat_t_type_len;
-	__u16	sadb_x_nat_t_type_exttype;
-	__u8		sadb_x_nat_t_type_type;
-	__u8		sadb_x_nat_t_type_reserved[3];
-} __attribute__((packed));
-/* sizeof(struct sadb_x_nat_t_type) == 8 */
-
-/* Pass a NAT Traversal port (Source or Dest port) */
-struct sadb_x_nat_t_port {
-	__u16	sadb_x_nat_t_port_len;
-	__u16	sadb_x_nat_t_port_exttype;
-	__be16		sadb_x_nat_t_port_port;
-	__u16	sadb_x_nat_t_port_reserved;
-} __attribute__((packed));
-/* sizeof(struct sadb_x_nat_t_port) == 8 */
-
-/* Generic LSM security context */
-struct sadb_x_sec_ctx {
-	__u16	sadb_x_sec_len;
-	__u16	sadb_x_sec_exttype;
-	__u8		sadb_x_ctx_alg;  /* LSMs: e.g., selinux == 1 */
-	__u8		sadb_x_ctx_doi;
-	__u16	sadb_x_ctx_len;
-} __attribute__((packed));
-/* sizeof(struct sadb_sec_ctx) = 8 */
-
-/* Used by MIGRATE to pass addresses IKE will use to perform
- * negotiation with the peer */
-struct sadb_x_kmaddress {
-	__u16	sadb_x_kmaddress_len;
-	__u16	sadb_x_kmaddress_exttype;
-	__u32	sadb_x_kmaddress_reserved;
-} __attribute__((packed));
-/* sizeof(struct sadb_x_kmaddress) == 8 */
-
-/* To specify the SA dump filter */
-struct sadb_x_filter {
-	__u16	sadb_x_filter_len;
-	__u16	sadb_x_filter_exttype;
-	__u32	sadb_x_filter_saddr[4];
-	__u32	sadb_x_filter_daddr[4];
-	__u16	sadb_x_filter_family;
-	__u8	sadb_x_filter_splen;
-	__u8	sadb_x_filter_dplen;
-} __attribute__((packed));
-/* sizeof(struct sadb_x_filter) == 40 */
-
-/* Message types */
-#define SADB_RESERVED		0
-#define SADB_GETSPI		1
-#define SADB_UPDATE		2
-#define SADB_ADD		3
-#define SADB_DELETE		4
-#define SADB_GET		5
-#define SADB_ACQUIRE		6
-#define SADB_REGISTER		7
-#define SADB_EXPIRE		8
-#define SADB_FLUSH		9
-#define SADB_DUMP		10
-#define SADB_X_PROMISC		11
-#define SADB_X_PCHANGE		12
-#define SADB_X_SPDUPDATE	13
-#define SADB_X_SPDADD		14
-#define SADB_X_SPDDELETE	15
-#define SADB_X_SPDGET		16
-#define SADB_X_SPDACQUIRE	17
-#define SADB_X_SPDDUMP		18
-#define SADB_X_SPDFLUSH		19
-#define SADB_X_SPDSETIDX	20
-#define SADB_X_SPDEXPIRE	21
-#define SADB_X_SPDDELETE2	22
-#define SADB_X_NAT_T_NEW_MAPPING	23
-#define SADB_X_MIGRATE		24
-#define SADB_MAX		24
-
-/* Security Association flags */
-#define SADB_SAFLAGS_PFS	1
-#define SADB_SAFLAGS_NOPMTUDISC	0x20000000
-#define SADB_SAFLAGS_DECAP_DSCP	0x40000000
-#define SADB_SAFLAGS_NOECN	0x80000000
-
-/* Security Association states */
-#define SADB_SASTATE_LARVAL	0
-#define SADB_SASTATE_MATURE	1
-#define SADB_SASTATE_DYING	2
-#define SADB_SASTATE_DEAD	3
-#define SADB_SASTATE_MAX	3
-
-/* Security Association types */
-#define SADB_SATYPE_UNSPEC	0
-#define SADB_SATYPE_AH		2
-#define SADB_SATYPE_ESP		3
-#define SADB_SATYPE_RSVP	5
-#define SADB_SATYPE_OSPFV2	6
-#define SADB_SATYPE_RIPV2	7
-#define SADB_SATYPE_MIP		8
-#define SADB_X_SATYPE_IPCOMP	9
-#define SADB_SATYPE_MAX		9
-
-/* Authentication algorithms */
-#define SADB_AALG_NONE			0
-#define SADB_AALG_MD5HMAC		2
-#define SADB_AALG_SHA1HMAC		3
-#define SADB_X_AALG_SHA2_256HMAC	5
-#define SADB_X_AALG_SHA2_384HMAC	6
-#define SADB_X_AALG_SHA2_512HMAC	7
-#define SADB_X_AALG_RIPEMD160HMAC	8
-#define SADB_X_AALG_AES_XCBC_MAC	9
-#define SADB_X_AALG_NULL		251	/* kame */
-#define SADB_AALG_MAX			251
-
-/* Encryption algorithms */
-#define SADB_EALG_NONE			0
-#define SADB_EALG_DESCBC		2
-#define SADB_EALG_3DESCBC		3
-#define SADB_X_EALG_CASTCBC		6
-#define SADB_X_EALG_BLOWFISHCBC		7
-#define SADB_EALG_NULL			11
-#define SADB_X_EALG_AESCBC		12
-#define SADB_X_EALG_AESCTR		13
-#define SADB_X_EALG_AES_CCM_ICV8	14
-#define SADB_X_EALG_AES_CCM_ICV12	15
-#define SADB_X_EALG_AES_CCM_ICV16	16
-#define SADB_X_EALG_AES_GCM_ICV8	18
-#define SADB_X_EALG_AES_GCM_ICV12	19
-#define SADB_X_EALG_AES_GCM_ICV16	20
-#define SADB_X_EALG_CAMELLIACBC		22
-#define SADB_X_EALG_NULL_AES_GMAC	23
-#define SADB_EALG_MAX                   253 /* last EALG */
-/* private allocations should use 249-255 (RFC2407) */
-#define SADB_X_EALG_SERPENTCBC  252     /* draft-ietf-ipsec-ciph-aes-cbc-00 */
-#define SADB_X_EALG_TWOFISHCBC  253     /* draft-ietf-ipsec-ciph-aes-cbc-00 */
-
-/* Compression algorithms */
-#define SADB_X_CALG_NONE		0
-#define SADB_X_CALG_OUI			1
-#define SADB_X_CALG_DEFLATE		2
-#define SADB_X_CALG_LZS			3
-#define SADB_X_CALG_LZJH		4
-#define SADB_X_CALG_MAX			4
-
-/* Extension Header values */
-#define SADB_EXT_RESERVED		0
-#define SADB_EXT_SA			1
-#define SADB_EXT_LIFETIME_CURRENT	2
-#define SADB_EXT_LIFETIME_HARD		3
-#define SADB_EXT_LIFETIME_SOFT		4
-#define SADB_EXT_ADDRESS_SRC		5
-#define SADB_EXT_ADDRESS_DST		6
-#define SADB_EXT_ADDRESS_PROXY		7
-#define SADB_EXT_KEY_AUTH		8
-#define SADB_EXT_KEY_ENCRYPT		9
-#define SADB_EXT_IDENTITY_SRC		10
-#define SADB_EXT_IDENTITY_DST		11
-#define SADB_EXT_SENSITIVITY		12
-#define SADB_EXT_PROPOSAL		13
-#define SADB_EXT_SUPPORTED_AUTH		14
-#define SADB_EXT_SUPPORTED_ENCRYPT	15
-#define SADB_EXT_SPIRANGE		16
-#define SADB_X_EXT_KMPRIVATE		17
-#define SADB_X_EXT_POLICY		18
-#define SADB_X_EXT_SA2			19
-/* The next four entries are for setting up NAT Traversal */
-#define SADB_X_EXT_NAT_T_TYPE		20
-#define SADB_X_EXT_NAT_T_SPORT		21
-#define SADB_X_EXT_NAT_T_DPORT		22
-#define SADB_X_EXT_NAT_T_OA		23
-#define SADB_X_EXT_SEC_CTX		24
-/* Used with MIGRATE to pass @ to IKE for negotiation */
-#define SADB_X_EXT_KMADDRESS		25
-#define SADB_X_EXT_FILTER		26
-#define SADB_EXT_MAX			26
-
-/* Identity Extension values */
-#define SADB_IDENTTYPE_RESERVED	0
-#define SADB_IDENTTYPE_PREFIX	1
-#define SADB_IDENTTYPE_FQDN	2
-#define SADB_IDENTTYPE_USERFQDN	3
-#define SADB_IDENTTYPE_MAX	3
-
-#endif /* !(_LINUX_PFKEY2_H) */
diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h
deleted file mode 100644
index a6aa466..0000000
--- a/include/uapi/linux/pkt_cls.h
+++ /dev/null
@@ -1,690 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef __LINUX_PKT_CLS_H
-#define __LINUX_PKT_CLS_H
-
-#include <linux/types.h>
-#include <linux/pkt_sched.h>
-
-#define TC_COOKIE_MAX_SIZE 16
-
-/* Action attributes */
-enum {
-	TCA_ACT_UNSPEC,
-	TCA_ACT_KIND,
-	TCA_ACT_OPTIONS,
-	TCA_ACT_INDEX,
-	TCA_ACT_STATS,
-	TCA_ACT_PAD,
-	TCA_ACT_COOKIE,
-	__TCA_ACT_MAX
-};
-
-#define TCA_ACT_MAX __TCA_ACT_MAX
-#define TCA_OLD_COMPAT (TCA_ACT_MAX+1)
-#define TCA_ACT_MAX_PRIO 32
-#define TCA_ACT_BIND	1
-#define TCA_ACT_NOBIND	0
-#define TCA_ACT_UNBIND	1
-#define TCA_ACT_NOUNBIND	0
-#define TCA_ACT_REPLACE		1
-#define TCA_ACT_NOREPLACE	0
-
-#define TC_ACT_UNSPEC	(-1)
-#define TC_ACT_OK		0
-#define TC_ACT_RECLASSIFY	1
-#define TC_ACT_SHOT		2
-#define TC_ACT_PIPE		3
-#define TC_ACT_STOLEN		4
-#define TC_ACT_QUEUED		5
-#define TC_ACT_REPEAT		6
-#define TC_ACT_REDIRECT		7
-#define TC_ACT_TRAP		8 /* For hw path, this means "trap to cpu"
-				   * and don't further process the frame
-				   * in hardware. For sw path, this is
-				   * equivalent of TC_ACT_STOLEN - drop
-				   * the skb and act like everything
-				   * is alright.
-				   */
-#define TC_ACT_VALUE_MAX	TC_ACT_TRAP
-
-/* There is a special kind of actions called "extended actions",
- * which need a value parameter. These have a local opcode located in
- * the highest nibble, starting from 1. The rest of the bits
- * are used to carry the value. These two parts together make
- * a combined opcode.
- */
-#define __TC_ACT_EXT_SHIFT 28
-#define __TC_ACT_EXT(local) ((local) << __TC_ACT_EXT_SHIFT)
-#define TC_ACT_EXT_VAL_MASK ((1 << __TC_ACT_EXT_SHIFT) - 1)
-#define TC_ACT_EXT_OPCODE(combined) ((combined) & (~TC_ACT_EXT_VAL_MASK))
-#define TC_ACT_EXT_CMP(combined, opcode) (TC_ACT_EXT_OPCODE(combined) == opcode)
-
-#define TC_ACT_JUMP __TC_ACT_EXT(1)
-#define TC_ACT_GOTO_CHAIN __TC_ACT_EXT(2)
-#define TC_ACT_EXT_OPCODE_MAX	TC_ACT_GOTO_CHAIN
-
-/* These macros are put here for binary compatibility with userspace apps that
- * make use of them. For kernel code and new userspace apps, use the TCA_ID_*
- * versions.
- */
-#define TCA_ACT_GACT 5
-#define TCA_ACT_IPT 6
-#define TCA_ACT_PEDIT 7
-#define TCA_ACT_MIRRED 8
-#define TCA_ACT_NAT 9
-#define TCA_ACT_XT 10
-#define TCA_ACT_SKBEDIT 11
-#define TCA_ACT_VLAN 12
-#define TCA_ACT_BPF 13
-#define TCA_ACT_CONNMARK 14
-#define TCA_ACT_SKBMOD 15
-#define TCA_ACT_CSUM 16
-#define TCA_ACT_TUNNEL_KEY 17
-#define TCA_ACT_SIMP 22
-#define TCA_ACT_IFE 25
-#define TCA_ACT_SAMPLE 26
-
-/* Action type identifiers*/
-enum tca_id {
-	TCA_ID_UNSPEC = 0,
-	TCA_ID_POLICE = 1,
-	TCA_ID_GACT = TCA_ACT_GACT,
-	TCA_ID_IPT = TCA_ACT_IPT,
-	TCA_ID_PEDIT = TCA_ACT_PEDIT,
-	TCA_ID_MIRRED = TCA_ACT_MIRRED,
-	TCA_ID_NAT = TCA_ACT_NAT,
-	TCA_ID_XT = TCA_ACT_XT,
-	TCA_ID_SKBEDIT = TCA_ACT_SKBEDIT,
-	TCA_ID_VLAN = TCA_ACT_VLAN,
-	TCA_ID_BPF = TCA_ACT_BPF,
-	TCA_ID_CONNMARK = TCA_ACT_CONNMARK,
-	TCA_ID_SKBMOD = TCA_ACT_SKBMOD,
-	TCA_ID_CSUM = TCA_ACT_CSUM,
-	TCA_ID_TUNNEL_KEY = TCA_ACT_TUNNEL_KEY,
-	TCA_ID_SIMP = TCA_ACT_SIMP,
-	TCA_ID_IFE = TCA_ACT_IFE,
-	TCA_ID_SAMPLE = TCA_ACT_SAMPLE,
-	TCA_ID_CTINFO,
-	TCA_ID_MPLS,
-	TCA_ID_CT,
-	/* other actions go here */
-	__TCA_ID_MAX = 255
-};
-
-#define TCA_ID_MAX __TCA_ID_MAX
-
-struct tc_police {
-	__u32			index;
-	int			action;
-#define TC_POLICE_UNSPEC	TC_ACT_UNSPEC
-#define TC_POLICE_OK		TC_ACT_OK
-#define TC_POLICE_RECLASSIFY	TC_ACT_RECLASSIFY
-#define TC_POLICE_SHOT		TC_ACT_SHOT
-#define TC_POLICE_PIPE		TC_ACT_PIPE
-
-	__u32			limit;
-	__u32			burst;
-	__u32			mtu;
-	struct tc_ratespec	rate;
-	struct tc_ratespec	peakrate;
-	int			refcnt;
-	int			bindcnt;
-	__u32			capab;
-};
-
-struct tcf_t {
-	__u64   install;
-	__u64   lastuse;
-	__u64   expires;
-	__u64   firstuse;
-};
-
-struct tc_cnt {
-	int                   refcnt;
-	int                   bindcnt;
-};
-
-#define tc_gen \
-	__u32                 index; \
-	__u32                 capab; \
-	int                   action; \
-	int                   refcnt; \
-	int                   bindcnt
-
-enum {
-	TCA_POLICE_UNSPEC,
-	TCA_POLICE_TBF,
-	TCA_POLICE_RATE,
-	TCA_POLICE_PEAKRATE,
-	TCA_POLICE_AVRATE,
-	TCA_POLICE_RESULT,
-	TCA_POLICE_TM,
-	TCA_POLICE_PAD,
-	TCA_POLICE_RATE64,
-	TCA_POLICE_PEAKRATE64,
-	__TCA_POLICE_MAX
-#define TCA_POLICE_RESULT TCA_POLICE_RESULT
-};
-
-#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1)
-
-/* tca flags definitions */
-#define TCA_CLS_FLAGS_SKIP_HW	(1 << 0) /* don't offload filter to HW */
-#define TCA_CLS_FLAGS_SKIP_SW	(1 << 1) /* don't use filter in SW */
-#define TCA_CLS_FLAGS_IN_HW	(1 << 2) /* filter is offloaded to HW */
-#define TCA_CLS_FLAGS_NOT_IN_HW (1 << 3) /* filter isn't offloaded to HW */
-#define TCA_CLS_FLAGS_VERBOSE	(1 << 4) /* verbose logging */
-
-/* U32 filters */
-
-#define TC_U32_HTID(h) ((h)&0xFFF00000)
-#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20)
-#define TC_U32_HASH(h) (((h)>>12)&0xFF)
-#define TC_U32_NODE(h) ((h)&0xFFF)
-#define TC_U32_KEY(h) ((h)&0xFFFFF)
-#define TC_U32_UNSPEC	0
-#define TC_U32_ROOT	(0xFFF00000)
-
-enum {
-	TCA_U32_UNSPEC,
-	TCA_U32_CLASSID,
-	TCA_U32_HASH,
-	TCA_U32_LINK,
-	TCA_U32_DIVISOR,
-	TCA_U32_SEL,
-	TCA_U32_POLICE,
-	TCA_U32_ACT,
-	TCA_U32_INDEV,
-	TCA_U32_PCNT,
-	TCA_U32_MARK,
-	TCA_U32_FLAGS,
-	TCA_U32_PAD,
-	__TCA_U32_MAX
-};
-
-#define TCA_U32_MAX (__TCA_U32_MAX - 1)
-
-struct tc_u32_key {
-	__be32		mask;
-	__be32		val;
-	int		off;
-	int		offmask;
-};
-
-struct tc_u32_sel {
-	unsigned char		flags;
-	unsigned char		offshift;
-	unsigned char		nkeys;
-
-	__be16			offmask;
-	__u16			off;
-	short			offoff;
-
-	short			hoff;
-	__be32			hmask;
-	struct tc_u32_key	keys[0];
-};
-
-struct tc_u32_mark {
-	__u32		val;
-	__u32		mask;
-	__u32		success;
-};
-
-struct tc_u32_pcnt {
-	__u64 rcnt;
-	__u64 rhit;
-	__u64 kcnts[0];
-};
-
-/* Flags */
-
-#define TC_U32_TERMINAL		1
-#define TC_U32_OFFSET		2
-#define TC_U32_VAROFFSET	4
-#define TC_U32_EAT		8
-
-#define TC_U32_MAXDEPTH 8
-
-
-/* RSVP filter */
-
-enum {
-	TCA_RSVP_UNSPEC,
-	TCA_RSVP_CLASSID,
-	TCA_RSVP_DST,
-	TCA_RSVP_SRC,
-	TCA_RSVP_PINFO,
-	TCA_RSVP_POLICE,
-	TCA_RSVP_ACT,
-	__TCA_RSVP_MAX
-};
-
-#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 )
-
-struct tc_rsvp_gpi {
-	__u32	key;
-	__u32	mask;
-	int	offset;
-};
-
-struct tc_rsvp_pinfo {
-	struct tc_rsvp_gpi dpi;
-	struct tc_rsvp_gpi spi;
-	__u8	protocol;
-	__u8	tunnelid;
-	__u8	tunnelhdr;
-	__u8	pad;
-};
-
-/* ROUTE filter */
-
-enum {
-	TCA_ROUTE4_UNSPEC,
-	TCA_ROUTE4_CLASSID,
-	TCA_ROUTE4_TO,
-	TCA_ROUTE4_FROM,
-	TCA_ROUTE4_IIF,
-	TCA_ROUTE4_POLICE,
-	TCA_ROUTE4_ACT,
-	__TCA_ROUTE4_MAX
-};
-
-#define TCA_ROUTE4_MAX (__TCA_ROUTE4_MAX - 1)
-
-
-/* FW filter */
-
-enum {
-	TCA_FW_UNSPEC,
-	TCA_FW_CLASSID,
-	TCA_FW_POLICE,
-	TCA_FW_INDEV,
-	TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */
-	TCA_FW_MASK,
-	__TCA_FW_MAX
-};
-
-#define TCA_FW_MAX (__TCA_FW_MAX - 1)
-
-/* TC index filter */
-
-enum {
-	TCA_TCINDEX_UNSPEC,
-	TCA_TCINDEX_HASH,
-	TCA_TCINDEX_MASK,
-	TCA_TCINDEX_SHIFT,
-	TCA_TCINDEX_FALL_THROUGH,
-	TCA_TCINDEX_CLASSID,
-	TCA_TCINDEX_POLICE,
-	TCA_TCINDEX_ACT,
-	__TCA_TCINDEX_MAX
-};
-
-#define TCA_TCINDEX_MAX     (__TCA_TCINDEX_MAX - 1)
-
-/* Flow filter */
-
-enum {
-	FLOW_KEY_SRC,
-	FLOW_KEY_DST,
-	FLOW_KEY_PROTO,
-	FLOW_KEY_PROTO_SRC,
-	FLOW_KEY_PROTO_DST,
-	FLOW_KEY_IIF,
-	FLOW_KEY_PRIORITY,
-	FLOW_KEY_MARK,
-	FLOW_KEY_NFCT,
-	FLOW_KEY_NFCT_SRC,
-	FLOW_KEY_NFCT_DST,
-	FLOW_KEY_NFCT_PROTO_SRC,
-	FLOW_KEY_NFCT_PROTO_DST,
-	FLOW_KEY_RTCLASSID,
-	FLOW_KEY_SKUID,
-	FLOW_KEY_SKGID,
-	FLOW_KEY_VLAN_TAG,
-	FLOW_KEY_RXHASH,
-	__FLOW_KEY_MAX,
-};
-
-#define FLOW_KEY_MAX	(__FLOW_KEY_MAX - 1)
-
-enum {
-	FLOW_MODE_MAP,
-	FLOW_MODE_HASH,
-};
-
-enum {
-	TCA_FLOW_UNSPEC,
-	TCA_FLOW_KEYS,
-	TCA_FLOW_MODE,
-	TCA_FLOW_BASECLASS,
-	TCA_FLOW_RSHIFT,
-	TCA_FLOW_ADDEND,
-	TCA_FLOW_MASK,
-	TCA_FLOW_XOR,
-	TCA_FLOW_DIVISOR,
-	TCA_FLOW_ACT,
-	TCA_FLOW_POLICE,
-	TCA_FLOW_EMATCHES,
-	TCA_FLOW_PERTURB,
-	__TCA_FLOW_MAX
-};
-
-#define TCA_FLOW_MAX	(__TCA_FLOW_MAX - 1)
-
-/* Basic filter */
-
-struct tc_basic_pcnt {
-	__u64 rcnt;
-	__u64 rhit;
-};
-
-enum {
-	TCA_BASIC_UNSPEC,
-	TCA_BASIC_CLASSID,
-	TCA_BASIC_EMATCHES,
-	TCA_BASIC_ACT,
-	TCA_BASIC_POLICE,
-	TCA_BASIC_PCNT,
-	TCA_BASIC_PAD,
-	__TCA_BASIC_MAX
-};
-
-#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1)
-
-
-/* Cgroup classifier */
-
-enum {
-	TCA_CGROUP_UNSPEC,
-	TCA_CGROUP_ACT,
-	TCA_CGROUP_POLICE,
-	TCA_CGROUP_EMATCHES,
-	__TCA_CGROUP_MAX,
-};
-
-#define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1)
-
-/* BPF classifier */
-
-#define TCA_BPF_FLAG_ACT_DIRECT		(1 << 0)
-
-enum {
-	TCA_BPF_UNSPEC,
-	TCA_BPF_ACT,
-	TCA_BPF_POLICE,
-	TCA_BPF_CLASSID,
-	TCA_BPF_OPS_LEN,
-	TCA_BPF_OPS,
-	TCA_BPF_FD,
-	TCA_BPF_NAME,
-	TCA_BPF_FLAGS,
-	TCA_BPF_FLAGS_GEN,
-	TCA_BPF_TAG,
-	TCA_BPF_ID,
-	__TCA_BPF_MAX,
-};
-
-#define TCA_BPF_MAX (__TCA_BPF_MAX - 1)
-
-/* Flower classifier */
-
-enum {
-	TCA_FLOWER_UNSPEC,
-	TCA_FLOWER_CLASSID,
-	TCA_FLOWER_INDEV,
-	TCA_FLOWER_ACT,
-	TCA_FLOWER_KEY_ETH_DST,		/* ETH_ALEN */
-	TCA_FLOWER_KEY_ETH_DST_MASK,	/* ETH_ALEN */
-	TCA_FLOWER_KEY_ETH_SRC,		/* ETH_ALEN */
-	TCA_FLOWER_KEY_ETH_SRC_MASK,	/* ETH_ALEN */
-	TCA_FLOWER_KEY_ETH_TYPE,	/* be16 */
-	TCA_FLOWER_KEY_IP_PROTO,	/* u8 */
-	TCA_FLOWER_KEY_IPV4_SRC,	/* be32 */
-	TCA_FLOWER_KEY_IPV4_SRC_MASK,	/* be32 */
-	TCA_FLOWER_KEY_IPV4_DST,	/* be32 */
-	TCA_FLOWER_KEY_IPV4_DST_MASK,	/* be32 */
-	TCA_FLOWER_KEY_IPV6_SRC,	/* struct in6_addr */
-	TCA_FLOWER_KEY_IPV6_SRC_MASK,	/* struct in6_addr */
-	TCA_FLOWER_KEY_IPV6_DST,	/* struct in6_addr */
-	TCA_FLOWER_KEY_IPV6_DST_MASK,	/* struct in6_addr */
-	TCA_FLOWER_KEY_TCP_SRC,		/* be16 */
-	TCA_FLOWER_KEY_TCP_DST,		/* be16 */
-	TCA_FLOWER_KEY_UDP_SRC,		/* be16 */
-	TCA_FLOWER_KEY_UDP_DST,		/* be16 */
-
-	TCA_FLOWER_FLAGS,
-	TCA_FLOWER_KEY_VLAN_ID,		/* be16 */
-	TCA_FLOWER_KEY_VLAN_PRIO,	/* u8   */
-	TCA_FLOWER_KEY_VLAN_ETH_TYPE,	/* be16 */
-
-	TCA_FLOWER_KEY_ENC_KEY_ID,	/* be32 */
-	TCA_FLOWER_KEY_ENC_IPV4_SRC,	/* be32 */
-	TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK,/* be32 */
-	TCA_FLOWER_KEY_ENC_IPV4_DST,	/* be32 */
-	TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,/* be32 */
-	TCA_FLOWER_KEY_ENC_IPV6_SRC,	/* struct in6_addr */
-	TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK,/* struct in6_addr */
-	TCA_FLOWER_KEY_ENC_IPV6_DST,	/* struct in6_addr */
-	TCA_FLOWER_KEY_ENC_IPV6_DST_MASK,/* struct in6_addr */
-
-	TCA_FLOWER_KEY_TCP_SRC_MASK,	/* be16 */
-	TCA_FLOWER_KEY_TCP_DST_MASK,	/* be16 */
-	TCA_FLOWER_KEY_UDP_SRC_MASK,	/* be16 */
-	TCA_FLOWER_KEY_UDP_DST_MASK,	/* be16 */
-	TCA_FLOWER_KEY_SCTP_SRC_MASK,	/* be16 */
-	TCA_FLOWER_KEY_SCTP_DST_MASK,	/* be16 */
-
-	TCA_FLOWER_KEY_SCTP_SRC,	/* be16 */
-	TCA_FLOWER_KEY_SCTP_DST,	/* be16 */
-
-	TCA_FLOWER_KEY_ENC_UDP_SRC_PORT,	/* be16 */
-	TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK,	/* be16 */
-	TCA_FLOWER_KEY_ENC_UDP_DST_PORT,	/* be16 */
-	TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK,	/* be16 */
-
-	TCA_FLOWER_KEY_FLAGS,		/* be32 */
-	TCA_FLOWER_KEY_FLAGS_MASK,	/* be32 */
-
-	TCA_FLOWER_KEY_ICMPV4_CODE,	/* u8 */
-	TCA_FLOWER_KEY_ICMPV4_CODE_MASK,/* u8 */
-	TCA_FLOWER_KEY_ICMPV4_TYPE,	/* u8 */
-	TCA_FLOWER_KEY_ICMPV4_TYPE_MASK,/* u8 */
-	TCA_FLOWER_KEY_ICMPV6_CODE,	/* u8 */
-	TCA_FLOWER_KEY_ICMPV6_CODE_MASK,/* u8 */
-	TCA_FLOWER_KEY_ICMPV6_TYPE,	/* u8 */
-	TCA_FLOWER_KEY_ICMPV6_TYPE_MASK,/* u8 */
-
-	TCA_FLOWER_KEY_ARP_SIP,		/* be32 */
-	TCA_FLOWER_KEY_ARP_SIP_MASK,	/* be32 */
-	TCA_FLOWER_KEY_ARP_TIP,		/* be32 */
-	TCA_FLOWER_KEY_ARP_TIP_MASK,	/* be32 */
-	TCA_FLOWER_KEY_ARP_OP,		/* u8 */
-	TCA_FLOWER_KEY_ARP_OP_MASK,	/* u8 */
-	TCA_FLOWER_KEY_ARP_SHA,		/* ETH_ALEN */
-	TCA_FLOWER_KEY_ARP_SHA_MASK,	/* ETH_ALEN */
-	TCA_FLOWER_KEY_ARP_THA,		/* ETH_ALEN */
-	TCA_FLOWER_KEY_ARP_THA_MASK,	/* ETH_ALEN */
-
-	TCA_FLOWER_KEY_MPLS_TTL,	/* u8 - 8 bits */
-	TCA_FLOWER_KEY_MPLS_BOS,	/* u8 - 1 bit */
-	TCA_FLOWER_KEY_MPLS_TC,		/* u8 - 3 bits */
-	TCA_FLOWER_KEY_MPLS_LABEL,	/* be32 - 20 bits */
-
-	TCA_FLOWER_KEY_TCP_FLAGS,	/* be16 */
-	TCA_FLOWER_KEY_TCP_FLAGS_MASK,	/* be16 */
-
-	TCA_FLOWER_KEY_IP_TOS,		/* u8 */
-	TCA_FLOWER_KEY_IP_TOS_MASK,	/* u8 */
-	TCA_FLOWER_KEY_IP_TTL,		/* u8 */
-	TCA_FLOWER_KEY_IP_TTL_MASK,	/* u8 */
-
-	TCA_FLOWER_KEY_CVLAN_ID,	/* be16 */
-	TCA_FLOWER_KEY_CVLAN_PRIO,	/* u8   */
-	TCA_FLOWER_KEY_CVLAN_ETH_TYPE,	/* be16 */
-
-	TCA_FLOWER_KEY_ENC_IP_TOS,	/* u8 */
-	TCA_FLOWER_KEY_ENC_IP_TOS_MASK,	/* u8 */
-	TCA_FLOWER_KEY_ENC_IP_TTL,	/* u8 */
-	TCA_FLOWER_KEY_ENC_IP_TTL_MASK,	/* u8 */
-
-	TCA_FLOWER_KEY_ENC_OPTS,
-	TCA_FLOWER_KEY_ENC_OPTS_MASK,
-
-	TCA_FLOWER_IN_HW_COUNT,
-
-	TCA_FLOWER_KEY_PORT_SRC_MIN,	/* be16 */
-	TCA_FLOWER_KEY_PORT_SRC_MAX,	/* be16 */
-	TCA_FLOWER_KEY_PORT_DST_MIN,	/* be16 */
-	TCA_FLOWER_KEY_PORT_DST_MAX,	/* be16 */
-
-	TCA_FLOWER_KEY_CT_STATE,	/* u16 */
-	TCA_FLOWER_KEY_CT_STATE_MASK,	/* u16 */
-	TCA_FLOWER_KEY_CT_ZONE,		/* u16 */
-	TCA_FLOWER_KEY_CT_ZONE_MASK,	/* u16 */
-	TCA_FLOWER_KEY_CT_MARK,		/* u32 */
-	TCA_FLOWER_KEY_CT_MARK_MASK,	/* u32 */
-	TCA_FLOWER_KEY_CT_LABELS,	/* u128 */
-	TCA_FLOWER_KEY_CT_LABELS_MASK,	/* u128 */
-
-	__TCA_FLOWER_MAX,
-};
-
-#define TCA_FLOWER_MAX (__TCA_FLOWER_MAX - 1)
-
-enum {
-	TCA_FLOWER_KEY_CT_FLAGS_NEW = 1 << 0, /* Beginning of a new connection. */
-	TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED = 1 << 1, /* Part of an existing connection. */
-	TCA_FLOWER_KEY_CT_FLAGS_RELATED = 1 << 2, /* Related to an established connection. */
-	TCA_FLOWER_KEY_CT_FLAGS_TRACKED = 1 << 3, /* Conntrack has occurred. */
-};
-
-enum {
-	TCA_FLOWER_KEY_ENC_OPTS_UNSPEC,
-	TCA_FLOWER_KEY_ENC_OPTS_GENEVE, /* Nested
-					 * TCA_FLOWER_KEY_ENC_OPT_GENEVE_
-					 * attributes
-					 */
-	__TCA_FLOWER_KEY_ENC_OPTS_MAX,
-};
-
-#define TCA_FLOWER_KEY_ENC_OPTS_MAX (__TCA_FLOWER_KEY_ENC_OPTS_MAX - 1)
-
-enum {
-	TCA_FLOWER_KEY_ENC_OPT_GENEVE_UNSPEC,
-	TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS,            /* u16 */
-	TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE,             /* u8 */
-	TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA,             /* 4 to 128 bytes */
-
-	__TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX,
-};
-
-#define TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX \
-		(__TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX - 1)
-
-enum {
-	TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT = (1 << 0),
-	TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1),
-};
-
-#define TCA_FLOWER_MASK_FLAGS_RANGE	(1 << 0) /* Range-based match */
-
-/* Match-all classifier */
-
-struct tc_matchall_pcnt {
-	__u64 rhit;
-};
-
-enum {
-	TCA_MATCHALL_UNSPEC,
-	TCA_MATCHALL_CLASSID,
-	TCA_MATCHALL_ACT,
-	TCA_MATCHALL_FLAGS,
-	TCA_MATCHALL_PCNT,
-	TCA_MATCHALL_PAD,
-	__TCA_MATCHALL_MAX,
-};
-
-#define TCA_MATCHALL_MAX (__TCA_MATCHALL_MAX - 1)
-
-/* Extended Matches */
-
-struct tcf_ematch_tree_hdr {
-	__u16		nmatches;
-	__u16		progid;
-};
-
-enum {
-	TCA_EMATCH_TREE_UNSPEC,
-	TCA_EMATCH_TREE_HDR,
-	TCA_EMATCH_TREE_LIST,
-	__TCA_EMATCH_TREE_MAX
-};
-#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1)
-
-struct tcf_ematch_hdr {
-	__u16		matchid;
-	__u16		kind;
-	__u16		flags;
-	__u16		pad; /* currently unused */
-};
-
-/*  0                   1
- *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 
- * +-----------------------+-+-+---+
- * |         Unused        |S|I| R |
- * +-----------------------+-+-+---+
- *
- * R(2) ::= relation to next ematch
- *          where: 0 0 END (last ematch)
- *                 0 1 AND
- *                 1 0 OR
- *                 1 1 Unused (invalid)
- * I(1) ::= invert result
- * S(1) ::= simple payload
- */
-#define TCF_EM_REL_END	0
-#define TCF_EM_REL_AND	(1<<0)
-#define TCF_EM_REL_OR	(1<<1)
-#define TCF_EM_INVERT	(1<<2)
-#define TCF_EM_SIMPLE	(1<<3)
-
-#define TCF_EM_REL_MASK	3
-#define TCF_EM_REL_VALID(v) (((v) & TCF_EM_REL_MASK) != TCF_EM_REL_MASK)
-
-enum {
-	TCF_LAYER_LINK,
-	TCF_LAYER_NETWORK,
-	TCF_LAYER_TRANSPORT,
-	__TCF_LAYER_MAX
-};
-#define TCF_LAYER_MAX (__TCF_LAYER_MAX - 1)
-
-/* Ematch type assignments
- *   1..32767		Reserved for ematches inside kernel tree
- *   32768..65535	Free to use, not reliable
- */
-#define	TCF_EM_CONTAINER	0
-#define	TCF_EM_CMP		1
-#define	TCF_EM_NBYTE		2
-#define	TCF_EM_U32		3
-#define	TCF_EM_META		4
-#define	TCF_EM_TEXT		5
-#define	TCF_EM_VLAN		6
-#define	TCF_EM_CANID		7
-#define	TCF_EM_IPSET		8
-#define	TCF_EM_IPT		9
-#define	TCF_EM_MAX		9
-
-enum {
-	TCF_EM_PROG_TC
-};
-
-enum {
-	TCF_EM_OPND_EQ,
-	TCF_EM_OPND_GT,
-	TCF_EM_OPND_LT
-};
-
-#endif
diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h
deleted file mode 100644
index 12b5189..0000000
--- a/include/uapi/linux/pkt_sched.h
+++ /dev/null
@@ -1,1416 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef __LINUX_PKT_SCHED_H
-#define __LINUX_PKT_SCHED_H
-
-#include <linux/const.h>
-#include <linux/types.h>
-
-/* Logical priority bands not depending on specific packet scheduler.
-   Every scheduler will map them to real traffic classes, if it has
-   no more precise mechanism to classify packets.
-
-   These numbers have no special meaning, though their coincidence
-   with obsolete IPv6 values is not occasional :-). New IPv6 drafts
-   preferred full anarchy inspired by diffserv group.
-
-   Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy
-   class, actually, as rule it will be handled with more care than
-   filler or even bulk.
- */
-
-#define TC_PRIO_BESTEFFORT		0
-#define TC_PRIO_FILLER			1
-#define TC_PRIO_BULK			2
-#define TC_PRIO_INTERACTIVE_BULK	4
-#define TC_PRIO_INTERACTIVE		6
-#define TC_PRIO_CONTROL			7
-
-#define TC_PRIO_MAX			15
-
-/* Generic queue statistics, available for all the elements.
-   Particular schedulers may have also their private records.
- */
-
-struct tc_stats {
-	__u64	bytes;			/* Number of 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
-					 * flow goes out of allocated bandwidth */
-	__u32	bps;			/* Current flow byte rate */
-	__u32	pps;			/* Current flow packet rate */
-	__u32	qlen;
-	__u32	backlog;
-};
-
-struct tc_estimator {
-	signed char	interval;
-	unsigned char	ewma_log;
-};
-
-/* "Handles"
-   ---------
-
-    All the traffic control objects have 32bit identifiers, or "handles".
-
-    They can be considered as opaque numbers from user API viewpoint,
-    but actually they always consist of two fields: major and
-    minor numbers, which are interpreted by kernel specially,
-    that may be used by applications, though not recommended.
-
-    F.e. qdisc handles always have minor number equal to zero,
-    classes (or flows) have major equal to parent qdisc major, and
-    minor uniquely identifying class inside qdisc.
-
-    Macros to manipulate handles:
- */
-
-#define TC_H_MAJ_MASK (0xFFFF0000U)
-#define TC_H_MIN_MASK (0x0000FFFFU)
-#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK)
-#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK)
-#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK))
-
-#define TC_H_UNSPEC	(0U)
-#define TC_H_ROOT	(0xFFFFFFFFU)
-#define TC_H_INGRESS    (0xFFFFFFF1U)
-#define TC_H_CLSACT	TC_H_INGRESS
-
-#define TC_H_MIN_PRIORITY	0xFFE0U
-#define TC_H_MIN_INGRESS	0xFFF2U
-#define TC_H_MIN_EGRESS		0xFFF3U
-
-/* Need to corrospond to iproute2 tc/tc_core.h "enum link_layer" */
-enum tc_link_layer {
-	TC_LINKLAYER_UNAWARE, /* Indicate unaware old iproute2 util */
-	TC_LINKLAYER_ETHERNET,
-	TC_LINKLAYER_ATM,
-};
-#define TC_LINKLAYER_MASK 0x0F /* limit use to lower 4 bits */
-
-struct tc_ratespec {
-	unsigned char	cell_log;
-	__u8		linklayer; /* lower 4 bits */
-	unsigned short	overhead;
-	short		cell_align;
-	unsigned short	mpu;
-	__u32		rate;
-};
-
-#define TC_RTAB_SIZE	1024
-
-struct tc_sizespec {
-	unsigned char	cell_log;
-	unsigned char	size_log;
-	short		cell_align;
-	int		overhead;
-	unsigned int	linklayer;
-	unsigned int	mpu;
-	unsigned int	mtu;
-	unsigned int	tsize;
-};
-
-enum {
-	TCA_STAB_UNSPEC,
-	TCA_STAB_BASE,
-	TCA_STAB_DATA,
-	__TCA_STAB_MAX
-};
-
-#define TCA_STAB_MAX (__TCA_STAB_MAX - 1)
-
-/* NSSFIFO section */
-
-enum {
-	TCA_NSSFIFO_UNSPEC,
-	TCA_NSSFIFO_PARMS,
-	__TCA_NSSFIFO_MAX
-};
-
-#define TCA_NSSFIFO_MAX	(__TCA_NSSFIFO_MAX - 1)
-
-struct tc_nssfifo_qopt {
-	__u32	limit;		/* Queue length: bytes for bfifo, packets for pfifo */
-	__u8	set_default;	/* Sets qdisc to be the default qdisc for enqueue */
-};
-
-/* NSSWRED section */
-
-enum {
-	TCA_NSSWRED_UNSPEC,
-	TCA_NSSWRED_PARMS,
-	__TCA_NSSWRED_MAX
-};
-
-#define TCA_NSSWRED_MAX (__TCA_NSSWRED_MAX - 1)
-#define NSSWRED_CLASS_MAX 6
-struct tc_red_alg_parameter {
-	__u32	min;			/* qlen_avg < min: all pkts are enqueued */
-	__u32	max;			/* qlen_avg > max: all pkts are dropped */
-	__u32	probability;		/* Drop probability at qlen_avg = max */
-	__u32	exp_weight_factor;	/* exp_weight_factor for calculate qlen_avg */
-};
-
-struct tc_nsswred_traffic_class {
-	__u32 limit;			/* Queue length */
-	__u32 weight_mode_value;	/* Weight mode value */
-	struct tc_red_alg_parameter rap;/* Parameters for RED alg */
-};
-
-/*
- * Weight modes for WRED
- */
-enum tc_nsswred_weight_modes {
-	TC_NSSWRED_WEIGHT_MODE_DSCP = 0,	/* Weight mode is DSCP */
-	TC_NSSWRED_WEIGHT_MODES,		/* Must be last */
-};
-typedef enum tc_nsswred_weight_modes tc_nsswred_weight_mode_t;
-
-struct tc_nsswred_qopt {
-	__u32	limit;				/* Queue length */
-	tc_nsswred_weight_mode_t weight_mode;	/* Weight mode */
-	__u32	traffic_classes;		/* How many traffic classes: DPs */
-	__u32	def_traffic_class;		/* Default traffic if no match: def_DP */
-	__u32	traffic_id;			/* The traffic id to be configured */
-	__u32	weight_mode_value;		/* Weight mode value */
-	struct tc_red_alg_parameter rap;	/* RED algorithm parameters */
-	struct tc_nsswred_traffic_class tntc[NSSWRED_CLASS_MAX];
-						/* Traffic settings for dumpping */
-	__u8	ecn;				/* Setting ECN bit or dropping */
-	__u8	set_default;			/* Sets qdisc to be the default for enqueue */
-};
-
-/* NSSCODEL section */
-
-enum {
-	TCA_NSSCODEL_UNSPEC,
-	TCA_NSSCODEL_PARMS,
-	__TCA_NSSCODEL_MAX
-};
-
-#define TCA_NSSCODEL_MAX	(__TCA_NSSCODEL_MAX - 1)
-
-struct tc_nsscodel_qopt {
-	__u32	target;		/* Acceptable queueing delay */
-	__u32	limit;		/* Maximum number of packets that can be held in the queue */
-	__u32	interval;	/* Monitoring interval */
-	__u8	set_default;	/* Sets qdisc to be the default qdisc for enqueue */
-};
-
-struct tc_nsscodel_xstats {
-	__u32 peak_queue_delay;	/* Peak delay experienced by a dequeued packet */
-	__u32 peak_drop_delay;	/* Peak delay experienced by a dropped packet */
-};
-
-/* NSSTBL section */
-
-enum {
-	TCA_NSSTBL_UNSPEC,
-	TCA_NSSTBL_PARMS,
-	__TCA_NSSTBL_MAX
-};
-
-#define TCA_NSSTBL_MAX	(__TCA_NSSTBL_MAX - 1)
-
-struct tc_nsstbl_qopt {
-	__u32	burst;		/* Maximum burst size */
-	__u32	rate;		/* Limiting rate of TBF */
-	__u32	peakrate;	/* Maximum rate at which TBF is allowed to send */
-	__u32	mtu;		/* Max size of packet, or minumim burst size */
-};
-
-/* NSSPRIO section */
-
-#define TCA_NSSPRIO_MAX_BANDS 256
-
-enum {
-	TCA_NSSPRIO_UNSPEC,
-	TCA_NSSPRIO_PARMS,
-	__TCA_NSSPRIO_MAX
-};
-
-#define TCA_NSSPRIO_MAX	(__TCA_NSSPRIO_MAX - 1)
-
-struct tc_nssprio_qopt {
-	int	bands;				/* Number of bands */
-};
-
-/* NSSBF section */
-
-enum {
-	TCA_NSSBF_UNSPEC,
-	TCA_NSSBF_CLASS_PARMS,
-	TCA_NSSBF_QDISC_PARMS,
-	__TCA_NSSBF_MAX
-};
-
-#define TCA_NSSBF_MAX	(__TCA_NSSBF_MAX - 1)
-
-struct tc_nssbf_class_qopt {
-	__u32	burst;		/* Maximum burst size */
-	__u32	rate;		/* Allowed bandwidth for this class */
-	__u32	mtu;		/* MTU of the associated interface */
-	__u32	quantum;	/* Quamtum allocation for DRR */
-};
-
-struct tc_nssbf_qopt {
-	__u16	defcls;		/* Default class value */
-};
-
-/* NSSWRR section */
-
-enum {
-	TCA_NSSWRR_UNSPEC,
-	TCA_NSSWRR_CLASS_PARMS,
-	__TCA_NSSWRR_MAX
-};
-
-#define TCA_NSSWRR_MAX	(__TCA_NSSWRR_MAX - 1)
-
-struct tc_nsswrr_class_qopt {
-	__u32	quantum;	/* Weight associated to this class */
-};
-
-/* NSSWFQ section */
-
-enum {
-	TCA_NSSWFQ_UNSPEC,
-	TCA_NSSWFQ_CLASS_PARMS,
-	__TCA_NSSWFQ_MAX
-};
-
-#define TCA_NSSWFQ_MAX	(__TCA_NSSWFQ_MAX - 1)
-
-struct tc_nsswfq_class_qopt {
-	__u32	quantum;	/* Weight associated to this class */
-};
-
-/* NSSHTB section */
-
-enum {
-	TCA_NSSHTB_UNSPEC,
-	TCA_NSSHTB_CLASS_PARMS,
-	TCA_NSSHTB_QDISC_PARMS,
-	__TCA_NSSHTB_MAX
-};
-
-#define TCA_NSSHTB_MAX	(__TCA_NSSHTB_MAX - 1)
-
-struct tc_nsshtb_class_qopt {
-	__u32	burst;		/* Allowed burst size */
-	__u32	rate;		/* Allowed bandwidth for this class */
-	__u32	cburst;		/* Maximum burst size */
-	__u32	crate;		/* Maximum bandwidth for this class */
-	__u32	quantum;	/* Quantum allocation for DRR */
-	__u32	priority;	/* Priority value associated with this class */
-	__u32	overhead;	/* Overhead in bytes per packet */
-};
-
-struct tc_nsshtb_qopt {
-	__u32	r2q;		/* Rate to quantum ratio */
-};
-
-/* NSSBLACKHOLE section */
-
-enum {
-	TCA_NSSBLACKHOLE_UNSPEC,
-	TCA_NSSBLACKHOLE_PARMS,
-	__TCA_NSSBLACKHOLE_MAX
-};
-
-#define TCA_NSSBLACKHOLE_MAX	(__TCA_NSSBLACKHOLE_MAX - 1)
-
-struct tc_nssblackhole_qopt {
-	__u8	set_default;	/* Sets qdisc to be the default qdisc for enqueue */
-};
-
-/* FIFO section */
-
-struct tc_fifo_qopt {
-	__u32	limit;	/* Queue length: bytes for bfifo, packets for pfifo */
-};
-
-/* SKBPRIO section */
-
-/*
- * Priorities go from zero to (SKBPRIO_MAX_PRIORITY - 1).
- * SKBPRIO_MAX_PRIORITY should be at least 64 in order for skbprio to be able
- * to map one to one the DS field of IPV4 and IPV6 headers.
- * Memory allocation grows linearly with SKBPRIO_MAX_PRIORITY.
- */
-
-#define SKBPRIO_MAX_PRIORITY 64
-
-struct tc_skbprio_qopt {
-	__u32	limit;		/* Queue length in packets. */
-};
-
-/* PRIO section */
-
-#define TCQ_PRIO_BANDS	16
-#define TCQ_MIN_PRIO_BANDS 2
-
-struct tc_prio_qopt {
-	int	bands;			/* Number of bands */
-	__u8	priomap[TC_PRIO_MAX+1];	/* Map: logical priority -> PRIO band */
-};
-
-/* MULTIQ section */
-
-struct tc_multiq_qopt {
-	__u16	bands;			/* Number of bands */
-	__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 {
-	struct tc_ratespec rate;
-	struct tc_ratespec peakrate;
-	__u32		limit;
-	__u32		buffer;
-	__u32		mtu;
-};
-
-enum {
-	TCA_TBF_UNSPEC,
-	TCA_TBF_PARMS,
-	TCA_TBF_RTAB,
-	TCA_TBF_PTAB,
-	TCA_TBF_RATE64,
-	TCA_TBF_PRATE64,
-	TCA_TBF_BURST,
-	TCA_TBF_PBURST,
-	TCA_TBF_PAD,
-	__TCA_TBF_MAX,
-};
-
-#define TCA_TBF_MAX (__TCA_TBF_MAX - 1)
-
-
-/* TEQL section */
-
-/* TEQL does not require any parameters */
-
-/* SFQ section */
-
-struct tc_sfq_qopt {
-	unsigned	quantum;	/* Bytes per round allocated to flow */
-	int		perturb_period;	/* Period of hash perturbation */
-	__u32		limit;		/* Maximal packets in queue */
-	unsigned	divisor;	/* Hash divisor  */
-	unsigned	flows;		/* Maximal number of flows  */
-};
-
-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;
-};
-
-/* RED section */
-
-enum {
-	TCA_RED_UNSPEC,
-	TCA_RED_PARMS,
-	TCA_RED_STAB,
-	TCA_RED_MAX_P,
-	__TCA_RED_MAX,
-};
-
-#define TCA_RED_MAX (__TCA_RED_MAX - 1)
-
-struct tc_red_qopt {
-	__u32		limit;		/* HARD maximal queue length (bytes)	*/
-	__u32		qth_min;	/* Min average length threshold (bytes) */
-	__u32		qth_max;	/* Max average length threshold (bytes) */
-	unsigned char   Wlog;		/* log(W)		*/
-	unsigned char   Plog;		/* log(P_max/(qth_max-qth_min))	*/
-	unsigned char   Scell_log;	/* cell size for idle damping */
-	unsigned char	flags;
-#define TC_RED_ECN		1
-#define TC_RED_HARDDROP		2
-#define TC_RED_ADAPTATIVE	4
-};
-
-struct tc_red_xstats {
-	__u32           early;          /* Early drops */
-	__u32           pdrop;          /* Drops due to queue limits */
-	__u32           other;          /* Drops due to drop() calls */
-	__u32           marked;         /* Marked packets */
-};
-
-/* GRED section */
-
-#define MAX_DPs 16
-
-enum {
-       TCA_GRED_UNSPEC,
-       TCA_GRED_PARMS,
-       TCA_GRED_STAB,
-       TCA_GRED_DPS,
-       TCA_GRED_MAX_P,
-       TCA_GRED_LIMIT,
-       TCA_GRED_VQ_LIST,	/* nested TCA_GRED_VQ_ENTRY */
-       __TCA_GRED_MAX,
-};
-
-#define TCA_GRED_MAX (__TCA_GRED_MAX - 1)
-
-enum {
-	TCA_GRED_VQ_ENTRY_UNSPEC,
-	TCA_GRED_VQ_ENTRY,	/* nested TCA_GRED_VQ_* */
-	__TCA_GRED_VQ_ENTRY_MAX,
-};
-#define TCA_GRED_VQ_ENTRY_MAX (__TCA_GRED_VQ_ENTRY_MAX - 1)
-
-enum {
-	TCA_GRED_VQ_UNSPEC,
-	TCA_GRED_VQ_PAD,
-	TCA_GRED_VQ_DP,			/* u32 */
-	TCA_GRED_VQ_STAT_BYTES,		/* u64 */
-	TCA_GRED_VQ_STAT_PACKETS,	/* u32 */
-	TCA_GRED_VQ_STAT_BACKLOG,	/* u32 */
-	TCA_GRED_VQ_STAT_PROB_DROP,	/* u32 */
-	TCA_GRED_VQ_STAT_PROB_MARK,	/* u32 */
-	TCA_GRED_VQ_STAT_FORCED_DROP,	/* u32 */
-	TCA_GRED_VQ_STAT_FORCED_MARK,	/* u32 */
-	TCA_GRED_VQ_STAT_PDROP,		/* u32 */
-	TCA_GRED_VQ_STAT_OTHER,		/* u32 */
-	TCA_GRED_VQ_FLAGS,		/* u32 */
-	__TCA_GRED_VQ_MAX
-};
-
-#define TCA_GRED_VQ_MAX (__TCA_GRED_VQ_MAX - 1)
-
-struct tc_gred_qopt {
-	__u32		limit;        /* HARD maximal queue length (bytes)    */
-	__u32		qth_min;      /* Min average length threshold (bytes) */
-	__u32		qth_max;      /* Max average length threshold (bytes) */
-	__u32		DP;           /* up to 2^32 DPs */
-	__u32		backlog;
-	__u32		qave;
-	__u32		forced;
-	__u32		early;
-	__u32		other;
-	__u32		pdrop;
-	__u8		Wlog;         /* log(W)               */
-	__u8		Plog;         /* log(P_max/(qth_max-qth_min)) */
-	__u8		Scell_log;    /* cell size for idle damping */
-	__u8		prio;         /* prio of this VQ */
-	__u32		packets;
-	__u32		bytesin;
-};
-
-/* gred setup */
-struct tc_gred_sopt {
-	__u32		DPs;
-	__u32		def_DP;
-	__u8		grio;
-	__u8		flags;
-	__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
-#define TC_HTB_PROTOVER		3 /* the same as HTB and TC's major */
-
-struct tc_htb_opt {
-	struct tc_ratespec 	rate;
-	struct tc_ratespec 	ceil;
-	__u32	buffer;
-	__u32	cbuffer;
-	__u32	quantum;
-	__u32	level;		/* out only */
-	__u32	prio;
-};
-struct tc_htb_glob {
-	__u32 version;		/* to match HTB/TC */
-    	__u32 rate2quantum;	/* bps->quantum divisor */
-    	__u32 defcls;		/* default class number */
-	__u32 debug;		/* debug flags */
-
-	/* stats */
-	__u32 direct_pkts; /* count of non shaped packets */
-};
-enum {
-	TCA_HTB_UNSPEC,
-	TCA_HTB_PARMS,
-	TCA_HTB_INIT,
-	TCA_HTB_CTAB,
-	TCA_HTB_RTAB,
-	TCA_HTB_DIRECT_QLEN,
-	TCA_HTB_RATE64,
-	TCA_HTB_CEIL64,
-	TCA_HTB_PAD,
-	__TCA_HTB_MAX,
-};
-
-#define TCA_HTB_MAX (__TCA_HTB_MAX - 1)
-
-struct tc_htb_xstats {
-	__u32 lends;
-	__u32 borrows;
-	__u32 giants;	/* unused since 'Make HTB scheduler work with TSO.' */
-	__s32 tokens;
-	__s32 ctokens;
-};
-
-/* HFSC section */
-
-struct tc_hfsc_qopt {
-	__u16	defcls;		/* default class */
-};
-
-struct tc_service_curve {
-	__u32	m1;		/* slope of the first segment in bps */
-	__u32	d;		/* x-projection of the first segment in us */
-	__u32	m2;		/* slope of the second segment in bps */
-};
-
-struct tc_hfsc_stats {
-	__u64	work;		/* total work done */
-	__u64	rtwork;		/* work done by real-time criteria */
-	__u32	period;		/* current period */
-	__u32	level;		/* class level in hierarchy */
-};
-
-enum {
-	TCA_HFSC_UNSPEC,
-	TCA_HFSC_RSC,
-	TCA_HFSC_FSC,
-	TCA_HFSC_USC,
-	__TCA_HFSC_MAX,
-};
-
-#define TCA_HFSC_MAX (__TCA_HFSC_MAX - 1)
-
-
-/* CBQ section */
-
-#define TC_CBQ_MAXPRIO		8
-#define TC_CBQ_MAXLEVEL		8
-#define TC_CBQ_DEF_EWMA		5
-
-struct tc_cbq_lssopt {
-	unsigned char	change;
-	unsigned char	flags;
-#define TCF_CBQ_LSS_BOUNDED	1
-#define TCF_CBQ_LSS_ISOLATED	2
-	unsigned char  	ewma_log;
-	unsigned char  	level;
-#define TCF_CBQ_LSS_FLAGS	1
-#define TCF_CBQ_LSS_EWMA	2
-#define TCF_CBQ_LSS_MAXIDLE	4
-#define TCF_CBQ_LSS_MINIDLE	8
-#define TCF_CBQ_LSS_OFFTIME	0x10
-#define TCF_CBQ_LSS_AVPKT	0x20
-	__u32		maxidle;
-	__u32		minidle;
-	__u32		offtime;
-	__u32		avpkt;
-};
-
-struct tc_cbq_wrropt {
-	unsigned char	flags;
-	unsigned char	priority;
-	unsigned char	cpriority;
-	unsigned char	__reserved;
-	__u32		allot;
-	__u32		weight;
-};
-
-struct tc_cbq_ovl {
-	unsigned char	strategy;
-#define	TC_CBQ_OVL_CLASSIC	0
-#define	TC_CBQ_OVL_DELAY	1
-#define	TC_CBQ_OVL_LOWPRIO	2
-#define	TC_CBQ_OVL_DROP		3
-#define	TC_CBQ_OVL_RCLASSIC	4
-	unsigned char	priority2;
-	__u16		pad;
-	__u32		penalty;
-};
-
-struct tc_cbq_police {
-	unsigned char	police;
-	unsigned char	__res1;
-	unsigned short	__res2;
-};
-
-struct tc_cbq_fopt {
-	__u32		split;
-	__u32		defmap;
-	__u32		defchange;
-};
-
-struct tc_cbq_xstats {
-	__u32		borrows;
-	__u32		overactions;
-	__s32		avgidle;
-	__s32		undertime;
-};
-
-enum {
-	TCA_CBQ_UNSPEC,
-	TCA_CBQ_LSSOPT,
-	TCA_CBQ_WRROPT,
-	TCA_CBQ_FOPT,
-	TCA_CBQ_OVL_STRATEGY,
-	TCA_CBQ_RATE,
-	TCA_CBQ_RTAB,
-	TCA_CBQ_POLICE,
-	__TCA_CBQ_MAX,
-};
-
-#define TCA_CBQ_MAX	(__TCA_CBQ_MAX - 1)
-
-/* ARL section */
-
-struct tc_arl_xstats {
-	__u32	max_bw;		/* The maximum bw measured */
-	__u32	min_rate;	/* The lowest base rate */
-	__u32	current_rate;	/* The current rate */
-	__u32	latency;	/* The current latency */
-	__u32	base_rate;	/* The base rate */
-	__u32	current_bw;	/* The current bw measured */
-	__u32	state;		/* The current state */
-};
-
-enum {
-	TCA_ARL_UNSPEC,
-	TCA_ARL_BUFFER,
-	TCA_ARL_MIN_RATE,
-	TCA_ARL_MAX_BW,
-	TCA_ARL_LIMIT,
-	TCA_ARL_MAX_LATENCY,
-	TCA_ARL_LATENCY_HYSTERESIS,
-	TCA_ARL_PAD,
-	TCA_ARL_MODE,
-	TCA_ARL_CODEL_TARGET,
-	__TCA_ARL_MAX,
-};
-#define TCA_ARL_MAX (__TCA_ARL_MAX - 1)
-
-/* dsmark section */
-
-enum {
-	TCA_DSMARK_UNSPEC,
-	TCA_DSMARK_INDICES,
-	TCA_DSMARK_DEFAULT_INDEX,
-	TCA_DSMARK_SET_TC_INDEX,
-	TCA_DSMARK_MASK,
-	TCA_DSMARK_VALUE,
-	__TCA_DSMARK_MAX,
-};
-
-#define TCA_DSMARK_MAX (__TCA_DSMARK_MAX - 1)
-
-/* ATM  section */
-
-enum {
-	TCA_ATM_UNSPEC,
-	TCA_ATM_FD,		/* file/socket descriptor */
-	TCA_ATM_PTR,		/* pointer to descriptor - later */
-	TCA_ATM_HDR,		/* LL header */
-	TCA_ATM_EXCESS,		/* excess traffic class (0 for CLP)  */
-	TCA_ATM_ADDR,		/* PVC address (for output only) */
-	TCA_ATM_STATE,		/* VC state (ATM_VS_*; for output only) */
-	__TCA_ATM_MAX,
-};
-
-#define TCA_ATM_MAX	(__TCA_ATM_MAX - 1)
-
-/* Network emulator */
-
-enum {
-	TCA_NETEM_UNSPEC,
-	TCA_NETEM_CORR,
-	TCA_NETEM_DELAY_DIST,
-	TCA_NETEM_REORDER,
-	TCA_NETEM_CORRUPT,
-	TCA_NETEM_LOSS,
-	TCA_NETEM_RATE,
-	TCA_NETEM_ECN,
-	TCA_NETEM_RATE64,
-	TCA_NETEM_PAD,
-	TCA_NETEM_LATENCY64,
-	TCA_NETEM_JITTER64,
-	TCA_NETEM_SLOT,
-	TCA_NETEM_SLOT_DIST,
-	__TCA_NETEM_MAX,
-};
-
-#define TCA_NETEM_MAX (__TCA_NETEM_MAX - 1)
-
-struct tc_netem_qopt {
-	__u32	latency;	/* added delay (us) */
-	__u32   limit;		/* fifo limit (packets) */
-	__u32	loss;		/* random packet loss (0=none ~0=100%) */
-	__u32	gap;		/* re-ordering gap (0 for none) */
-	__u32   duplicate;	/* random packet dup  (0=none ~0=100%) */
-	__u32	jitter;		/* random jitter in latency (us) */
-};
-
-struct tc_netem_corr {
-	__u32	delay_corr;	/* delay correlation */
-	__u32	loss_corr;	/* packet loss correlation */
-	__u32	dup_corr;	/* duplicate correlation  */
-};
-
-struct tc_netem_reorder {
-	__u32	probability;
-	__u32	correlation;
-};
-
-struct tc_netem_corrupt {
-	__u32	probability;
-	__u32	correlation;
-};
-
-struct tc_netem_rate {
-	__u32	rate;	/* byte/s */
-	__s32	packet_overhead;
-	__u32	cell_size;
-	__s32	cell_overhead;
-};
-
-struct tc_netem_slot {
-	__s64   min_delay; /* nsec */
-	__s64   max_delay;
-	__s32   max_packets;
-	__s32   max_bytes;
-	__s64	dist_delay; /* nsec */
-	__s64	dist_jitter; /* nsec */
-};
-
-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 */
-
-enum {
-	TCA_DRR_UNSPEC,
-	TCA_DRR_QUANTUM,
-	__TCA_DRR_MAX
-};
-
-#define TCA_DRR_MAX	(__TCA_DRR_MAX - 1)
-
-struct tc_drr_stats {
-	__u32	deficit;
-};
-
-/* MQPRIO */
-#define TC_QOPT_BITMASK 15
-#define TC_QOPT_MAX_QUEUE 16
-
-enum {
-	TC_MQPRIO_HW_OFFLOAD_NONE,	/* no offload requested */
-	TC_MQPRIO_HW_OFFLOAD_TCS,	/* offload TCs, no queue counts */
-	__TC_MQPRIO_HW_OFFLOAD_MAX
-};
-
-#define TC_MQPRIO_HW_OFFLOAD_MAX (__TC_MQPRIO_HW_OFFLOAD_MAX - 1)
-
-enum {
-	TC_MQPRIO_MODE_DCB,
-	TC_MQPRIO_MODE_CHANNEL,
-	__TC_MQPRIO_MODE_MAX
-};
-
-#define __TC_MQPRIO_MODE_MAX (__TC_MQPRIO_MODE_MAX - 1)
-
-enum {
-	TC_MQPRIO_SHAPER_DCB,
-	TC_MQPRIO_SHAPER_BW_RATE,	/* Add new shapers below */
-	__TC_MQPRIO_SHAPER_MAX
-};
-
-#define __TC_MQPRIO_SHAPER_MAX (__TC_MQPRIO_SHAPER_MAX - 1)
-
-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];
-};
-
-#define TC_MQPRIO_F_MODE		0x1
-#define TC_MQPRIO_F_SHAPER		0x2
-#define TC_MQPRIO_F_MIN_RATE		0x4
-#define TC_MQPRIO_F_MAX_RATE		0x8
-
-enum {
-	TCA_MQPRIO_UNSPEC,
-	TCA_MQPRIO_MODE,
-	TCA_MQPRIO_SHAPER,
-	TCA_MQPRIO_MIN_RATE64,
-	TCA_MQPRIO_MAX_RATE64,
-	__TCA_MQPRIO_MAX,
-};
-
-#define TCA_MQPRIO_MAX (__TCA_MQPRIO_MAX - 1)
-
-/* 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;
-};
-
-/* CODEL */
-
-enum {
-	TCA_CODEL_UNSPEC,
-	TCA_CODEL_TARGET,
-	TCA_CODEL_LIMIT,
-	TCA_CODEL_INTERVAL,
-	TCA_CODEL_ECN,
-	TCA_CODEL_CE_THRESHOLD,
-	__TCA_CODEL_MAX
-};
-
-#define TCA_CODEL_MAX	(__TCA_CODEL_MAX - 1)
-
-struct tc_codel_xstats {
-	__u32	maxpacket; /* largest packet we've seen so far */
-	__u32	count;	   /* how many drops we've done since the last time we
-			    * entered dropping state
-			    */
-	__u32	lastcount; /* count at entry to dropping state */
-	__u32	ldelay;    /* in-queue delay seen by most recently dequeued packet */
-	__s32	drop_next; /* time to drop next packet */
-	__u32	drop_overlimit; /* number of time max qdisc packet limit was hit */
-	__u32	ecn_mark;  /* number of packets we ECN marked instead of dropped */
-	__u32	dropping;  /* are we in dropping state ? */
-	__u32	ce_mark;   /* number of CE marked packets because of ce_threshold */
-};
-
-/* FQ_CODEL */
-
-enum {
-	TCA_FQ_CODEL_UNSPEC,
-	TCA_FQ_CODEL_TARGET,
-	TCA_FQ_CODEL_LIMIT,
-	TCA_FQ_CODEL_INTERVAL,
-	TCA_FQ_CODEL_ECN,
-	TCA_FQ_CODEL_FLOWS,
-	TCA_FQ_CODEL_QUANTUM,
-	TCA_FQ_CODEL_CE_THRESHOLD,
-	TCA_FQ_CODEL_DROP_BATCH_SIZE,
-	TCA_FQ_CODEL_MEMORY_LIMIT,
-	__TCA_FQ_CODEL_MAX
-};
-
-#define TCA_FQ_CODEL_MAX	(__TCA_FQ_CODEL_MAX - 1)
-
-enum {
-	TCA_FQ_CODEL_XSTATS_QDISC,
-	TCA_FQ_CODEL_XSTATS_CLASS,
-};
-
-struct tc_fq_codel_qd_stats {
-	__u32	maxpacket;	/* largest packet we've seen so far */
-	__u32	drop_overlimit; /* number of time max qdisc
-				 * packet limit was hit
-				 */
-	__u32	ecn_mark;	/* number of packets we ECN marked
-				 * instead of being dropped
-				 */
-	__u32	new_flow_count; /* number of time packets
-				 * created a 'new flow'
-				 */
-	__u32	new_flows_len;	/* count of flows in new list */
-	__u32	old_flows_len;	/* count of flows in old list */
-	__u32	ce_mark;	/* packets above ce_threshold */
-	__u32	memory_usage;	/* in bytes */
-	__u32	drop_overmemory;
-};
-
-struct tc_fq_codel_cl_stats {
-	__s32	deficit;
-	__u32	ldelay;		/* in-queue delay seen by most recently
-				 * dequeued packet
-				 */
-	__u32	count;
-	__u32	lastcount;
-	__u32	dropping;
-	__s32	drop_next;
-};
-
-struct tc_fq_codel_xstats {
-	__u32	type;
-	union {
-		struct tc_fq_codel_qd_stats qdisc_stats;
-		struct tc_fq_codel_cl_stats class_stats;
-	};
-};
-
-/* FQ */
-
-enum {
-	TCA_FQ_UNSPEC,
-
-	TCA_FQ_PLIMIT,		/* limit of total number of packets in queue */
-
-	TCA_FQ_FLOW_PLIMIT,	/* limit of packets per flow */
-
-	TCA_FQ_QUANTUM,		/* RR quantum */
-
-	TCA_FQ_INITIAL_QUANTUM,		/* RR quantum for new flow */
-
-	TCA_FQ_RATE_ENABLE,	/* enable/disable rate limiting */
-
-	TCA_FQ_FLOW_DEFAULT_RATE,/* obsolete, do not use */
-
-	TCA_FQ_FLOW_MAX_RATE,	/* per flow max rate */
-
-	TCA_FQ_BUCKETS_LOG,	/* log2(number of buckets) */
-
-	TCA_FQ_FLOW_REFILL_DELAY,	/* flow credit refill delay in usec */
-
-	TCA_FQ_ORPHAN_MASK,	/* mask applied to orphaned skb hashes */
-
-	TCA_FQ_LOW_RATE_THRESHOLD, /* per packet delay under this rate */
-
-	TCA_FQ_CE_THRESHOLD,	/* DCTCP-like CE-marking threshold */
-
-	__TCA_FQ_MAX
-};
-
-#define TCA_FQ_MAX	(__TCA_FQ_MAX - 1)
-
-struct tc_fq_qd_stats {
-	__u64	gc_flows;
-	__u64	highprio_packets;
-	__u64	tcp_retrans;
-	__u64	throttled;
-	__u64	flows_plimit;
-	__u64	pkts_too_long;
-	__u64	allocation_errors;
-	__s64	time_next_delayed_flow;
-	__u32	flows;
-	__u32	inactive_flows;
-	__u32	throttled_flows;
-	__u32	unthrottle_latency_ns;
-	__u64	ce_mark;		/* packets above ce_threshold */
-};
-
-/* Heavy-Hitter Filter */
-
-enum {
-	TCA_HHF_UNSPEC,
-	TCA_HHF_BACKLOG_LIMIT,
-	TCA_HHF_QUANTUM,
-	TCA_HHF_HH_FLOWS_LIMIT,
-	TCA_HHF_RESET_TIMEOUT,
-	TCA_HHF_ADMIT_BYTES,
-	TCA_HHF_EVICT_TIMEOUT,
-	TCA_HHF_NON_HH_WEIGHT,
-	__TCA_HHF_MAX
-};
-
-#define TCA_HHF_MAX	(__TCA_HHF_MAX - 1)
-
-struct tc_hhf_xstats {
-	__u32	drop_overlimit; /* number of times max qdisc packet limit
-				 * was hit
-				 */
-	__u32	hh_overlimit;   /* number of times max heavy-hitters was hit */
-	__u32	hh_tot_count;   /* number of captured heavy-hitters so far */
-	__u32	hh_cur_count;   /* number of current heavy-hitters */
-};
-
-/* PIE */
-enum {
-	TCA_PIE_UNSPEC,
-	TCA_PIE_TARGET,
-	TCA_PIE_LIMIT,
-	TCA_PIE_TUPDATE,
-	TCA_PIE_ALPHA,
-	TCA_PIE_BETA,
-	TCA_PIE_ECN,
-	TCA_PIE_BYTEMODE,
-	__TCA_PIE_MAX
-};
-#define TCA_PIE_MAX   (__TCA_PIE_MAX - 1)
-
-struct tc_pie_xstats {
-	__u64 prob;             /* current probability */
-	__u32 delay;            /* current delay in ms */
-	__u32 avg_dq_rate;      /* current average dq_rate in bits/pie_time */
-	__u32 packets_in;       /* total number of packets enqueued */
-	__u32 dropped;          /* packets dropped due to pie_action */
-	__u32 overlimit;        /* dropped due to lack of space in queue */
-	__u32 maxq;             /* maximum queue size */
-	__u32 ecn_mark;         /* packets marked with ecn*/
-};
-
-/* CBS */
-struct tc_cbs_qopt {
-	__u8 offload;
-	__u8 _pad[3];
-	__s32 hicredit;
-	__s32 locredit;
-	__s32 idleslope;
-	__s32 sendslope;
-};
-
-enum {
-	TCA_CBS_UNSPEC,
-	TCA_CBS_PARMS,
-	__TCA_CBS_MAX,
-};
-
-#define TCA_CBS_MAX (__TCA_CBS_MAX - 1)
-
-
-/* ETF */
-struct tc_etf_qopt {
-	__s32 delta;
-	__s32 clockid;
-	__u32 flags;
-#define TC_ETF_DEADLINE_MODE_ON	_BITUL(0)
-#define TC_ETF_OFFLOAD_ON	_BITUL(1)
-#define TC_ETF_SKIP_SOCK_CHECK	_BITUL(2)
-};
-
-enum {
-	TCA_ETF_UNSPEC,
-	TCA_ETF_PARMS,
-	__TCA_ETF_MAX,
-};
-
-#define TCA_ETF_MAX (__TCA_ETF_MAX - 1)
-
-
-/* CAKE */
-enum {
-	TCA_CAKE_UNSPEC,
-	TCA_CAKE_PAD,
-	TCA_CAKE_BASE_RATE64,
-	TCA_CAKE_DIFFSERV_MODE,
-	TCA_CAKE_ATM,
-	TCA_CAKE_FLOW_MODE,
-	TCA_CAKE_OVERHEAD,
-	TCA_CAKE_RTT,
-	TCA_CAKE_TARGET,
-	TCA_CAKE_AUTORATE,
-	TCA_CAKE_MEMORY,
-	TCA_CAKE_NAT,
-	TCA_CAKE_RAW,
-	TCA_CAKE_WASH,
-	TCA_CAKE_MPU,
-	TCA_CAKE_INGRESS,
-	TCA_CAKE_ACK_FILTER,
-	TCA_CAKE_SPLIT_GSO,
-	TCA_CAKE_FWMARK,
-	__TCA_CAKE_MAX
-};
-#define TCA_CAKE_MAX	(__TCA_CAKE_MAX - 1)
-
-enum {
-	__TCA_CAKE_STATS_INVALID,
-	TCA_CAKE_STATS_PAD,
-	TCA_CAKE_STATS_CAPACITY_ESTIMATE64,
-	TCA_CAKE_STATS_MEMORY_LIMIT,
-	TCA_CAKE_STATS_MEMORY_USED,
-	TCA_CAKE_STATS_AVG_NETOFF,
-	TCA_CAKE_STATS_MIN_NETLEN,
-	TCA_CAKE_STATS_MAX_NETLEN,
-	TCA_CAKE_STATS_MIN_ADJLEN,
-	TCA_CAKE_STATS_MAX_ADJLEN,
-	TCA_CAKE_STATS_TIN_STATS,
-	TCA_CAKE_STATS_DEFICIT,
-	TCA_CAKE_STATS_COBALT_COUNT,
-	TCA_CAKE_STATS_DROPPING,
-	TCA_CAKE_STATS_DROP_NEXT_US,
-	TCA_CAKE_STATS_P_DROP,
-	TCA_CAKE_STATS_BLUE_TIMER_US,
-	__TCA_CAKE_STATS_MAX
-};
-#define TCA_CAKE_STATS_MAX (__TCA_CAKE_STATS_MAX - 1)
-
-enum {
-	__TCA_CAKE_TIN_STATS_INVALID,
-	TCA_CAKE_TIN_STATS_PAD,
-	TCA_CAKE_TIN_STATS_SENT_PACKETS,
-	TCA_CAKE_TIN_STATS_SENT_BYTES64,
-	TCA_CAKE_TIN_STATS_DROPPED_PACKETS,
-	TCA_CAKE_TIN_STATS_DROPPED_BYTES64,
-	TCA_CAKE_TIN_STATS_ACKS_DROPPED_PACKETS,
-	TCA_CAKE_TIN_STATS_ACKS_DROPPED_BYTES64,
-	TCA_CAKE_TIN_STATS_ECN_MARKED_PACKETS,
-	TCA_CAKE_TIN_STATS_ECN_MARKED_BYTES64,
-	TCA_CAKE_TIN_STATS_BACKLOG_PACKETS,
-	TCA_CAKE_TIN_STATS_BACKLOG_BYTES,
-	TCA_CAKE_TIN_STATS_THRESHOLD_RATE64,
-	TCA_CAKE_TIN_STATS_TARGET_US,
-	TCA_CAKE_TIN_STATS_INTERVAL_US,
-	TCA_CAKE_TIN_STATS_WAY_INDIRECT_HITS,
-	TCA_CAKE_TIN_STATS_WAY_MISSES,
-	TCA_CAKE_TIN_STATS_WAY_COLLISIONS,
-	TCA_CAKE_TIN_STATS_PEAK_DELAY_US,
-	TCA_CAKE_TIN_STATS_AVG_DELAY_US,
-	TCA_CAKE_TIN_STATS_BASE_DELAY_US,
-	TCA_CAKE_TIN_STATS_SPARSE_FLOWS,
-	TCA_CAKE_TIN_STATS_BULK_FLOWS,
-	TCA_CAKE_TIN_STATS_UNRESPONSIVE_FLOWS,
-	TCA_CAKE_TIN_STATS_MAX_SKBLEN,
-	TCA_CAKE_TIN_STATS_FLOW_QUANTUM,
-	__TCA_CAKE_TIN_STATS_MAX
-};
-#define TCA_CAKE_TIN_STATS_MAX (__TCA_CAKE_TIN_STATS_MAX - 1)
-#define TC_CAKE_MAX_TINS (8)
-
-enum {
-	CAKE_FLOW_NONE = 0,
-	CAKE_FLOW_SRC_IP,
-	CAKE_FLOW_DST_IP,
-	CAKE_FLOW_HOSTS,    /* = CAKE_FLOW_SRC_IP | CAKE_FLOW_DST_IP */
-	CAKE_FLOW_FLOWS,
-	CAKE_FLOW_DUAL_SRC, /* = CAKE_FLOW_SRC_IP | CAKE_FLOW_FLOWS */
-	CAKE_FLOW_DUAL_DST, /* = CAKE_FLOW_DST_IP | CAKE_FLOW_FLOWS */
-	CAKE_FLOW_TRIPLE,   /* = CAKE_FLOW_HOSTS  | CAKE_FLOW_FLOWS */
-	CAKE_FLOW_MAX,
-};
-
-enum {
-	CAKE_DIFFSERV_DIFFSERV3 = 0,
-	CAKE_DIFFSERV_DIFFSERV4,
-	CAKE_DIFFSERV_DIFFSERV8,
-	CAKE_DIFFSERV_BESTEFFORT,
-	CAKE_DIFFSERV_PRECEDENCE,
-	CAKE_DIFFSERV_MAX
-};
-
-enum {
-	CAKE_ACK_NONE = 0,
-	CAKE_ACK_FILTER,
-	CAKE_ACK_AGGRESSIVE,
-	CAKE_ACK_MAX
-};
-
-enum {
-	CAKE_ATM_NONE = 0,
-	CAKE_ATM_ATM,
-	CAKE_ATM_PTM,
-	CAKE_ATM_MAX
-};
-
-
-/* TAPRIO */
-enum {
-	TC_TAPRIO_CMD_SET_GATES = 0x00,
-	TC_TAPRIO_CMD_SET_AND_HOLD = 0x01,
-	TC_TAPRIO_CMD_SET_AND_RELEASE = 0x02,
-};
-
-enum {
-	TCA_TAPRIO_SCHED_ENTRY_UNSPEC,
-	TCA_TAPRIO_SCHED_ENTRY_INDEX, /* u32 */
-	TCA_TAPRIO_SCHED_ENTRY_CMD, /* u8 */
-	TCA_TAPRIO_SCHED_ENTRY_GATE_MASK, /* u32 */
-	TCA_TAPRIO_SCHED_ENTRY_INTERVAL, /* u32 */
-	__TCA_TAPRIO_SCHED_ENTRY_MAX,
-};
-#define TCA_TAPRIO_SCHED_ENTRY_MAX (__TCA_TAPRIO_SCHED_ENTRY_MAX - 1)
-
-/* The format for schedule entry list is:
- * [TCA_TAPRIO_SCHED_ENTRY_LIST]
- *   [TCA_TAPRIO_SCHED_ENTRY]
- *     [TCA_TAPRIO_SCHED_ENTRY_CMD]
- *     [TCA_TAPRIO_SCHED_ENTRY_GATES]
- *     [TCA_TAPRIO_SCHED_ENTRY_INTERVAL]
- */
-enum {
-	TCA_TAPRIO_SCHED_UNSPEC,
-	TCA_TAPRIO_SCHED_ENTRY,
-	__TCA_TAPRIO_SCHED_MAX,
-};
-
-#define TCA_TAPRIO_SCHED_MAX (__TCA_TAPRIO_SCHED_MAX - 1)
-
-/* The format for the admin sched (dump only):
- * [TCA_TAPRIO_SCHED_ADMIN_SCHED]
- *   [TCA_TAPRIO_ATTR_SCHED_BASE_TIME]
- *   [TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST]
- *     [TCA_TAPRIO_ATTR_SCHED_ENTRY]
- *       [TCA_TAPRIO_ATTR_SCHED_ENTRY_CMD]
- *       [TCA_TAPRIO_ATTR_SCHED_ENTRY_GATES]
- *       [TCA_TAPRIO_ATTR_SCHED_ENTRY_INTERVAL]
- */
-
-#define TCA_TAPRIO_ATTR_FLAG_TXTIME_ASSIST	BIT(0)
-#define TCA_TAPRIO_ATTR_FLAG_FULL_OFFLOAD	BIT(1)
-
-enum {
-	TCA_TAPRIO_ATTR_UNSPEC,
-	TCA_TAPRIO_ATTR_PRIOMAP, /* struct tc_mqprio_qopt */
-	TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST, /* nested of entry */
-	TCA_TAPRIO_ATTR_SCHED_BASE_TIME, /* s64 */
-	TCA_TAPRIO_ATTR_SCHED_SINGLE_ENTRY, /* single entry */
-	TCA_TAPRIO_ATTR_SCHED_CLOCKID, /* s32 */
-	TCA_TAPRIO_PAD,
-	TCA_TAPRIO_ATTR_ADMIN_SCHED, /* The admin sched, only used in dump */
-	TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME, /* s64 */
-	TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION, /* s64 */
-	TCA_TAPRIO_ATTR_FLAGS, /* u32 */
-	TCA_TAPRIO_ATTR_TXTIME_DELAY, /* u32 */
-	__TCA_TAPRIO_ATTR_MAX,
-};
-
-#define TCA_TAPRIO_ATTR_MAX (__TCA_TAPRIO_ATTR_MAX - 1)
-
-#endif
diff --git a/include/uapi/linux/posix_types.h b/include/uapi/linux/posix_types.h
deleted file mode 100644
index 9a7a740..0000000
--- a/include/uapi/linux/posix_types.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_POSIX_TYPES_H
-#define _LINUX_POSIX_TYPES_H
-
-#include <linux/stddef.h>
-
-/*
- * This allows for 1024 file descriptors: if NR_OPEN is ever grown
- * beyond that you'll have to change this too. But 1024 fd's seem to be
- * enough even for such "real" unices like OSF/1, so hopefully this is
- * one limit that doesn't have to be changed [again].
- *
- * Note that POSIX wants the FD_CLEAR(fd,fdsetp) defines to be in
- * <sys/time.h> (and thus <linux/time.h>) - but this is a more logical
- * place for them. Solved by having dummy defines in <sys/time.h>.
- */
-
-/*
- * This macro may have been defined in <gnu/types.h>. But we always
- * use the one here.
- */
-#undef __FD_SETSIZE
-#define __FD_SETSIZE	1024
-
-typedef struct {
-	unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))];
-} __kernel_fd_set;
-
-/* Type of a signal handler.  */
-typedef void (*__kernel_sighandler_t)(int);
-
-/* Type of a SYSV IPC key.  */
-typedef int __kernel_key_t;
-typedef int __kernel_mqd_t;
-
-#include <asm/posix_types.h>
-
-#endif /* _LINUX_POSIX_TYPES_H */
diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h
deleted file mode 100644
index 0d4c150..0000000
--- a/include/uapi/linux/sctp.h
+++ /dev/null
@@ -1,1174 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
-/* SCTP kernel implementation
- * (C) Copyright IBM Corp. 2001, 2004
- * Copyright (c) 1999-2000 Cisco, Inc.
- * Copyright (c) 1999-2001 Motorola, Inc.
- * Copyright (c) 2002 Intel Corp.
- *
- * This file is part of the SCTP kernel implementation
- *
- * This header represents the structures and constants needed to support
- * the SCTP Extension to the Sockets API.
- *
- * This SCTP implementation is free software;
- * you can redistribute it and/or modify it under the terms of
- * the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This SCTP implementation 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 GNU CC; see the file COPYING.  If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * Please send any bug reports or fixes you make to the
- * email address(es):
- *    lksctp developers <linux-sctp@vger.kernel.org>
- *
- * Or submit a bug report through the following website:
- *    http://www.sf.net/projects/lksctp
- *
- * Written or modified by:
- *    La Monte H.P. Yarroll    <piggy@acm.org>
- *    R. Stewart               <randall@sctp.chicago.il.us>
- *    K. Morneau               <kmorneau@cisco.com>
- *    Q. Xie                   <qxie1@email.mot.com>
- *    Karl Knutson             <karl@athena.chicago.il.us>
- *    Jon Grimm                <jgrimm@us.ibm.com>
- *    Daisy Chang              <daisyc@us.ibm.com>
- *    Ryan Layer               <rmlayer@us.ibm.com>
- *    Ardelle Fan              <ardelle.fan@intel.com>
- *    Sridhar Samudrala        <sri@us.ibm.com>
- *    Inaky Perez-Gonzalez     <inaky.gonzalez@intel.com>
- *    Vlad Yasevich            <vladislav.yasevich@hp.com>
- *
- * Any bugs reported given to us we will try to fix... any fixes shared will
- * be incorporated into the next SCTP release.
- */
-
-#ifndef _SCTP_H
-#define _SCTP_H
-
-#include <linux/types.h>
-#include <linux/socket.h>
-
-typedef __s32 sctp_assoc_t;
-
-#define SCTP_FUTURE_ASSOC	0
-#define SCTP_CURRENT_ASSOC	1
-#define SCTP_ALL_ASSOC		2
-
-/* The following symbols come from the Sockets API Extensions for
- * SCTP <draft-ietf-tsvwg-sctpsocket-07.txt>.
- */
-#define SCTP_RTOINFO	0
-#define SCTP_ASSOCINFO  1
-#define SCTP_INITMSG	2
-#define SCTP_NODELAY	3		/* Get/set nodelay option. */
-#define SCTP_AUTOCLOSE	4
-#define SCTP_SET_PEER_PRIMARY_ADDR 5
-#define SCTP_PRIMARY_ADDR	6
-#define SCTP_ADAPTATION_LAYER	7
-#define SCTP_DISABLE_FRAGMENTS	8
-#define SCTP_PEER_ADDR_PARAMS	9
-#define SCTP_DEFAULT_SEND_PARAM	10
-#define SCTP_EVENTS	11
-#define SCTP_I_WANT_MAPPED_V4_ADDR 12	/* Turn on/off mapped v4 addresses  */
-#define SCTP_MAXSEG	13		/* Get/set maximum fragment. */
-#define SCTP_STATUS	14
-#define SCTP_GET_PEER_ADDR_INFO	15
-#define SCTP_DELAYED_ACK_TIME	16
-#define SCTP_DELAYED_ACK SCTP_DELAYED_ACK_TIME
-#define SCTP_DELAYED_SACK SCTP_DELAYED_ACK_TIME
-#define SCTP_CONTEXT	17
-#define SCTP_FRAGMENT_INTERLEAVE	18
-#define SCTP_PARTIAL_DELIVERY_POINT	19 /* Set/Get partial delivery point */
-#define SCTP_MAX_BURST	20		/* Set/Get max burst */
-#define SCTP_AUTH_CHUNK	21	/* Set only: add a chunk type to authenticate */
-#define SCTP_HMAC_IDENT	22
-#define SCTP_AUTH_KEY	23
-#define SCTP_AUTH_ACTIVE_KEY	24
-#define SCTP_AUTH_DELETE_KEY	25
-#define SCTP_PEER_AUTH_CHUNKS	26	/* Read only */
-#define SCTP_LOCAL_AUTH_CHUNKS	27	/* Read only */
-#define SCTP_GET_ASSOC_NUMBER	28	/* Read only */
-#define SCTP_GET_ASSOC_ID_LIST	29	/* Read only */
-#define SCTP_AUTO_ASCONF       30
-#define SCTP_PEER_ADDR_THLDS	31
-#define SCTP_RECVRCVINFO	32
-#define SCTP_RECVNXTINFO	33
-#define SCTP_DEFAULT_SNDINFO	34
-#define SCTP_AUTH_DEACTIVATE_KEY	35
-#define SCTP_REUSE_PORT		36
-
-/* Internal Socket Options. Some of the sctp library functions are
- * implemented using these socket options.
- */
-#define SCTP_SOCKOPT_BINDX_ADD	100	/* BINDX requests for adding addrs */
-#define SCTP_SOCKOPT_BINDX_REM	101	/* BINDX requests for removing addrs. */
-#define SCTP_SOCKOPT_PEELOFF	102	/* peel off association. */
-/* Options 104-106 are deprecated and removed. Do not use this space */
-#define SCTP_SOCKOPT_CONNECTX_OLD	107	/* CONNECTX old requests. */
-#define SCTP_GET_PEER_ADDRS	108		/* Get all peer address. */
-#define SCTP_GET_LOCAL_ADDRS	109		/* Get all local address. */
-#define SCTP_SOCKOPT_CONNECTX	110		/* CONNECTX requests. */
-#define SCTP_SOCKOPT_CONNECTX3	111	/* CONNECTX requests (updated) */
-#define SCTP_GET_ASSOC_STATS	112	/* Read only */
-#define SCTP_PR_SUPPORTED	113
-#define SCTP_DEFAULT_PRINFO	114
-#define SCTP_PR_ASSOC_STATUS	115
-#define SCTP_PR_STREAM_STATUS	116
-#define SCTP_RECONFIG_SUPPORTED	117
-#define SCTP_ENABLE_STREAM_RESET	118
-#define SCTP_RESET_STREAMS	119
-#define SCTP_RESET_ASSOC	120
-#define SCTP_ADD_STREAMS	121
-#define SCTP_SOCKOPT_PEELOFF_FLAGS 122
-#define SCTP_STREAM_SCHEDULER	123
-#define SCTP_STREAM_SCHEDULER_VALUE	124
-#define SCTP_INTERLEAVING_SUPPORTED	125
-#define SCTP_SENDMSG_CONNECT	126
-#define SCTP_EVENT	127
-#define SCTP_ASCONF_SUPPORTED	128
-#define SCTP_AUTH_SUPPORTED	129
-#define SCTP_ECN_SUPPORTED	130
-
-/* PR-SCTP policies */
-#define SCTP_PR_SCTP_NONE	0x0000
-#define SCTP_PR_SCTP_TTL	0x0010
-#define SCTP_PR_SCTP_RTX	0x0020
-#define SCTP_PR_SCTP_PRIO	0x0030
-#define SCTP_PR_SCTP_MAX	SCTP_PR_SCTP_PRIO
-#define SCTP_PR_SCTP_MASK	0x0030
-
-#define __SCTP_PR_INDEX(x)	((x >> 4) - 1)
-#define SCTP_PR_INDEX(x)	__SCTP_PR_INDEX(SCTP_PR_SCTP_ ## x)
-
-#define SCTP_PR_POLICY(x)	((x) & SCTP_PR_SCTP_MASK)
-#define SCTP_PR_SET_POLICY(flags, x)	\
-	do {				\
-		flags &= ~SCTP_PR_SCTP_MASK;	\
-		flags |= x;		\
-	} while (0)
-
-#define SCTP_PR_TTL_ENABLED(x)	(SCTP_PR_POLICY(x) == SCTP_PR_SCTP_TTL)
-#define SCTP_PR_RTX_ENABLED(x)	(SCTP_PR_POLICY(x) == SCTP_PR_SCTP_RTX)
-#define SCTP_PR_PRIO_ENABLED(x)	(SCTP_PR_POLICY(x) == SCTP_PR_SCTP_PRIO)
-
-/* For enable stream reset */
-#define SCTP_ENABLE_RESET_STREAM_REQ	0x01
-#define SCTP_ENABLE_RESET_ASSOC_REQ	0x02
-#define SCTP_ENABLE_CHANGE_ASSOC_REQ	0x04
-#define SCTP_ENABLE_STRRESET_MASK	0x07
-
-#define SCTP_STREAM_RESET_INCOMING	0x01
-#define SCTP_STREAM_RESET_OUTGOING	0x02
-
-/* These are bit fields for msghdr->msg_flags.  See section 5.1.  */
-/* On user space Linux, these live in <bits/socket.h> as an enum.  */
-enum sctp_msg_flags {
-	MSG_NOTIFICATION = 0x8000,
-#define MSG_NOTIFICATION MSG_NOTIFICATION
-};
-
-/* 5.3.1 SCTP Initiation Structure (SCTP_INIT)
- *
- *   This cmsghdr structure provides information for initializing new
- *   SCTP associations with sendmsg().  The SCTP_INITMSG socket option
- *   uses this same data structure.  This structure is not used for
- *   recvmsg().
- *
- *   cmsg_level    cmsg_type      cmsg_data[]
- *   ------------  ------------   ----------------------
- *   IPPROTO_SCTP  SCTP_INIT      struct sctp_initmsg
- */
-struct sctp_initmsg {
-	__u16 sinit_num_ostreams;
-	__u16 sinit_max_instreams;
-	__u16 sinit_max_attempts;
-	__u16 sinit_max_init_timeo;
-};
-
-/* 5.3.2 SCTP Header Information Structure (SCTP_SNDRCV)
- *
- *   This cmsghdr structure specifies SCTP options for sendmsg() and
- *   describes SCTP header information about a received message through
- *   recvmsg().
- *
- *   cmsg_level    cmsg_type      cmsg_data[]
- *   ------------  ------------   ----------------------
- *   IPPROTO_SCTP  SCTP_SNDRCV    struct sctp_sndrcvinfo
- */
-struct sctp_sndrcvinfo {
-	__u16 sinfo_stream;
-	__u16 sinfo_ssn;
-	__u16 sinfo_flags;
-	__u32 sinfo_ppid;
-	__u32 sinfo_context;
-	__u32 sinfo_timetolive;
-	__u32 sinfo_tsn;
-	__u32 sinfo_cumtsn;
-	sctp_assoc_t sinfo_assoc_id;
-};
-
-/* 5.3.4 SCTP Send Information Structure (SCTP_SNDINFO)
- *
- *   This cmsghdr structure specifies SCTP options for sendmsg().
- *
- *   cmsg_level    cmsg_type      cmsg_data[]
- *   ------------  ------------   -------------------
- *   IPPROTO_SCTP  SCTP_SNDINFO   struct sctp_sndinfo
- */
-struct sctp_sndinfo {
-	__u16 snd_sid;
-	__u16 snd_flags;
-	__u32 snd_ppid;
-	__u32 snd_context;
-	sctp_assoc_t snd_assoc_id;
-};
-
-/* 5.3.5 SCTP Receive Information Structure (SCTP_RCVINFO)
- *
- *   This cmsghdr structure describes SCTP receive information
- *   about a received message through recvmsg().
- *
- *   cmsg_level    cmsg_type      cmsg_data[]
- *   ------------  ------------   -------------------
- *   IPPROTO_SCTP  SCTP_RCVINFO   struct sctp_rcvinfo
- */
-struct sctp_rcvinfo {
-	__u16 rcv_sid;
-	__u16 rcv_ssn;
-	__u16 rcv_flags;
-	__u32 rcv_ppid;
-	__u32 rcv_tsn;
-	__u32 rcv_cumtsn;
-	__u32 rcv_context;
-	sctp_assoc_t rcv_assoc_id;
-};
-
-/* 5.3.6 SCTP Next Receive Information Structure (SCTP_NXTINFO)
- *
- *   This cmsghdr structure describes SCTP receive information
- *   of the next message that will be delivered through recvmsg()
- *   if this information is already available when delivering
- *   the current message.
- *
- *   cmsg_level    cmsg_type      cmsg_data[]
- *   ------------  ------------   -------------------
- *   IPPROTO_SCTP  SCTP_NXTINFO   struct sctp_nxtinfo
- */
-struct sctp_nxtinfo {
-	__u16 nxt_sid;
-	__u16 nxt_flags;
-	__u32 nxt_ppid;
-	__u32 nxt_length;
-	sctp_assoc_t nxt_assoc_id;
-};
-
-/* 5.3.7 SCTP PR-SCTP Information Structure (SCTP_PRINFO)
- *
- *   This cmsghdr structure specifies SCTP options for sendmsg().
- *
- *   cmsg_level    cmsg_type      cmsg_data[]
- *   ------------  ------------   -------------------
- *   IPPROTO_SCTP  SCTP_PRINFO    struct sctp_prinfo
- */
-struct sctp_prinfo {
-	__u16 pr_policy;
-	__u32 pr_value;
-};
-
-/* 5.3.8 SCTP AUTH Information Structure (SCTP_AUTHINFO)
- *
- *   This cmsghdr structure specifies SCTP options for sendmsg().
- *
- *   cmsg_level    cmsg_type      cmsg_data[]
- *   ------------  ------------   -------------------
- *   IPPROTO_SCTP  SCTP_AUTHINFO  struct sctp_authinfo
- */
-struct sctp_authinfo {
-	__u16 auth_keynumber;
-};
-
-/*
- *  sinfo_flags: 16 bits (unsigned integer)
- *
- *   This field may contain any of the following flags and is composed of
- *   a bitwise OR of these values.
- */
-enum sctp_sinfo_flags {
-	SCTP_UNORDERED		= (1 << 0), /* Send/receive message unordered. */
-	SCTP_ADDR_OVER		= (1 << 1), /* Override the primary destination. */
-	SCTP_ABORT		= (1 << 2), /* Send an ABORT message to the peer. */
-	SCTP_SACK_IMMEDIATELY	= (1 << 3), /* SACK should be sent without delay. */
-	/* 2 bits here have been used by SCTP_PR_SCTP_MASK */
-	SCTP_SENDALL		= (1 << 6),
-	SCTP_PR_SCTP_ALL	= (1 << 7),
-	SCTP_NOTIFICATION	= MSG_NOTIFICATION, /* Next message is not user msg but notification. */
-	SCTP_EOF		= MSG_FIN,  /* Initiate graceful shutdown process. */
-};
-
-typedef union {
-	__u8   			raw;
-	struct sctp_initmsg	init;
-	struct sctp_sndrcvinfo	sndrcv;
-} sctp_cmsg_data_t;
-
-/* These are cmsg_types.  */
-typedef enum sctp_cmsg_type {
-	SCTP_INIT,		/* 5.2.1 SCTP Initiation Structure */
-#define SCTP_INIT	SCTP_INIT
-	SCTP_SNDRCV,		/* 5.2.2 SCTP Header Information Structure */
-#define SCTP_SNDRCV	SCTP_SNDRCV
-	SCTP_SNDINFO,		/* 5.3.4 SCTP Send Information Structure */
-#define SCTP_SNDINFO	SCTP_SNDINFO
-	SCTP_RCVINFO,		/* 5.3.5 SCTP Receive Information Structure */
-#define SCTP_RCVINFO	SCTP_RCVINFO
-	SCTP_NXTINFO,		/* 5.3.6 SCTP Next Receive Information Structure */
-#define SCTP_NXTINFO	SCTP_NXTINFO
-	SCTP_PRINFO,		/* 5.3.7 SCTP PR-SCTP Information Structure */
-#define SCTP_PRINFO	SCTP_PRINFO
-	SCTP_AUTHINFO,		/* 5.3.8 SCTP AUTH Information Structure */
-#define SCTP_AUTHINFO	SCTP_AUTHINFO
-	SCTP_DSTADDRV4,		/* 5.3.9 SCTP Destination IPv4 Address Structure */
-#define SCTP_DSTADDRV4	SCTP_DSTADDRV4
-	SCTP_DSTADDRV6,		/* 5.3.10 SCTP Destination IPv6 Address Structure */
-#define SCTP_DSTADDRV6	SCTP_DSTADDRV6
-} sctp_cmsg_t;
-
-/*
- * 5.3.1.1 SCTP_ASSOC_CHANGE
- *
- *   Communication notifications inform the ULP that an SCTP association
- *   has either begun or ended. The identifier for a new association is
- *   provided by this notificaion. The notification information has the
- *   following format:
- *
- */
-struct sctp_assoc_change {
-	__u16 sac_type;
-	__u16 sac_flags;
-	__u32 sac_length;
-	__u16 sac_state;
-	__u16 sac_error;
-	__u16 sac_outbound_streams;
-	__u16 sac_inbound_streams;
-	sctp_assoc_t sac_assoc_id;
-	__u8 sac_info[0];
-};
-
-/*
- *   sac_state: 32 bits (signed integer)
- *
- *   This field holds one of a number of values that communicate the
- *   event that happened to the association.  They include:
- *
- *   Note:  The following state names deviate from the API draft as
- *   the names clash too easily with other kernel symbols.
- */
-enum sctp_sac_state {
-	SCTP_COMM_UP,
-	SCTP_COMM_LOST,
-	SCTP_RESTART,
-	SCTP_SHUTDOWN_COMP,
-	SCTP_CANT_STR_ASSOC,
-};
-
-/*
- * 5.3.1.2 SCTP_PEER_ADDR_CHANGE
- *
- *   When a destination address on a multi-homed peer encounters a change
- *   an interface details event is sent.  The information has the
- *   following structure:
- */
-struct sctp_paddr_change {
-	__u16 spc_type;
-	__u16 spc_flags;
-	__u32 spc_length;
-	struct sockaddr_storage spc_aaddr;
-	int spc_state;
-	int spc_error;
-	sctp_assoc_t spc_assoc_id;
-} __attribute__((packed, aligned(4)));
-
-/*
- *    spc_state:  32 bits (signed integer)
- *
- *   This field holds one of a number of values that communicate the
- *   event that happened to the address.  They include:
- */
-enum sctp_spc_state {
-	SCTP_ADDR_AVAILABLE,
-	SCTP_ADDR_UNREACHABLE,
-	SCTP_ADDR_REMOVED,
-	SCTP_ADDR_ADDED,
-	SCTP_ADDR_MADE_PRIM,
-	SCTP_ADDR_CONFIRMED,
-};
-
-
-/*
- * 5.3.1.3 SCTP_REMOTE_ERROR
- *
- *   A remote peer may send an Operational Error message to its peer.
- *   This message indicates a variety of error conditions on an
- *   association. The entire error TLV as it appears on the wire is
- *   included in a SCTP_REMOTE_ERROR event.  Please refer to the SCTP
- *   specification [SCTP] and any extensions for a list of possible
- *   error formats. SCTP error TLVs have the format:
- */
-struct sctp_remote_error {
-	__u16 sre_type;
-	__u16 sre_flags;
-	__u32 sre_length;
-	__be16 sre_error;
-	sctp_assoc_t sre_assoc_id;
-	__u8 sre_data[0];
-};
-
-
-/*
- * 5.3.1.4 SCTP_SEND_FAILED
- *
- *   If SCTP cannot deliver a message it may return the message as a
- *   notification.
- */
-struct sctp_send_failed {
-	__u16 ssf_type;
-	__u16 ssf_flags;
-	__u32 ssf_length;
-	__u32 ssf_error;
-	struct sctp_sndrcvinfo ssf_info;
-	sctp_assoc_t ssf_assoc_id;
-	__u8 ssf_data[0];
-};
-
-/*
- *   ssf_flags: 16 bits (unsigned integer)
- *
- *   The flag value will take one of the following values
- *
- *   SCTP_DATA_UNSENT  - Indicates that the data was never put on
- *                       the wire.
- *
- *   SCTP_DATA_SENT    - Indicates that the data was put on the wire.
- *                       Note that this does not necessarily mean that the
- *                       data was (or was not) successfully delivered.
- */
-enum sctp_ssf_flags {
-	SCTP_DATA_UNSENT,
-	SCTP_DATA_SENT,
-};
-
-/*
- * 5.3.1.5 SCTP_SHUTDOWN_EVENT
- *
- *   When a peer sends a SHUTDOWN, SCTP delivers this notification to
- *   inform the application that it should cease sending data.
- */
-struct sctp_shutdown_event {
-	__u16 sse_type;
-	__u16 sse_flags;
-	__u32 sse_length;
-	sctp_assoc_t sse_assoc_id;
-};
-
-/*
- * 5.3.1.6 SCTP_ADAPTATION_INDICATION
- *
- *   When a peer sends a Adaptation Layer Indication parameter , SCTP
- *   delivers this notification to inform the application
- *   that of the peers requested adaptation layer.
- */
-struct sctp_adaptation_event {
-	__u16 sai_type;
-	__u16 sai_flags;
-	__u32 sai_length;
-	__u32 sai_adaptation_ind;
-	sctp_assoc_t sai_assoc_id;
-};
-
-/*
- * 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT
- *
- *   When a receiver is engaged in a partial delivery of a
- *   message this notification will be used to indicate
- *   various events.
- */
-struct sctp_pdapi_event {
-	__u16 pdapi_type;
-	__u16 pdapi_flags;
-	__u32 pdapi_length;
-	__u32 pdapi_indication;
-	sctp_assoc_t pdapi_assoc_id;
-	__u32 pdapi_stream;
-	__u32 pdapi_seq;
-};
-
-enum { SCTP_PARTIAL_DELIVERY_ABORTED=0, };
-
-/*
- * 5.3.1.8.  SCTP_AUTHENTICATION_EVENT
- *
- *  When a receiver is using authentication this message will provide
- *  notifications regarding new keys being made active as well as errors.
- */
-struct sctp_authkey_event {
-	__u16 auth_type;
-	__u16 auth_flags;
-	__u32 auth_length;
-	__u16 auth_keynumber;
-	__u16 auth_altkeynumber;
-	__u32 auth_indication;
-	sctp_assoc_t auth_assoc_id;
-};
-
-enum {
-	SCTP_AUTH_NEW_KEY,
-#define	SCTP_AUTH_NEWKEY	SCTP_AUTH_NEW_KEY /* compatible with before */
-	SCTP_AUTH_FREE_KEY,
-	SCTP_AUTH_NO_AUTH,
-};
-
-/*
- * 6.1.9. SCTP_SENDER_DRY_EVENT
- *
- * When the SCTP stack has no more user data to send or retransmit, this
- * notification is given to the user. Also, at the time when a user app
- * subscribes to this event, if there is no data to be sent or
- * retransmit, the stack will immediately send up this notification.
- */
-struct sctp_sender_dry_event {
-	__u16 sender_dry_type;
-	__u16 sender_dry_flags;
-	__u32 sender_dry_length;
-	sctp_assoc_t sender_dry_assoc_id;
-};
-
-#define SCTP_STREAM_RESET_INCOMING_SSN	0x0001
-#define SCTP_STREAM_RESET_OUTGOING_SSN	0x0002
-#define SCTP_STREAM_RESET_DENIED	0x0004
-#define SCTP_STREAM_RESET_FAILED	0x0008
-struct sctp_stream_reset_event {
-	__u16 strreset_type;
-	__u16 strreset_flags;
-	__u32 strreset_length;
-	sctp_assoc_t strreset_assoc_id;
-	__u16 strreset_stream_list[];
-};
-
-#define SCTP_ASSOC_RESET_DENIED		0x0004
-#define SCTP_ASSOC_RESET_FAILED		0x0008
-struct sctp_assoc_reset_event {
-	__u16 assocreset_type;
-	__u16 assocreset_flags;
-	__u32 assocreset_length;
-	sctp_assoc_t assocreset_assoc_id;
-	__u32 assocreset_local_tsn;
-	__u32 assocreset_remote_tsn;
-};
-
-#define SCTP_ASSOC_CHANGE_DENIED	0x0004
-#define SCTP_ASSOC_CHANGE_FAILED	0x0008
-#define SCTP_STREAM_CHANGE_DENIED	SCTP_ASSOC_CHANGE_DENIED
-#define SCTP_STREAM_CHANGE_FAILED	SCTP_ASSOC_CHANGE_FAILED
-struct sctp_stream_change_event {
-	__u16 strchange_type;
-	__u16 strchange_flags;
-	__u32 strchange_length;
-	sctp_assoc_t strchange_assoc_id;
-	__u16 strchange_instrms;
-	__u16 strchange_outstrms;
-};
-
-/*
- * Described in Section 7.3
- *   Ancillary Data and Notification Interest Options
- */
-struct sctp_event_subscribe {
-	__u8 sctp_data_io_event;
-	__u8 sctp_association_event;
-	__u8 sctp_address_event;
-	__u8 sctp_send_failure_event;
-	__u8 sctp_peer_error_event;
-	__u8 sctp_shutdown_event;
-	__u8 sctp_partial_delivery_event;
-	__u8 sctp_adaptation_layer_event;
-	__u8 sctp_authentication_event;
-	__u8 sctp_sender_dry_event;
-	__u8 sctp_stream_reset_event;
-	__u8 sctp_assoc_reset_event;
-	__u8 sctp_stream_change_event;
-};
-
-/*
- * 5.3.1 SCTP Notification Structure
- *
- *   The notification structure is defined as the union of all
- *   notification types.
- *
- */
-union sctp_notification {
-	struct {
-		__u16 sn_type;             /* Notification type. */
-		__u16 sn_flags;
-		__u32 sn_length;
-	} sn_header;
-	struct sctp_assoc_change sn_assoc_change;
-	struct sctp_paddr_change sn_paddr_change;
-	struct sctp_remote_error sn_remote_error;
-	struct sctp_send_failed sn_send_failed;
-	struct sctp_shutdown_event sn_shutdown_event;
-	struct sctp_adaptation_event sn_adaptation_event;
-	struct sctp_pdapi_event sn_pdapi_event;
-	struct sctp_authkey_event sn_authkey_event;
-	struct sctp_sender_dry_event sn_sender_dry_event;
-	struct sctp_stream_reset_event sn_strreset_event;
-	struct sctp_assoc_reset_event sn_assocreset_event;
-	struct sctp_stream_change_event sn_strchange_event;
-};
-
-/* Section 5.3.1
- * All standard values for sn_type flags are greater than 2^15.
- * Values from 2^15 and down are reserved.
- */
-
-enum sctp_sn_type {
-	SCTP_SN_TYPE_BASE	= (1<<15),
-	SCTP_DATA_IO_EVENT	= SCTP_SN_TYPE_BASE,
-#define SCTP_DATA_IO_EVENT		SCTP_DATA_IO_EVENT
-	SCTP_ASSOC_CHANGE,
-#define SCTP_ASSOC_CHANGE		SCTP_ASSOC_CHANGE
-	SCTP_PEER_ADDR_CHANGE,
-#define SCTP_PEER_ADDR_CHANGE		SCTP_PEER_ADDR_CHANGE
-	SCTP_SEND_FAILED,
-#define SCTP_SEND_FAILED		SCTP_SEND_FAILED
-	SCTP_REMOTE_ERROR,
-#define SCTP_REMOTE_ERROR		SCTP_REMOTE_ERROR
-	SCTP_SHUTDOWN_EVENT,
-#define SCTP_SHUTDOWN_EVENT		SCTP_SHUTDOWN_EVENT
-	SCTP_PARTIAL_DELIVERY_EVENT,
-#define SCTP_PARTIAL_DELIVERY_EVENT	SCTP_PARTIAL_DELIVERY_EVENT
-	SCTP_ADAPTATION_INDICATION,
-#define SCTP_ADAPTATION_INDICATION	SCTP_ADAPTATION_INDICATION
-	SCTP_AUTHENTICATION_EVENT,
-#define SCTP_AUTHENTICATION_INDICATION	SCTP_AUTHENTICATION_EVENT
-	SCTP_SENDER_DRY_EVENT,
-#define SCTP_SENDER_DRY_EVENT		SCTP_SENDER_DRY_EVENT
-	SCTP_STREAM_RESET_EVENT,
-#define SCTP_STREAM_RESET_EVENT		SCTP_STREAM_RESET_EVENT
-	SCTP_ASSOC_RESET_EVENT,
-#define SCTP_ASSOC_RESET_EVENT		SCTP_ASSOC_RESET_EVENT
-	SCTP_STREAM_CHANGE_EVENT,
-#define SCTP_STREAM_CHANGE_EVENT	SCTP_STREAM_CHANGE_EVENT
-	SCTP_SN_TYPE_MAX	= SCTP_STREAM_CHANGE_EVENT,
-#define SCTP_SN_TYPE_MAX		SCTP_SN_TYPE_MAX
-};
-
-/* Notification error codes used to fill up the error fields in some
- * notifications.
- * SCTP_PEER_ADDRESS_CHAGE 	: spc_error
- * SCTP_ASSOC_CHANGE		: sac_error
- * These names should be potentially included in the draft 04 of the SCTP
- * sockets API specification.
- */
-typedef enum sctp_sn_error {
-	SCTP_FAILED_THRESHOLD,
-	SCTP_RECEIVED_SACK,
-	SCTP_HEARTBEAT_SUCCESS,
-	SCTP_RESPONSE_TO_USER_REQ,
-	SCTP_INTERNAL_ERROR,
-	SCTP_SHUTDOWN_GUARD_EXPIRES,
-	SCTP_PEER_FAULTY,
-} sctp_sn_error_t;
-
-/*
- * 7.1.1 Retransmission Timeout Parameters (SCTP_RTOINFO)
- *
- *   The protocol parameters used to initialize and bound retransmission
- *   timeout (RTO) are tunable.  See [SCTP] for more information on how
- *   these parameters are used in RTO calculation.
- */
-struct sctp_rtoinfo {
-	sctp_assoc_t	srto_assoc_id;
-	__u32		srto_initial;
-	__u32		srto_max;
-	__u32		srto_min;
-};
-
-/*
- * 7.1.2 Association Parameters (SCTP_ASSOCINFO)
- *
- *   This option is used to both examine and set various association and
- *   endpoint parameters.
- */
-struct sctp_assocparams {
-	sctp_assoc_t	sasoc_assoc_id;
-	__u16		sasoc_asocmaxrxt;
-	__u16		sasoc_number_peer_destinations;
-	__u32		sasoc_peer_rwnd;
-	__u32		sasoc_local_rwnd;
-	__u32		sasoc_cookie_life;
-};
-
-/*
- * 7.1.9 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR)
- *
- *  Requests that the peer mark the enclosed address as the association
- *  primary. The enclosed address must be one of the association's
- *  locally bound addresses. The following structure is used to make a
- *   set primary request:
- */
-struct sctp_setpeerprim {
-	sctp_assoc_t            sspp_assoc_id;
-	struct sockaddr_storage sspp_addr;
-} __attribute__((packed, aligned(4)));
-
-/*
- * 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
- *
- *  Requests that the local SCTP stack use the enclosed peer address as
- *  the association primary. The enclosed address must be one of the
- *  association peer's addresses. The following structure is used to
- *  make a set peer primary request:
- */
-struct sctp_prim {
-	sctp_assoc_t            ssp_assoc_id;
-	struct sockaddr_storage ssp_addr;
-} __attribute__((packed, aligned(4)));
-
-/* For backward compatibility use, define the old name too */
-#define sctp_setprim	sctp_prim
-
-/*
- * 7.1.11 Set Adaptation Layer Indicator (SCTP_ADAPTATION_LAYER)
- *
- * Requests that the local endpoint set the specified Adaptation Layer
- * Indication parameter for all future INIT and INIT-ACK exchanges.
- */
-struct sctp_setadaptation {
-	__u32	ssb_adaptation_ind;
-};
-
-/*
- * 7.1.13 Peer Address Parameters  (SCTP_PEER_ADDR_PARAMS)
- *
- *   Applications can enable or disable heartbeats for any peer address
- *   of an association, modify an address's heartbeat interval, force a
- *   heartbeat to be sent immediately, and adjust the address's maximum
- *   number of retransmissions sent before an address is considered
- *   unreachable. The following structure is used to access and modify an
- *   address's parameters:
- */
-enum  sctp_spp_flags {
-	SPP_HB_ENABLE = 1<<0,		/*Enable heartbeats*/
-	SPP_HB_DISABLE = 1<<1,		/*Disable heartbeats*/
-	SPP_HB = SPP_HB_ENABLE | SPP_HB_DISABLE,
-	SPP_HB_DEMAND = 1<<2,		/*Send heartbeat immediately*/
-	SPP_PMTUD_ENABLE = 1<<3,	/*Enable PMTU discovery*/
-	SPP_PMTUD_DISABLE = 1<<4,	/*Disable PMTU discovery*/
-	SPP_PMTUD = SPP_PMTUD_ENABLE | SPP_PMTUD_DISABLE,
-	SPP_SACKDELAY_ENABLE = 1<<5,	/*Enable SACK*/
-	SPP_SACKDELAY_DISABLE = 1<<6,	/*Disable SACK*/
-	SPP_SACKDELAY = SPP_SACKDELAY_ENABLE | SPP_SACKDELAY_DISABLE,
-	SPP_HB_TIME_IS_ZERO = 1<<7,	/* Set HB delay to 0 */
-	SPP_IPV6_FLOWLABEL = 1<<8,
-	SPP_DSCP = 1<<9,
-};
-
-struct sctp_paddrparams {
-	sctp_assoc_t		spp_assoc_id;
-	struct sockaddr_storage	spp_address;
-	__u32			spp_hbinterval;
-	__u16			spp_pathmaxrxt;
-	__u32			spp_pathmtu;
-	__u32			spp_sackdelay;
-	__u32			spp_flags;
-	__u32			spp_ipv6_flowlabel;
-	__u8			spp_dscp;
-} __attribute__((packed, aligned(4)));
-
-/*
- * 7.1.18.  Add a chunk that must be authenticated (SCTP_AUTH_CHUNK)
- *
- * This set option adds a chunk type that the user is requesting to be
- * received only in an authenticated way.  Changes to the list of chunks
- * will only effect future associations on the socket.
- */
-struct sctp_authchunk {
-	__u8		sauth_chunk;
-};
-
-/*
- * 7.1.19.  Get or set the list of supported HMAC Identifiers (SCTP_HMAC_IDENT)
- *
- * This option gets or sets the list of HMAC algorithms that the local
- * endpoint requires the peer to use.
- */
-/* This here is only used by user space as is. It might not be a good idea
- * to export/reveal the whole structure with reserved fields etc.
- */
-enum {
-	SCTP_AUTH_HMAC_ID_SHA1 = 1,
-	SCTP_AUTH_HMAC_ID_SHA256 = 3,
-};
-
-struct sctp_hmacalgo {
-	__u32		shmac_num_idents;
-	__u16		shmac_idents[];
-};
-
-/* Sadly, user and kernel space have different names for
- * this structure member, so this is to not break anything.
- */
-#define shmac_number_of_idents	shmac_num_idents
-
-/*
- * 7.1.20.  Set a shared key (SCTP_AUTH_KEY)
- *
- * This option will set a shared secret key which is used to build an
- * association shared key.
- */
-struct sctp_authkey {
-	sctp_assoc_t	sca_assoc_id;
-	__u16		sca_keynumber;
-	__u16		sca_keylength;
-	__u8		sca_key[];
-};
-
-/*
- * 7.1.21.  Get or set the active shared key (SCTP_AUTH_ACTIVE_KEY)
- *
- * This option will get or set the active shared key to be used to build
- * the association shared key.
- */
-
-struct sctp_authkeyid {
-	sctp_assoc_t	scact_assoc_id;
-	__u16		scact_keynumber;
-};
-
-
-/*
- * 7.1.23.  Get or set delayed ack timer (SCTP_DELAYED_SACK)
- *
- * This option will effect the way delayed acks are performed.  This
- * option allows you to get or set the delayed ack time, in
- * milliseconds.  It also allows changing the delayed ack frequency.
- * Changing the frequency to 1 disables the delayed sack algorithm.  If
- * the assoc_id is 0, then this sets or gets the endpoints default
- * values.  If the assoc_id field is non-zero, then the set or get
- * effects the specified association for the one to many model (the
- * assoc_id field is ignored by the one to one model).  Note that if
- * sack_delay or sack_freq are 0 when setting this option, then the
- * current values will remain unchanged.
- */
-struct sctp_sack_info {
-	sctp_assoc_t	sack_assoc_id;
-	uint32_t	sack_delay;
-	uint32_t	sack_freq;
-};
-
-struct sctp_assoc_value {
-    sctp_assoc_t            assoc_id;
-    uint32_t                assoc_value;
-};
-
-struct sctp_stream_value {
-	sctp_assoc_t assoc_id;
-	uint16_t stream_id;
-	uint16_t stream_value;
-};
-
-/*
- * 7.2.2 Peer Address Information
- *
- *   Applications can retrieve information about a specific peer address
- *   of an association, including its reachability state, congestion
- *   window, and retransmission timer values.  This information is
- *   read-only. The following structure is used to access this
- *   information:
- */
-struct sctp_paddrinfo {
-	sctp_assoc_t		spinfo_assoc_id;
-	struct sockaddr_storage	spinfo_address;
-	__s32			spinfo_state;
-	__u32			spinfo_cwnd;
-	__u32			spinfo_srtt;
-	__u32			spinfo_rto;
-	__u32			spinfo_mtu;
-} __attribute__((packed, aligned(4)));
-
-/* Peer addresses's state. */
-/* UNKNOWN: Peer address passed by the upper layer in sendmsg or connect[x]
- * calls.
- * UNCONFIRMED: Peer address received in INIT/INIT-ACK address parameters.
- *              Not yet confirmed by a heartbeat and not available for data
- *		transfers.
- * ACTIVE : Peer address confirmed, active and available for data transfers.
- * INACTIVE: Peer address inactive and not available for data transfers.
- */
-enum sctp_spinfo_state {
-	SCTP_INACTIVE,
-	SCTP_PF,
-	SCTP_ACTIVE,
-	SCTP_UNCONFIRMED,
-	SCTP_UNKNOWN = 0xffff  /* Value used for transport state unknown */
-};
-
-/*
- * 7.2.1 Association Status (SCTP_STATUS)
- *
- *   Applications can retrieve current status information about an
- *   association, including association state, peer receiver window size,
- *   number of unacked data chunks, and number of data chunks pending
- *   receipt.  This information is read-only.  The following structure is
- *   used to access this information:
- */
-struct sctp_status {
-	sctp_assoc_t		sstat_assoc_id;
-	__s32			sstat_state;
-	__u32			sstat_rwnd;
-	__u16			sstat_unackdata;
-	__u16			sstat_penddata;
-	__u16			sstat_instrms;
-	__u16			sstat_outstrms;
-	__u32			sstat_fragmentation_point;
-	struct sctp_paddrinfo	sstat_primary;
-};
-
-/*
- * 7.2.3.  Get the list of chunks the peer requires to be authenticated
- *         (SCTP_PEER_AUTH_CHUNKS)
- *
- * This option gets a list of chunks for a specified association that
- * the peer requires to be received authenticated only.
- */
-struct sctp_authchunks {
-	sctp_assoc_t	gauth_assoc_id;
-	__u32		gauth_number_of_chunks;
-	uint8_t		gauth_chunks[];
-};
-
-/* The broken spelling has been released already in lksctp-tools header,
- * so don't break anyone, now that it's fixed.
- */
-#define guth_number_of_chunks	gauth_number_of_chunks
-
-/* Association states.  */
-enum sctp_sstat_state {
-	SCTP_EMPTY                = 0,
-	SCTP_CLOSED               = 1,
-	SCTP_COOKIE_WAIT          = 2,
-	SCTP_COOKIE_ECHOED        = 3,
-	SCTP_ESTABLISHED          = 4,
-	SCTP_SHUTDOWN_PENDING     = 5,
-	SCTP_SHUTDOWN_SENT        = 6,
-	SCTP_SHUTDOWN_RECEIVED    = 7,
-	SCTP_SHUTDOWN_ACK_SENT    = 8,
-};
-
-/*
- * 8.2.6. Get the Current Identifiers of Associations
- *        (SCTP_GET_ASSOC_ID_LIST)
- *
- * This option gets the current list of SCTP association identifiers of
- * the SCTP associations handled by a one-to-many style socket.
- */
-struct sctp_assoc_ids {
-	__u32		gaids_number_of_ids;
-	sctp_assoc_t	gaids_assoc_id[];
-};
-
-/*
- * 8.3, 8.5 get all peer/local addresses in an association.
- * This parameter struct is used by SCTP_GET_PEER_ADDRS and
- * SCTP_GET_LOCAL_ADDRS socket options used internally to implement
- * sctp_getpaddrs() and sctp_getladdrs() API.
- */
-struct sctp_getaddrs_old {
-	sctp_assoc_t            assoc_id;
-	int			addr_num;
-	struct sockaddr		*addrs;
-};
-
-struct sctp_getaddrs {
-	sctp_assoc_t		assoc_id; /*input*/
-	__u32			addr_num; /*output*/
-	__u8			addrs[0]; /*output, variable size*/
-};
-
-/* A socket user request obtained via SCTP_GET_ASSOC_STATS that retrieves
- * association stats. All stats are counts except sas_maxrto and
- * sas_obs_rto_ipaddr. maxrto is the max observed rto + transport since
- * the last call. Will return 0 when RTO was not update since last call
- */
-struct sctp_assoc_stats {
-	sctp_assoc_t	sas_assoc_id;    /* Input */
-					 /* Transport of observed max RTO */
-	struct sockaddr_storage sas_obs_rto_ipaddr;
-	__u64		sas_maxrto;      /* Maximum Observed RTO for period */
-	__u64		sas_isacks;	 /* SACKs received */
-	__u64		sas_osacks;	 /* SACKs sent */
-	__u64		sas_opackets;	 /* Packets sent */
-	__u64		sas_ipackets;	 /* Packets received */
-	__u64		sas_rtxchunks;   /* Retransmitted Chunks */
-	__u64		sas_outofseqtsns;/* TSN received > next expected */
-	__u64		sas_idupchunks;  /* Dups received (ordered+unordered) */
-	__u64		sas_gapcnt;      /* Gap Acknowledgements Received */
-	__u64		sas_ouodchunks;  /* Unordered data chunks sent */
-	__u64		sas_iuodchunks;  /* Unordered data chunks received */
-	__u64		sas_oodchunks;	 /* Ordered data chunks sent */
-	__u64		sas_iodchunks;	 /* Ordered data chunks received */
-	__u64		sas_octrlchunks; /* Control chunks sent */
-	__u64		sas_ictrlchunks; /* Control chunks received */
-};
-
-/*
- * 8.1 sctp_bindx()
- *
- * The flags parameter is formed from the bitwise OR of zero or more of the
- * following currently defined flags:
- */
-#define SCTP_BINDX_ADD_ADDR 0x01
-#define SCTP_BINDX_REM_ADDR 0x02
-
-/* This is the structure that is passed as an argument(optval) to
- * getsockopt(SCTP_SOCKOPT_PEELOFF).
- */
-typedef struct {
-	sctp_assoc_t associd;
-	int sd;
-} sctp_peeloff_arg_t;
-
-typedef struct {
-	sctp_peeloff_arg_t p_arg;
-	unsigned flags;
-} sctp_peeloff_flags_arg_t;
-
-/*
- *  Peer Address Thresholds socket option
- */
-struct sctp_paddrthlds {
-	sctp_assoc_t spt_assoc_id;
-	struct sockaddr_storage spt_address;
-	__u16 spt_pathmaxrxt;
-	__u16 spt_pathpfthld;
-};
-
-/*
- * Socket Option for Getting the Association/Stream-Specific PR-SCTP Status
- */
-struct sctp_prstatus {
-	sctp_assoc_t sprstat_assoc_id;
-	__u16 sprstat_sid;
-	__u16 sprstat_policy;
-	__u64 sprstat_abandoned_unsent;
-	__u64 sprstat_abandoned_sent;
-};
-
-struct sctp_default_prinfo {
-	sctp_assoc_t pr_assoc_id;
-	__u32 pr_value;
-	__u16 pr_policy;
-};
-
-struct sctp_info {
-	__u32	sctpi_tag;
-	__u32	sctpi_state;
-	__u32	sctpi_rwnd;
-	__u16	sctpi_unackdata;
-	__u16	sctpi_penddata;
-	__u16	sctpi_instrms;
-	__u16	sctpi_outstrms;
-	__u32	sctpi_fragmentation_point;
-	__u32	sctpi_inqueue;
-	__u32	sctpi_outqueue;
-	__u32	sctpi_overall_error;
-	__u32	sctpi_max_burst;
-	__u32	sctpi_maxseg;
-	__u32	sctpi_peer_rwnd;
-	__u32	sctpi_peer_tag;
-	__u8	sctpi_peer_capable;
-	__u8	sctpi_peer_sack;
-	__u16	__reserved1;
-
-	/* assoc status info */
-	__u64	sctpi_isacks;
-	__u64	sctpi_osacks;
-	__u64	sctpi_opackets;
-	__u64	sctpi_ipackets;
-	__u64	sctpi_rtxchunks;
-	__u64	sctpi_outofseqtsns;
-	__u64	sctpi_idupchunks;
-	__u64	sctpi_gapcnt;
-	__u64	sctpi_ouodchunks;
-	__u64	sctpi_iuodchunks;
-	__u64	sctpi_oodchunks;
-	__u64	sctpi_iodchunks;
-	__u64	sctpi_octrlchunks;
-	__u64	sctpi_ictrlchunks;
-
-	/* primary transport info */
-	struct sockaddr_storage	sctpi_p_address;
-	__s32	sctpi_p_state;
-	__u32	sctpi_p_cwnd;
-	__u32	sctpi_p_srtt;
-	__u32	sctpi_p_rto;
-	__u32	sctpi_p_hbinterval;
-	__u32	sctpi_p_pathmaxrxt;
-	__u32	sctpi_p_sackdelay;
-	__u32	sctpi_p_sackfreq;
-	__u32	sctpi_p_ssthresh;
-	__u32	sctpi_p_partial_bytes_acked;
-	__u32	sctpi_p_flight_size;
-	__u16	sctpi_p_error;
-	__u16	__reserved2;
-
-	/* sctp sock info */
-	__u32	sctpi_s_autoclose;
-	__u32	sctpi_s_adaptation_ind;
-	__u32	sctpi_s_pd_point;
-	__u8	sctpi_s_nodelay;
-	__u8	sctpi_s_disable_fragments;
-	__u8	sctpi_s_v4mapped;
-	__u8	sctpi_s_frag_interleave;
-	__u32	sctpi_s_type;
-	__u32	__reserved3;
-};
-
-struct sctp_reset_streams {
-	sctp_assoc_t srs_assoc_id;
-	uint16_t srs_flags;
-	uint16_t srs_number_streams;	/* 0 == ALL */
-	uint16_t srs_stream_list[];	/* list if srs_num_streams is not 0 */
-};
-
-struct sctp_add_streams {
-	sctp_assoc_t sas_assoc_id;
-	uint16_t sas_instrms;
-	uint16_t sas_outstrms;
-};
-
-struct sctp_event {
-	sctp_assoc_t se_assoc_id;
-	uint16_t se_type;
-	uint8_t se_on;
-};
-
-/* SCTP Stream schedulers */
-enum sctp_sched_type {
-	SCTP_SS_FCFS,
-	SCTP_SS_DEFAULT = SCTP_SS_FCFS,
-	SCTP_SS_PRIO,
-	SCTP_SS_RR,
-	SCTP_SS_MAX = SCTP_SS_RR
-};
-
-#endif /* _SCTP_H */
diff --git a/include/uapi/linux/seg6.h b/include/uapi/linux/seg6.h
deleted file mode 100644
index 329163e..0000000
--- a/include/uapi/linux/seg6.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
-/*
- *  SR-IPv6 implementation
- *
- *  Author:
- *  David Lebrun <david.lebrun@uclouvain.be>
- *
- *
- *  This program is free software; you can redistribute it and/or
- *      modify it under the terms of the GNU General Public License
- *      as published by the Free Software Foundation; either version
- *      2 of the License, or (at your option) any later version.
- */
-
-#ifndef _LINUX_SEG6_H
-#define _LINUX_SEG6_H
-
-#include <linux/types.h>
-#include <linux/in6.h>		/* For struct in6_addr. */
-
-/*
- * SRH
- */
-struct ipv6_sr_hdr {
-	__u8	nexthdr;
-	__u8	hdrlen;
-	__u8	type;
-	__u8	segments_left;
-	__u8	first_segment; /* Represents the last_entry field of SRH */
-	__u8	flags;
-	__u16	tag;
-
-	struct in6_addr segments[0];
-};
-
-#define SR6_FLAG1_PROTECTED	(1 << 6)
-#define SR6_FLAG1_OAM		(1 << 5)
-#define SR6_FLAG1_ALERT		(1 << 4)
-#define SR6_FLAG1_HMAC		(1 << 3)
-
-#define SR6_TLV_INGRESS		1
-#define SR6_TLV_EGRESS		2
-#define SR6_TLV_OPAQUE		3
-#define SR6_TLV_PADDING		4
-#define SR6_TLV_HMAC		5
-
-#define sr_has_hmac(srh) ((srh)->flags & SR6_FLAG1_HMAC)
-
-struct sr6_tlv {
-	__u8 type;
-	__u8 len;
-	__u8 data[0];
-};
-
-#endif
diff --git a/include/uapi/linux/seg6_genl.h b/include/uapi/linux/seg6_genl.h
deleted file mode 100644
index 0c23052..0000000
--- a/include/uapi/linux/seg6_genl.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_SEG6_GENL_H
-#define _LINUX_SEG6_GENL_H
-
-#define SEG6_GENL_NAME		"SEG6"
-#define SEG6_GENL_VERSION	0x1
-
-enum {
-	SEG6_ATTR_UNSPEC,
-	SEG6_ATTR_DST,
-	SEG6_ATTR_DSTLEN,
-	SEG6_ATTR_HMACKEYID,
-	SEG6_ATTR_SECRET,
-	SEG6_ATTR_SECRETLEN,
-	SEG6_ATTR_ALGID,
-	SEG6_ATTR_HMACINFO,
-	__SEG6_ATTR_MAX,
-};
-
-#define SEG6_ATTR_MAX (__SEG6_ATTR_MAX - 1)
-
-enum {
-	SEG6_CMD_UNSPEC,
-	SEG6_CMD_SETHMAC,
-	SEG6_CMD_DUMPHMAC,
-	SEG6_CMD_SET_TUNSRC,
-	SEG6_CMD_GET_TUNSRC,
-	__SEG6_CMD_MAX,
-};
-
-#define SEG6_CMD_MAX (__SEG6_CMD_MAX - 1)
-
-#endif
diff --git a/include/uapi/linux/seg6_hmac.h b/include/uapi/linux/seg6_hmac.h
deleted file mode 100644
index 3fb3412..0000000
--- a/include/uapi/linux/seg6_hmac.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_SEG6_HMAC_H
-#define _LINUX_SEG6_HMAC_H
-
-#include <linux/types.h>
-#include <linux/seg6.h>
-
-#define SEG6_HMAC_SECRET_LEN	64
-#define SEG6_HMAC_FIELD_LEN	32
-
-struct sr6_tlv_hmac {
-	struct sr6_tlv tlvhdr;
-	__u16 reserved;
-	__be32 hmackeyid;
-	__u8 hmac[SEG6_HMAC_FIELD_LEN];
-};
-
-enum {
-	SEG6_HMAC_ALGO_SHA1 = 1,
-	SEG6_HMAC_ALGO_SHA256 = 2,
-};
-
-#endif
diff --git a/include/uapi/linux/seg6_iptunnel.h b/include/uapi/linux/seg6_iptunnel.h
deleted file mode 100644
index 3004e98..0000000
--- a/include/uapi/linux/seg6_iptunnel.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
-/*
- *  SR-IPv6 implementation
- *
- *  Author:
- *  David Lebrun <david.lebrun@uclouvain.be>
- *
- *
- *  This program is free software; you can redistribute it and/or
- *      modify it under the terms of the GNU General Public License
- *      as published by the Free Software Foundation; either version
- *      2 of the License, or (at your option) any later version.
- */
-
-#ifndef _LINUX_SEG6_IPTUNNEL_H
-#define _LINUX_SEG6_IPTUNNEL_H
-
-#include <linux/seg6.h>		/* For struct ipv6_sr_hdr. */
-
-enum {
-	SEG6_IPTUNNEL_UNSPEC,
-	SEG6_IPTUNNEL_SRH,
-	__SEG6_IPTUNNEL_MAX,
-};
-#define SEG6_IPTUNNEL_MAX (__SEG6_IPTUNNEL_MAX - 1)
-
-struct seg6_iptunnel_encap {
-	int mode;
-	struct ipv6_sr_hdr srh[0];
-};
-
-#define SEG6_IPTUN_ENCAP_SIZE(x) ((sizeof(*x)) + (((x)->srh->hdrlen + 1) << 3))
-
-enum {
-	SEG6_IPTUN_MODE_INLINE,
-	SEG6_IPTUN_MODE_ENCAP,
-	SEG6_IPTUN_MODE_L2ENCAP,
-};
-
-
-#endif
diff --git a/include/uapi/linux/seg6_local.h b/include/uapi/linux/seg6_local.h
deleted file mode 100644
index 5312de8..0000000
--- a/include/uapi/linux/seg6_local.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- *  SR-IPv6 implementation
- *
- *  Author:
- *  David Lebrun <david.lebrun@uclouvain.be>
- *
- *
- *  This program is free software; you can redistribute it and/or
- *      modify it under the terms of the GNU General Public License
- *      as published by the Free Software Foundation; either version
- *      2 of the License, or (at your option) any later version.
- */
-
-#ifndef _LINUX_SEG6_LOCAL_H
-#define _LINUX_SEG6_LOCAL_H
-
-#include <linux/seg6.h>
-
-enum {
-	SEG6_LOCAL_UNSPEC,
-	SEG6_LOCAL_ACTION,
-	SEG6_LOCAL_SRH,
-	SEG6_LOCAL_TABLE,
-	SEG6_LOCAL_NH4,
-	SEG6_LOCAL_NH6,
-	SEG6_LOCAL_IIF,
-	SEG6_LOCAL_OIF,
-	SEG6_LOCAL_BPF,
-	__SEG6_LOCAL_MAX,
-};
-#define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1)
-
-enum {
-	SEG6_LOCAL_ACTION_UNSPEC	= 0,
-	/* node segment */
-	SEG6_LOCAL_ACTION_END		= 1,
-	/* adjacency segment (IPv6 cross-connect) */
-	SEG6_LOCAL_ACTION_END_X		= 2,
-	/* lookup of next seg NH in table */
-	SEG6_LOCAL_ACTION_END_T		= 3,
-	/* decap and L2 cross-connect */
-	SEG6_LOCAL_ACTION_END_DX2	= 4,
-	/* decap and IPv6 cross-connect */
-	SEG6_LOCAL_ACTION_END_DX6	= 5,
-	/* decap and IPv4 cross-connect */
-	SEG6_LOCAL_ACTION_END_DX4	= 6,
-	/* decap and lookup of DA in v6 table */
-	SEG6_LOCAL_ACTION_END_DT6	= 7,
-	/* decap and lookup of DA in v4 table */
-	SEG6_LOCAL_ACTION_END_DT4	= 8,
-	/* binding segment with insertion */
-	SEG6_LOCAL_ACTION_END_B6	= 9,
-	/* binding segment with encapsulation */
-	SEG6_LOCAL_ACTION_END_B6_ENCAP	= 10,
-	/* binding segment with MPLS encap */
-	SEG6_LOCAL_ACTION_END_BM	= 11,
-	/* lookup last seg in table */
-	SEG6_LOCAL_ACTION_END_S		= 12,
-	/* forward to SR-unaware VNF with static proxy */
-	SEG6_LOCAL_ACTION_END_AS	= 13,
-	/* forward to SR-unaware VNF with masquerading */
-	SEG6_LOCAL_ACTION_END_AM	= 14,
-	/* custom BPF action */
-	SEG6_LOCAL_ACTION_END_BPF	= 15,
-
-	__SEG6_LOCAL_ACTION_MAX,
-};
-
-#define SEG6_LOCAL_ACTION_MAX (__SEG6_LOCAL_ACTION_MAX - 1)
-
-enum {
-	SEG6_LOCAL_BPF_PROG_UNSPEC,
-	SEG6_LOCAL_BPF_PROG,
-	SEG6_LOCAL_BPF_PROG_NAME,
-	__SEG6_LOCAL_BPF_PROG_MAX,
-};
-
-#define SEG6_LOCAL_BPF_PROG_MAX (__SEG6_LOCAL_BPF_PROG_MAX - 1)
-
-#endif
diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h
deleted file mode 100644
index 549a31c..0000000
--- a/include/uapi/linux/snmp.h
+++ /dev/null
@@ -1,326 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/*
- * Definitions for MIBs
- *
- * Author: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
- */
-
-#ifndef _LINUX_SNMP_H
-#define _LINUX_SNMP_H
-
-/* ipstats mib definitions */
-/*
- * RFC 1213:  MIB-II
- * RFC 2011 (updates 1213):  SNMPv2-MIB-IP
- * RFC 2863:  Interfaces Group MIB
- * RFC 2465:  IPv6 MIB: General Group
- * draft-ietf-ipv6-rfc2011-update-10.txt: MIB for IP: IP Statistics Tables
- */
-enum
-{
-	IPSTATS_MIB_NUM = 0,
-/* frequently written fields in fast path, kept in same cache line */
-	IPSTATS_MIB_INPKTS,			/* InReceives */
-	IPSTATS_MIB_INOCTETS,			/* InOctets */
-	IPSTATS_MIB_INDELIVERS,			/* InDelivers */
-	IPSTATS_MIB_OUTFORWDATAGRAMS,		/* OutForwDatagrams */
-	IPSTATS_MIB_OUTPKTS,			/* OutRequests */
-	IPSTATS_MIB_OUTOCTETS,			/* OutOctets */
-/* other fields */
-	IPSTATS_MIB_INHDRERRORS,		/* InHdrErrors */
-	IPSTATS_MIB_INTOOBIGERRORS,		/* InTooBigErrors */
-	IPSTATS_MIB_INNOROUTES,			/* InNoRoutes */
-	IPSTATS_MIB_INADDRERRORS,		/* InAddrErrors */
-	IPSTATS_MIB_INUNKNOWNPROTOS,		/* InUnknownProtos */
-	IPSTATS_MIB_INTRUNCATEDPKTS,		/* InTruncatedPkts */
-	IPSTATS_MIB_INDISCARDS,			/* InDiscards */
-	IPSTATS_MIB_OUTDISCARDS,		/* OutDiscards */
-	IPSTATS_MIB_OUTNOROUTES,		/* OutNoRoutes */
-	IPSTATS_MIB_REASMTIMEOUT,		/* ReasmTimeout */
-	IPSTATS_MIB_REASMREQDS,			/* ReasmReqds */
-	IPSTATS_MIB_REASMOKS,			/* ReasmOKs */
-	IPSTATS_MIB_REASMFAILS,			/* ReasmFails */
-	IPSTATS_MIB_FRAGOKS,			/* FragOKs */
-	IPSTATS_MIB_FRAGFAILS,			/* FragFails */
-	IPSTATS_MIB_FRAGCREATES,		/* FragCreates */
-	IPSTATS_MIB_INMCASTPKTS,		/* InMcastPkts */
-	IPSTATS_MIB_OUTMCASTPKTS,		/* OutMcastPkts */
-	IPSTATS_MIB_INBCASTPKTS,		/* InBcastPkts */
-	IPSTATS_MIB_OUTBCASTPKTS,		/* OutBcastPkts */
-	IPSTATS_MIB_INMCASTOCTETS,		/* InMcastOctets */
-	IPSTATS_MIB_OUTMCASTOCTETS,		/* OutMcastOctets */
-	IPSTATS_MIB_INBCASTOCTETS,		/* InBcastOctets */
-	IPSTATS_MIB_OUTBCASTOCTETS,		/* OutBcastOctets */
-	IPSTATS_MIB_CSUMERRORS,			/* InCsumErrors */
-	IPSTATS_MIB_NOECTPKTS,			/* InNoECTPkts */
-	IPSTATS_MIB_ECT1PKTS,			/* InECT1Pkts */
-	IPSTATS_MIB_ECT0PKTS,			/* InECT0Pkts */
-	IPSTATS_MIB_CEPKTS,			/* InCEPkts */
-	IPSTATS_MIB_REASM_OVERLAPS,		/* ReasmOverlaps */
-	__IPSTATS_MIB_MAX
-};
-
-/* icmp mib definitions */
-/*
- * RFC 1213:  MIB-II ICMP Group
- * RFC 2011 (updates 1213):  SNMPv2 MIB for IP: ICMP group
- */
-enum
-{
-	ICMP_MIB_NUM = 0,
-	ICMP_MIB_INMSGS,			/* InMsgs */
-	ICMP_MIB_INERRORS,			/* InErrors */
-	ICMP_MIB_INDESTUNREACHS,		/* InDestUnreachs */
-	ICMP_MIB_INTIMEEXCDS,			/* InTimeExcds */
-	ICMP_MIB_INPARMPROBS,			/* InParmProbs */
-	ICMP_MIB_INSRCQUENCHS,			/* InSrcQuenchs */
-	ICMP_MIB_INREDIRECTS,			/* InRedirects */
-	ICMP_MIB_INECHOS,			/* InEchos */
-	ICMP_MIB_INECHOREPS,			/* InEchoReps */
-	ICMP_MIB_INTIMESTAMPS,			/* InTimestamps */
-	ICMP_MIB_INTIMESTAMPREPS,		/* InTimestampReps */
-	ICMP_MIB_INADDRMASKS,			/* InAddrMasks */
-	ICMP_MIB_INADDRMASKREPS,		/* InAddrMaskReps */
-	ICMP_MIB_OUTMSGS,			/* OutMsgs */
-	ICMP_MIB_OUTERRORS,			/* OutErrors */
-	ICMP_MIB_OUTDESTUNREACHS,		/* OutDestUnreachs */
-	ICMP_MIB_OUTTIMEEXCDS,			/* OutTimeExcds */
-	ICMP_MIB_OUTPARMPROBS,			/* OutParmProbs */
-	ICMP_MIB_OUTSRCQUENCHS,			/* OutSrcQuenchs */
-	ICMP_MIB_OUTREDIRECTS,			/* OutRedirects */
-	ICMP_MIB_OUTECHOS,			/* OutEchos */
-	ICMP_MIB_OUTECHOREPS,			/* OutEchoReps */
-	ICMP_MIB_OUTTIMESTAMPS,			/* OutTimestamps */
-	ICMP_MIB_OUTTIMESTAMPREPS,		/* OutTimestampReps */
-	ICMP_MIB_OUTADDRMASKS,			/* OutAddrMasks */
-	ICMP_MIB_OUTADDRMASKREPS,		/* OutAddrMaskReps */
-	ICMP_MIB_CSUMERRORS,			/* InCsumErrors */
-	__ICMP_MIB_MAX
-};
-
-#define __ICMPMSG_MIB_MAX 512	/* Out+In for all 8-bit ICMP types */
-
-/* icmp6 mib definitions */
-/*
- * RFC 2466:  ICMPv6-MIB
- */
-enum
-{
-	ICMP6_MIB_NUM = 0,
-	ICMP6_MIB_INMSGS,			/* InMsgs */
-	ICMP6_MIB_INERRORS,			/* InErrors */
-	ICMP6_MIB_OUTMSGS,			/* OutMsgs */
-	ICMP6_MIB_OUTERRORS,			/* OutErrors */
-	ICMP6_MIB_CSUMERRORS,			/* InCsumErrors */
-	__ICMP6_MIB_MAX
-};
-
-#define __ICMP6MSG_MIB_MAX 512 /* Out+In for all 8-bit ICMPv6 types */
-
-/* tcp mib definitions */
-/*
- * RFC 1213:  MIB-II TCP group
- * RFC 2012 (updates 1213):  SNMPv2-MIB-TCP
- */
-enum
-{
-	TCP_MIB_NUM = 0,
-	TCP_MIB_RTOALGORITHM,			/* RtoAlgorithm */
-	TCP_MIB_RTOMIN,				/* RtoMin */
-	TCP_MIB_RTOMAX,				/* RtoMax */
-	TCP_MIB_MAXCONN,			/* MaxConn */
-	TCP_MIB_ACTIVEOPENS,			/* ActiveOpens */
-	TCP_MIB_PASSIVEOPENS,			/* PassiveOpens */
-	TCP_MIB_ATTEMPTFAILS,			/* AttemptFails */
-	TCP_MIB_ESTABRESETS,			/* EstabResets */
-	TCP_MIB_CURRESTAB,			/* CurrEstab */
-	TCP_MIB_INSEGS,				/* InSegs */
-	TCP_MIB_OUTSEGS,			/* OutSegs */
-	TCP_MIB_RETRANSSEGS,			/* RetransSegs */
-	TCP_MIB_INERRS,				/* InErrs */
-	TCP_MIB_OUTRSTS,			/* OutRsts */
-	TCP_MIB_CSUMERRORS,			/* InCsumErrors */
-	__TCP_MIB_MAX
-};
-
-/* udp mib definitions */
-/*
- * RFC 1213:  MIB-II UDP group
- * RFC 2013 (updates 1213):  SNMPv2-MIB-UDP
- */
-enum
-{
-	UDP_MIB_NUM = 0,
-	UDP_MIB_INDATAGRAMS,			/* InDatagrams */
-	UDP_MIB_NOPORTS,			/* NoPorts */
-	UDP_MIB_INERRORS,			/* InErrors */
-	UDP_MIB_OUTDATAGRAMS,			/* OutDatagrams */
-	UDP_MIB_RCVBUFERRORS,			/* RcvbufErrors */
-	UDP_MIB_SNDBUFERRORS,			/* SndbufErrors */
-	UDP_MIB_CSUMERRORS,			/* InCsumErrors */
-	UDP_MIB_IGNOREDMULTI,			/* IgnoredMulti */
-	__UDP_MIB_MAX
-};
-
-/* linux mib definitions */
-enum
-{
-	LINUX_MIB_NUM = 0,
-	LINUX_MIB_SYNCOOKIESSENT,		/* SyncookiesSent */
-	LINUX_MIB_SYNCOOKIESRECV,		/* SyncookiesRecv */
-	LINUX_MIB_SYNCOOKIESFAILED,		/* SyncookiesFailed */
-	LINUX_MIB_EMBRYONICRSTS,		/* EmbryonicRsts */
-	LINUX_MIB_PRUNECALLED,			/* PruneCalled */
-	LINUX_MIB_RCVPRUNED,			/* RcvPruned */
-	LINUX_MIB_OFOPRUNED,			/* OfoPruned */
-	LINUX_MIB_OUTOFWINDOWICMPS,		/* OutOfWindowIcmps */
-	LINUX_MIB_LOCKDROPPEDICMPS,		/* LockDroppedIcmps */
-	LINUX_MIB_ARPFILTER,			/* ArpFilter */
-	LINUX_MIB_TIMEWAITED,			/* TimeWaited */
-	LINUX_MIB_TIMEWAITRECYCLED,		/* TimeWaitRecycled */
-	LINUX_MIB_TIMEWAITKILLED,		/* TimeWaitKilled */
-	LINUX_MIB_PAWSACTIVEREJECTED,		/* PAWSActiveRejected */
-	LINUX_MIB_PAWSESTABREJECTED,		/* PAWSEstabRejected */
-	LINUX_MIB_DELAYEDACKS,			/* DelayedACKs */
-	LINUX_MIB_DELAYEDACKLOCKED,		/* DelayedACKLocked */
-	LINUX_MIB_DELAYEDACKLOST,		/* DelayedACKLost */
-	LINUX_MIB_LISTENOVERFLOWS,		/* ListenOverflows */
-	LINUX_MIB_LISTENDROPS,			/* ListenDrops */
-	LINUX_MIB_TCPHPHITS,			/* TCPHPHits */
-	LINUX_MIB_TCPPUREACKS,			/* TCPPureAcks */
-	LINUX_MIB_TCPHPACKS,			/* TCPHPAcks */
-	LINUX_MIB_TCPRENORECOVERY,		/* TCPRenoRecovery */
-	LINUX_MIB_TCPSACKRECOVERY,		/* TCPSackRecovery */
-	LINUX_MIB_TCPSACKRENEGING,		/* TCPSACKReneging */
-	LINUX_MIB_TCPSACKREORDER,		/* TCPSACKReorder */
-	LINUX_MIB_TCPRENOREORDER,		/* TCPRenoReorder */
-	LINUX_MIB_TCPTSREORDER,			/* TCPTSReorder */
-	LINUX_MIB_TCPFULLUNDO,			/* TCPFullUndo */
-	LINUX_MIB_TCPPARTIALUNDO,		/* TCPPartialUndo */
-	LINUX_MIB_TCPDSACKUNDO,			/* TCPDSACKUndo */
-	LINUX_MIB_TCPLOSSUNDO,			/* TCPLossUndo */
-	LINUX_MIB_TCPLOSTRETRANSMIT,		/* TCPLostRetransmit */
-	LINUX_MIB_TCPRENOFAILURES,		/* TCPRenoFailures */
-	LINUX_MIB_TCPSACKFAILURES,		/* TCPSackFailures */
-	LINUX_MIB_TCPLOSSFAILURES,		/* TCPLossFailures */
-	LINUX_MIB_TCPFASTRETRANS,		/* TCPFastRetrans */
-	LINUX_MIB_TCPSLOWSTARTRETRANS,		/* TCPSlowStartRetrans */
-	LINUX_MIB_TCPTIMEOUTS,			/* TCPTimeouts */
-	LINUX_MIB_TCPLOSSPROBES,		/* TCPLossProbes */
-	LINUX_MIB_TCPLOSSPROBERECOVERY,		/* TCPLossProbeRecovery */
-	LINUX_MIB_TCPRENORECOVERYFAIL,		/* TCPRenoRecoveryFail */
-	LINUX_MIB_TCPSACKRECOVERYFAIL,		/* TCPSackRecoveryFail */
-	LINUX_MIB_TCPRCVCOLLAPSED,		/* TCPRcvCollapsed */
-	LINUX_MIB_TCPDSACKOLDSENT,		/* TCPDSACKOldSent */
-	LINUX_MIB_TCPDSACKOFOSENT,		/* TCPDSACKOfoSent */
-	LINUX_MIB_TCPDSACKRECV,			/* TCPDSACKRecv */
-	LINUX_MIB_TCPDSACKOFORECV,		/* TCPDSACKOfoRecv */
-	LINUX_MIB_TCPABORTONDATA,		/* TCPAbortOnData */
-	LINUX_MIB_TCPABORTONCLOSE,		/* TCPAbortOnClose */
-	LINUX_MIB_TCPABORTONMEMORY,		/* TCPAbortOnMemory */
-	LINUX_MIB_TCPABORTONTIMEOUT,		/* TCPAbortOnTimeout */
-	LINUX_MIB_TCPABORTONLINGER,		/* TCPAbortOnLinger */
-	LINUX_MIB_TCPABORTFAILED,		/* TCPAbortFailed */
-	LINUX_MIB_TCPMEMORYPRESSURES,		/* TCPMemoryPressures */
-	LINUX_MIB_TCPMEMORYPRESSURESCHRONO,	/* TCPMemoryPressuresChrono */
-	LINUX_MIB_TCPSACKDISCARD,		/* TCPSACKDiscard */
-	LINUX_MIB_TCPDSACKIGNOREDOLD,		/* TCPSACKIgnoredOld */
-	LINUX_MIB_TCPDSACKIGNOREDNOUNDO,	/* TCPSACKIgnoredNoUndo */
-	LINUX_MIB_TCPSPURIOUSRTOS,		/* TCPSpuriousRTOs */
-	LINUX_MIB_TCPMD5NOTFOUND,		/* TCPMD5NotFound */
-	LINUX_MIB_TCPMD5UNEXPECTED,		/* TCPMD5Unexpected */
-	LINUX_MIB_TCPMD5FAILURE,		/* TCPMD5Failure */
-	LINUX_MIB_SACKSHIFTED,
-	LINUX_MIB_SACKMERGED,
-	LINUX_MIB_SACKSHIFTFALLBACK,
-	LINUX_MIB_TCPBACKLOGDROP,
-	LINUX_MIB_PFMEMALLOCDROP,
-	LINUX_MIB_TCPMINTTLDROP, /* RFC 5082 */
-	LINUX_MIB_TCPDEFERACCEPTDROP,
-	LINUX_MIB_IPRPFILTER, /* IP Reverse Path Filter (rp_filter) */
-	LINUX_MIB_TCPTIMEWAITOVERFLOW,		/* TCPTimeWaitOverflow */
-	LINUX_MIB_TCPREQQFULLDOCOOKIES,		/* TCPReqQFullDoCookies */
-	LINUX_MIB_TCPREQQFULLDROP,		/* TCPReqQFullDrop */
-	LINUX_MIB_TCPRETRANSFAIL,		/* TCPRetransFail */
-	LINUX_MIB_TCPRCVCOALESCE,		/* TCPRcvCoalesce */
-	LINUX_MIB_TCPBACKLOGCOALESCE,		/* TCPBacklogCoalesce */
-	LINUX_MIB_TCPOFOQUEUE,			/* TCPOFOQueue */
-	LINUX_MIB_TCPOFODROP,			/* TCPOFODrop */
-	LINUX_MIB_TCPOFOMERGE,			/* TCPOFOMerge */
-	LINUX_MIB_TCPCHALLENGEACK,		/* TCPChallengeACK */
-	LINUX_MIB_TCPSYNCHALLENGE,		/* TCPSYNChallenge */
-	LINUX_MIB_TCPFASTOPENACTIVE,		/* TCPFastOpenActive */
-	LINUX_MIB_TCPFASTOPENACTIVEFAIL,	/* TCPFastOpenActiveFail */
-	LINUX_MIB_TCPFASTOPENPASSIVE,		/* TCPFastOpenPassive*/
-	LINUX_MIB_TCPFASTOPENPASSIVEFAIL,	/* TCPFastOpenPassiveFail */
-	LINUX_MIB_TCPFASTOPENLISTENOVERFLOW,	/* TCPFastOpenListenOverflow */
-	LINUX_MIB_TCPFASTOPENCOOKIEREQD,	/* TCPFastOpenCookieReqd */
-	LINUX_MIB_TCPFASTOPENBLACKHOLE,		/* TCPFastOpenBlackholeDetect */
-	LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES, /* TCPSpuriousRtxHostQueues */
-	LINUX_MIB_BUSYPOLLRXPACKETS,		/* BusyPollRxPackets */
-	LINUX_MIB_TCPAUTOCORKING,		/* TCPAutoCorking */
-	LINUX_MIB_TCPFROMZEROWINDOWADV,		/* TCPFromZeroWindowAdv */
-	LINUX_MIB_TCPTOZEROWINDOWADV,		/* TCPToZeroWindowAdv */
-	LINUX_MIB_TCPWANTZEROWINDOWADV,		/* TCPWantZeroWindowAdv */
-	LINUX_MIB_TCPSYNRETRANS,		/* TCPSynRetrans */
-	LINUX_MIB_TCPORIGDATASENT,		/* TCPOrigDataSent */
-	LINUX_MIB_TCPHYSTARTTRAINDETECT,	/* TCPHystartTrainDetect */
-	LINUX_MIB_TCPHYSTARTTRAINCWND,		/* TCPHystartTrainCwnd */
-	LINUX_MIB_TCPHYSTARTDELAYDETECT,	/* TCPHystartDelayDetect */
-	LINUX_MIB_TCPHYSTARTDELAYCWND,		/* TCPHystartDelayCwnd */
-	LINUX_MIB_TCPACKSKIPPEDSYNRECV,		/* TCPACKSkippedSynRecv */
-	LINUX_MIB_TCPACKSKIPPEDPAWS,		/* TCPACKSkippedPAWS */
-	LINUX_MIB_TCPACKSKIPPEDSEQ,		/* TCPACKSkippedSeq */
-	LINUX_MIB_TCPACKSKIPPEDFINWAIT2,	/* TCPACKSkippedFinWait2 */
-	LINUX_MIB_TCPACKSKIPPEDTIMEWAIT,	/* TCPACKSkippedTimeWait */
-	LINUX_MIB_TCPACKSKIPPEDCHALLENGE,	/* TCPACKSkippedChallenge */
-	LINUX_MIB_TCPWINPROBE,			/* TCPWinProbe */
-	LINUX_MIB_TCPKEEPALIVE,			/* TCPKeepAlive */
-	LINUX_MIB_TCPMTUPFAIL,			/* TCPMTUPFail */
-	LINUX_MIB_TCPMTUPSUCCESS,		/* TCPMTUPSuccess */
-	LINUX_MIB_TCPDELIVERED,			/* TCPDelivered */
-	LINUX_MIB_TCPDELIVEREDCE,		/* TCPDeliveredCE */
-	LINUX_MIB_TCPACKCOMPRESSED,		/* TCPAckCompressed */
-	LINUX_MIB_TCPZEROWINDOWDROP,		/* TCPZeroWindowDrop */
-	LINUX_MIB_TCPRCVQDROP,			/* TCPRcvQDrop */
-	LINUX_MIB_TCPWQUEUETOOBIG,		/* TCPWqueueTooBig */
-	LINUX_MIB_TCPFASTOPENPASSIVEALTKEY,	/* TCPFastOpenPassiveAltKey */
-	__LINUX_MIB_MAX
-};
-
-/* linux Xfrm mib definitions */
-enum
-{
-	LINUX_MIB_XFRMNUM = 0,
-	LINUX_MIB_XFRMINERROR,			/* XfrmInError */
-	LINUX_MIB_XFRMINBUFFERERROR,		/* XfrmInBufferError */
-	LINUX_MIB_XFRMINHDRERROR,		/* XfrmInHdrError */
-	LINUX_MIB_XFRMINNOSTATES,		/* XfrmInNoStates */
-	LINUX_MIB_XFRMINSTATEPROTOERROR,	/* XfrmInStateProtoError */
-	LINUX_MIB_XFRMINSTATEMODEERROR,		/* XfrmInStateModeError */
-	LINUX_MIB_XFRMINSTATESEQERROR,		/* XfrmInStateSeqError */
-	LINUX_MIB_XFRMINSTATEEXPIRED,		/* XfrmInStateExpired */
-	LINUX_MIB_XFRMINSTATEMISMATCH,		/* XfrmInStateMismatch */
-	LINUX_MIB_XFRMINSTATEINVALID,		/* XfrmInStateInvalid */
-	LINUX_MIB_XFRMINTMPLMISMATCH,		/* XfrmInTmplMismatch */
-	LINUX_MIB_XFRMINNOPOLS,			/* XfrmInNoPols */
-	LINUX_MIB_XFRMINPOLBLOCK,		/* XfrmInPolBlock */
-	LINUX_MIB_XFRMINPOLERROR,		/* XfrmInPolError */
-	LINUX_MIB_XFRMOUTERROR,			/* XfrmOutError */
-	LINUX_MIB_XFRMOUTBUNDLEGENERROR,	/* XfrmOutBundleGenError */
-	LINUX_MIB_XFRMOUTBUNDLECHECKERROR,	/* XfrmOutBundleCheckError */
-	LINUX_MIB_XFRMOUTNOSTATES,		/* XfrmOutNoStates */
-	LINUX_MIB_XFRMOUTSTATEPROTOERROR,	/* XfrmOutStateProtoError */
-	LINUX_MIB_XFRMOUTSTATEMODEERROR,	/* XfrmOutStateModeError */
-	LINUX_MIB_XFRMOUTSTATESEQERROR,		/* XfrmOutStateSeqError */
-	LINUX_MIB_XFRMOUTSTATEEXPIRED,		/* XfrmOutStateExpired */
-	LINUX_MIB_XFRMOUTPOLBLOCK,		/* XfrmOutPolBlock */
-	LINUX_MIB_XFRMOUTPOLDEAD,		/* XfrmOutPolDead */
-	LINUX_MIB_XFRMOUTPOLERROR,		/* XfrmOutPolError */
-	LINUX_MIB_XFRMFWDHDRERROR,		/* XfrmFwdHdrError*/
-	LINUX_MIB_XFRMOUTSTATEINVALID,		/* XfrmOutStateInvalid */
-	LINUX_MIB_XFRMACQUIREERROR,		/* XfrmAcquireError */
-	__LINUX_MIB_XFRMMAX
-};
-
-#endif	/* _LINUX_SNMP_H */
diff --git a/include/uapi/linux/socket.h b/include/uapi/linux/socket.h
deleted file mode 100644
index debcf26..0000000
--- a/include/uapi/linux/socket.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_SOCKET_H
-#define _LINUX_SOCKET_H
-
-/*
- * Desired design of maximum size and alignment (see RFC2553)
- */
-#define _K_SS_MAXSIZE	128	/* Implementation specific max size */
-
-typedef unsigned short __kernel_sa_family_t;
-
-/*
- * The definition uses anonymous union and struct in order to control the
- * default alignment.
- */
-struct __kernel_sockaddr_storage {
-	union {
-		struct {
-			__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, */
-				/* _SS_MAXSIZE value minus size of ss_family */
-		};
-		void *__align; /* implementation specific desired alignment */
-	};
-};
-
-#endif /* _LINUX_SOCKET_H */
diff --git a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h
deleted file mode 100644
index 7d1bccb..0000000
--- a/include/uapi/linux/sockios.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
-/*
- * INET		An implementation of the TCP/IP protocol suite for the LINUX
- *		operating system.  INET is implemented using the  BSD Socket
- *		interface as the means of communication with the user level.
- *
- *		Definitions of the socket-level I/O control calls.
- *
- * Version:	@(#)sockios.h	1.0.2	03/09/93
- *
- * Authors:	Ross Biro
- *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- *
- *		This program is free software; you can redistribute it and/or
- *		modify it under the terms of the GNU General Public License
- *		as published by the Free Software Foundation; either version
- *		2 of the License, or (at your option) any later version.
- */
-#ifndef _LINUX_SOCKIOS_H
-#define _LINUX_SOCKIOS_H
-
-#include <asm/bitsperlong.h>
-#include <asm/sockios.h>
-
-/* Linux-specific socket ioctls */
-#define SIOCINQ		FIONREAD
-#define SIOCOUTQ	TIOCOUTQ        /* output queue size (not sent + not acked) */
-
-#define SOCK_IOC_TYPE	0x89
-
-/*
- * the timeval/timespec data structure layout is defined by libc,
- * so we need to cover both possible versions on 32-bit.
- */
-/* Get stamp (timeval) */
-#define SIOCGSTAMP_NEW	 _IOR(SOCK_IOC_TYPE, 0x06, long long[2])
-/* Get stamp (timespec) */
-#define SIOCGSTAMPNS_NEW _IOR(SOCK_IOC_TYPE, 0x07, long long[2])
-
-#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
-/* on 64-bit and x32, avoid the ?: operator */
-#define SIOCGSTAMP	SIOCGSTAMP_OLD
-#define SIOCGSTAMPNS	SIOCGSTAMPNS_OLD
-#else
-#define SIOCGSTAMP	((sizeof(struct timeval))  == 8 ? \
-			 SIOCGSTAMP_OLD   : SIOCGSTAMP_NEW)
-#define SIOCGSTAMPNS	((sizeof(struct timespec)) == 8 ? \
-			 SIOCGSTAMPNS_OLD : SIOCGSTAMPNS_NEW)
-#endif
-
-/* Routing table calls. */
-#define SIOCADDRT	0x890B		/* add routing table entry	*/
-#define SIOCDELRT	0x890C		/* delete routing table entry	*/
-#define SIOCRTMSG	0x890D		/* unused			*/
-
-/* Socket configuration controls. */
-#define SIOCGIFNAME	0x8910		/* get iface name		*/
-#define SIOCSIFLINK	0x8911		/* set iface channel		*/
-#define SIOCGIFCONF	0x8912		/* get iface list		*/
-#define SIOCGIFFLAGS	0x8913		/* get flags			*/
-#define SIOCSIFFLAGS	0x8914		/* set flags			*/
-#define SIOCGIFADDR	0x8915		/* get PA address		*/
-#define SIOCSIFADDR	0x8916		/* set PA address		*/
-#define SIOCGIFDSTADDR	0x8917		/* get remote PA address	*/
-#define SIOCSIFDSTADDR	0x8918		/* set remote PA address	*/
-#define SIOCGIFBRDADDR	0x8919		/* get broadcast PA address	*/
-#define SIOCSIFBRDADDR	0x891a		/* set broadcast PA address	*/
-#define SIOCGIFNETMASK	0x891b		/* get network PA mask		*/
-#define SIOCSIFNETMASK	0x891c		/* set network PA mask		*/
-#define SIOCGIFMETRIC	0x891d		/* get metric			*/
-#define SIOCSIFMETRIC	0x891e		/* set metric			*/
-#define SIOCGIFMEM	0x891f		/* get memory address (BSD)	*/
-#define SIOCSIFMEM	0x8920		/* set memory address (BSD)	*/
-#define SIOCGIFMTU	0x8921		/* get MTU size			*/
-#define SIOCSIFMTU	0x8922		/* set MTU size			*/
-#define SIOCSIFNAME	0x8923		/* set interface name */
-#define	SIOCSIFHWADDR	0x8924		/* set hardware address 	*/
-#define SIOCGIFENCAP	0x8925		/* get/set encapsulations       */
-#define SIOCSIFENCAP	0x8926		
-#define SIOCGIFHWADDR	0x8927		/* Get hardware address		*/
-#define SIOCGIFSLAVE	0x8929		/* Driver slaving support	*/
-#define SIOCSIFSLAVE	0x8930
-#define SIOCADDMULTI	0x8931		/* Multicast address lists	*/
-#define SIOCDELMULTI	0x8932
-#define SIOCGIFINDEX	0x8933		/* name -> if_index mapping	*/
-#define SIOGIFINDEX	SIOCGIFINDEX	/* misprint compatibility :-)	*/
-#define SIOCSIFPFLAGS	0x8934		/* set/get extended flags set	*/
-#define SIOCGIFPFLAGS	0x8935
-#define SIOCDIFADDR	0x8936		/* delete PA address		*/
-#define	SIOCSIFHWBROADCAST	0x8937	/* set hardware broadcast addr	*/
-#define SIOCGIFCOUNT	0x8938		/* get number of devices */
-
-#define SIOCGIFBR	0x8940		/* Bridging support		*/
-#define SIOCSIFBR	0x8941		/* Set bridging options 	*/
-
-#define SIOCGIFTXQLEN	0x8942		/* Get the tx queue length	*/
-#define SIOCSIFTXQLEN	0x8943		/* Set the tx queue length 	*/
-
-/* SIOCGIFDIVERT was:	0x8944		Frame diversion support */
-/* SIOCSIFDIVERT was:	0x8945		Set frame diversion options */
-
-#define SIOCETHTOOL	0x8946		/* Ethtool interface		*/
-
-#define SIOCGMIIPHY	0x8947		/* Get address of MII PHY in use. */
-#define SIOCGMIIREG	0x8948		/* Read MII PHY register.	*/
-#define SIOCSMIIREG	0x8949		/* Write MII PHY register.	*/
-
-#define SIOCWANDEV	0x894A		/* get/set netdev parameters	*/
-
-#define SIOCOUTQNSD	0x894B		/* output queue size (not sent only) */
-#define SIOCGSKNS	0x894C		/* get socket network namespace */
-
-/* ARP cache control calls. */
-		    /*  0x8950 - 0x8952  * obsolete calls, don't re-use */
-#define SIOCDARP	0x8953		/* delete ARP table entry	*/
-#define SIOCGARP	0x8954		/* get ARP table entry		*/
-#define SIOCSARP	0x8955		/* set ARP table entry		*/
-
-/* RARP cache control calls. */
-#define SIOCDRARP	0x8960		/* delete RARP table entry	*/
-#define SIOCGRARP	0x8961		/* get RARP table entry		*/
-#define SIOCSRARP	0x8962		/* set RARP table entry		*/
-
-/* Driver configuration calls */
-
-#define SIOCGIFMAP	0x8970		/* Get device parameters	*/
-#define SIOCSIFMAP	0x8971		/* Set device parameters	*/
-
-/* DLCI configuration calls */
-
-#define SIOCADDDLCI	0x8980		/* Create new DLCI device	*/
-#define SIOCDELDLCI	0x8981		/* Delete DLCI device		*/
-
-#define SIOCGIFVLAN	0x8982		/* 802.1Q VLAN support		*/
-#define SIOCSIFVLAN	0x8983		/* Set 802.1Q VLAN options 	*/
-
-/* bonding calls */
-
-#define SIOCBONDENSLAVE	0x8990		/* enslave a device to the bond */
-#define SIOCBONDRELEASE 0x8991		/* release a slave from the bond*/
-#define SIOCBONDSETHWADDR      0x8992	/* set the hw addr of the bond  */
-#define SIOCBONDSLAVEINFOQUERY 0x8993   /* rtn info about slave state   */
-#define SIOCBONDINFOQUERY      0x8994	/* rtn info about bond state    */
-#define SIOCBONDCHANGEACTIVE   0x8995   /* update to a new active slave */
-			
-/* bridge calls */
-#define SIOCBRADDBR     0x89a0		/* create new bridge device     */
-#define SIOCBRDELBR     0x89a1		/* remove bridge device         */
-#define SIOCBRADDIF	0x89a2		/* add interface to bridge      */
-#define SIOCBRDELIF	0x89a3		/* remove interface from bridge */
-
-/* hardware time stamping: parameters in linux/net_tstamp.h */
-#define SIOCSHWTSTAMP	0x89b0		/* set and get config		*/
-#define SIOCGHWTSTAMP	0x89b1		/* get config			*/
-
-/* Device private ioctl calls */
-
-/*
- *	These 16 ioctls are available to devices via the do_ioctl() device
- *	vector. Each device should include this file and redefine these names
- *	as their own. Because these are device dependent it is a good idea
- *	_NOT_ to issue them to random objects and hope.
- *
- *	THESE IOCTLS ARE _DEPRECATED_ AND WILL DISAPPEAR IN 2.5.X -DaveM
- */
- 
-#define SIOCDEVPRIVATE	0x89F0	/* to 89FF */
-
-/*
- *	These 16 ioctl calls are protocol private
- */
- 
-#define SIOCPROTOPRIVATE 0x89E0 /* to 89EF */
-#endif	/* _LINUX_SOCKIOS_H */
diff --git a/include/uapi/linux/stddef.h b/include/uapi/linux/stddef.h
deleted file mode 100644
index 23e025f..0000000
--- a/include/uapi/linux/stddef.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-
-
-#ifndef __always_inline
-#define __always_inline __inline__
-#endif
diff --git a/include/uapi/linux/sysinfo.h b/include/uapi/linux/sysinfo.h
deleted file mode 100644
index 435d5c2..0000000
--- a/include/uapi/linux/sysinfo.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_SYSINFO_H
-#define _LINUX_SYSINFO_H
-
-#include <linux/types.h>
-
-#define SI_LOAD_SHIFT	16
-struct sysinfo {
-	__kernel_long_t uptime;		/* Seconds since boot */
-	__kernel_ulong_t loads[3];	/* 1, 5, and 15 minute load averages */
-	__kernel_ulong_t totalram;	/* Total usable main memory size */
-	__kernel_ulong_t freeram;	/* Available memory size */
-	__kernel_ulong_t sharedram;	/* Amount of shared memory */
-	__kernel_ulong_t bufferram;	/* Memory used by buffers */
-	__kernel_ulong_t totalswap;	/* Total swap space size */
-	__kernel_ulong_t freeswap;	/* swap space still available */
-	__u16 procs;		   	/* Number of current processes */
-	__u16 pad;		   	/* Explicit padding for m68k */
-	__kernel_ulong_t totalhigh;	/* Total high memory size */
-	__kernel_ulong_t freehigh;	/* Available high memory size */
-	__u32 mem_unit;			/* Memory unit size in bytes */
-	char _f[20-2*sizeof(__kernel_ulong_t)-sizeof(__u32)];	/* Padding: libc5 uses this.. */
-};
-
-#endif /* _LINUX_SYSINFO_H */
diff --git a/include/uapi/linux/tc_act/tc_ct.h b/include/uapi/linux/tc_act/tc_ct.h
deleted file mode 100644
index 5fb1d7a..0000000
--- a/include/uapi/linux/tc_act/tc_ct.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef __UAPI_TC_CT_H
-#define __UAPI_TC_CT_H
-
-#include <linux/types.h>
-#include <linux/pkt_cls.h>
-
-enum {
-	TCA_CT_UNSPEC,
-	TCA_CT_PARMS,
-	TCA_CT_TM,
-	TCA_CT_ACTION,		/* u16 */
-	TCA_CT_ZONE,		/* u16 */
-	TCA_CT_MARK,		/* u32 */
-	TCA_CT_MARK_MASK,	/* u32 */
-	TCA_CT_LABELS,		/* u128 */
-	TCA_CT_LABELS_MASK,	/* u128 */
-	TCA_CT_NAT_IPV4_MIN,	/* be32 */
-	TCA_CT_NAT_IPV4_MAX,	/* be32 */
-	TCA_CT_NAT_IPV6_MIN,	/* struct in6_addr */
-	TCA_CT_NAT_IPV6_MAX,	/* struct in6_addr */
-	TCA_CT_NAT_PORT_MIN,	/* be16 */
-	TCA_CT_NAT_PORT_MAX,	/* be16 */
-	TCA_CT_PAD,
-	__TCA_CT_MAX
-};
-
-#define TCA_CT_MAX (__TCA_CT_MAX - 1)
-
-#define TCA_CT_ACT_COMMIT	(1 << 0)
-#define TCA_CT_ACT_FORCE	(1 << 1)
-#define TCA_CT_ACT_CLEAR	(1 << 2)
-#define TCA_CT_ACT_NAT		(1 << 3)
-#define TCA_CT_ACT_NAT_SRC	(1 << 4)
-#define TCA_CT_ACT_NAT_DST	(1 << 5)
-
-struct tc_ct {
-	tc_gen;
-};
-
-#endif /* __UAPI_TC_CT_H */
diff --git a/include/uapi/linux/tc_act/tc_ctinfo.h b/include/uapi/linux/tc_act/tc_ctinfo.h
deleted file mode 100644
index f5f26d9..0000000
--- a/include/uapi/linux/tc_act/tc_ctinfo.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef __UAPI_TC_CTINFO_H
-#define __UAPI_TC_CTINFO_H
-
-#include <linux/types.h>
-#include <linux/pkt_cls.h>
-
-struct tc_ctinfo {
-	tc_gen;
-};
-
-enum {
-	TCA_CTINFO_UNSPEC,
-	TCA_CTINFO_PAD,
-	TCA_CTINFO_TM,
-	TCA_CTINFO_ACT,
-	TCA_CTINFO_ZONE,
-	TCA_CTINFO_PARMS_DSCP_MASK,
-	TCA_CTINFO_PARMS_DSCP_STATEMASK,
-	TCA_CTINFO_PARMS_CPMARK_MASK,
-	TCA_CTINFO_STATS_DSCP_SET,
-	TCA_CTINFO_STATS_DSCP_ERROR,
-	TCA_CTINFO_STATS_CPMARK_SET,
-	__TCA_CTINFO_MAX
-};
-
-#define TCA_CTINFO_MAX (__TCA_CTINFO_MAX - 1)
-
-#endif
diff --git a/include/uapi/linux/tc_act/tc_ife.h b/include/uapi/linux/tc_act/tc_ife.h
deleted file mode 100644
index 8c401f1..0000000
--- a/include/uapi/linux/tc_act/tc_ife.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef __UAPI_TC_IFE_H
-#define __UAPI_TC_IFE_H
-
-#include <linux/types.h>
-#include <linux/pkt_cls.h>
-#include <linux/ife.h>
-
-/* Flag bits for now just encoding/decoding; mutually exclusive */
-#define IFE_ENCODE 1
-#define IFE_DECODE 0
-
-struct tc_ife {
-	tc_gen;
-	__u16 flags;
-};
-
-/*XXX: We need to encode the total number of bytes consumed */
-enum {
-	TCA_IFE_UNSPEC,
-	TCA_IFE_PARMS,
-	TCA_IFE_TM,
-	TCA_IFE_DMAC,
-	TCA_IFE_SMAC,
-	TCA_IFE_TYPE,
-	TCA_IFE_METALST,
-	TCA_IFE_PAD,
-	__TCA_IFE_MAX
-};
-#define TCA_IFE_MAX (__TCA_IFE_MAX - 1)
-
-#endif
diff --git a/include/uapi/linux/tc_act/tc_mpls.h b/include/uapi/linux/tc_act/tc_mpls.h
deleted file mode 100644
index 9360e95..0000000
--- a/include/uapi/linux/tc_act/tc_mpls.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* Copyright (C) 2019 Netronome Systems, Inc. */
-
-#ifndef __LINUX_TC_MPLS_H
-#define __LINUX_TC_MPLS_H
-
-#include <linux/pkt_cls.h>
-
-#define TCA_MPLS_ACT_POP	1
-#define TCA_MPLS_ACT_PUSH	2
-#define TCA_MPLS_ACT_MODIFY	3
-#define TCA_MPLS_ACT_DEC_TTL	4
-
-struct tc_mpls {
-	tc_gen;		/* generic TC action fields. */
-	int m_action;	/* action of type TCA_MPLS_ACT_*. */
-};
-
-enum {
-	TCA_MPLS_UNSPEC,
-	TCA_MPLS_TM,	/* struct tcf_t; time values associated with action. */
-	TCA_MPLS_PARMS,	/* struct tc_mpls; action type and general TC fields. */
-	TCA_MPLS_PAD,
-	TCA_MPLS_PROTO,	/* be16; eth_type of pushed or next (for pop) header. */
-	TCA_MPLS_LABEL,	/* u32; MPLS label. Lower 20 bits are used. */
-	TCA_MPLS_TC,	/* u8; MPLS TC field. Lower 3 bits are used. */
-	TCA_MPLS_TTL,	/* u8; MPLS TTL field. Must not be 0. */
-	TCA_MPLS_BOS,	/* u8; MPLS BOS field. Either 1 or 0. */
-	__TCA_MPLS_MAX,
-};
-#define TCA_MPLS_MAX (__TCA_MPLS_MAX - 1)
-
-#endif
diff --git a/include/uapi/linux/tc_act/tc_pedit.h b/include/uapi/linux/tc_act/tc_pedit.h
deleted file mode 100644
index f3e61b0..0000000
--- a/include/uapi/linux/tc_act/tc_pedit.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef __LINUX_TC_PED_H
-#define __LINUX_TC_PED_H
-
-#include <linux/types.h>
-#include <linux/pkt_cls.h>
-
-enum {
-	TCA_PEDIT_UNSPEC,
-	TCA_PEDIT_TM,
-	TCA_PEDIT_PARMS,
-	TCA_PEDIT_PAD,
-	TCA_PEDIT_PARMS_EX,
-	TCA_PEDIT_KEYS_EX,
-	TCA_PEDIT_KEY_EX,
-	__TCA_PEDIT_MAX
-};
-
-#define TCA_PEDIT_MAX (__TCA_PEDIT_MAX - 1)
-
-enum {
-	TCA_PEDIT_KEY_EX_HTYPE = 1,
-	TCA_PEDIT_KEY_EX_CMD = 2,
-	__TCA_PEDIT_KEY_EX_MAX
-};
-
-#define TCA_PEDIT_KEY_EX_MAX (__TCA_PEDIT_KEY_EX_MAX - 1)
-
- /* TCA_PEDIT_KEY_EX_HDR_TYPE_NETWROK is a special case for legacy users. It
-  * means no specific header type - offset is relative to the network layer
-  */
-enum pedit_header_type {
-	TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK = 0,
-	TCA_PEDIT_KEY_EX_HDR_TYPE_ETH = 1,
-	TCA_PEDIT_KEY_EX_HDR_TYPE_IP4 = 2,
-	TCA_PEDIT_KEY_EX_HDR_TYPE_IP6 = 3,
-	TCA_PEDIT_KEY_EX_HDR_TYPE_TCP = 4,
-	TCA_PEDIT_KEY_EX_HDR_TYPE_UDP = 5,
-	__PEDIT_HDR_TYPE_MAX,
-};
-
-#define TCA_PEDIT_HDR_TYPE_MAX (__PEDIT_HDR_TYPE_MAX - 1)
-
-enum pedit_cmd {
-	TCA_PEDIT_KEY_EX_CMD_SET = 0,
-	TCA_PEDIT_KEY_EX_CMD_ADD = 1,
-	__PEDIT_CMD_MAX,
-};
-
-#define TCA_PEDIT_CMD_MAX (__PEDIT_CMD_MAX - 1)
-
-struct tc_pedit_key {
-	__u32           mask;  /* AND */
-	__u32           val;   /*XOR */
-	__u32           off;  /*offset */
-	__u32           at;
-	__u32           offmask;
-	__u32           shift;
-};
-
-struct tc_pedit_sel {
-	tc_gen;
-	unsigned char           nkeys;
-	unsigned char           flags;
-	struct tc_pedit_key     keys[0];
-};
-
-#define tc_pedit tc_pedit_sel
-
-#endif
diff --git a/include/uapi/linux/tc_act/tc_sample.h b/include/uapi/linux/tc_act/tc_sample.h
deleted file mode 100644
index fee1bcc..0000000
--- a/include/uapi/linux/tc_act/tc_sample.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef __LINUX_TC_SAMPLE_H
-#define __LINUX_TC_SAMPLE_H
-
-#include <linux/types.h>
-#include <linux/pkt_cls.h>
-#include <linux/if_ether.h>
-
-struct tc_sample {
-	tc_gen;
-};
-
-enum {
-	TCA_SAMPLE_UNSPEC,
-	TCA_SAMPLE_TM,
-	TCA_SAMPLE_PARMS,
-	TCA_SAMPLE_RATE,
-	TCA_SAMPLE_TRUNC_SIZE,
-	TCA_SAMPLE_PSAMPLE_GROUP,
-	TCA_SAMPLE_PAD,
-	__TCA_SAMPLE_MAX
-};
-#define TCA_SAMPLE_MAX (__TCA_SAMPLE_MAX - 1)
-
-#endif
diff --git a/include/uapi/linux/tc_act/tc_skbmod.h b/include/uapi/linux/tc_act/tc_skbmod.h
deleted file mode 100644
index c525b35..0000000
--- a/include/uapi/linux/tc_act/tc_skbmod.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
-/*
- * Copyright (c) 2016, Jamal Hadi Salim
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
-*/
-
-#ifndef __LINUX_TC_SKBMOD_H
-#define __LINUX_TC_SKBMOD_H
-
-#include <linux/pkt_cls.h>
-
-#define SKBMOD_F_DMAC	0x1
-#define SKBMOD_F_SMAC	0x2
-#define SKBMOD_F_ETYPE	0x4
-#define SKBMOD_F_SWAPMAC 0x8
-
-struct tc_skbmod {
-	tc_gen;
-	__u64 flags;
-};
-
-enum {
-	TCA_SKBMOD_UNSPEC,
-	TCA_SKBMOD_TM,
-	TCA_SKBMOD_PARMS,
-	TCA_SKBMOD_DMAC,
-	TCA_SKBMOD_SMAC,
-	TCA_SKBMOD_ETYPE,
-	TCA_SKBMOD_PAD,
-	__TCA_SKBMOD_MAX
-};
-#define TCA_SKBMOD_MAX (__TCA_SKBMOD_MAX - 1)
-
-#endif
diff --git a/include/uapi/linux/tc_act/tc_tunnel_key.h b/include/uapi/linux/tc_act/tc_tunnel_key.h
deleted file mode 100644
index 41c8b46..0000000
--- a/include/uapi/linux/tc_act/tc_tunnel_key.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
-/*
- * Copyright (c) 2016, Amir Vadai <amir@vadai.me>
- * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef __LINUX_TC_TUNNEL_KEY_H
-#define __LINUX_TC_TUNNEL_KEY_H
-
-#include <linux/pkt_cls.h>
-
-#define TCA_TUNNEL_KEY_ACT_SET	    1
-#define TCA_TUNNEL_KEY_ACT_RELEASE  2
-
-struct tc_tunnel_key {
-	tc_gen;
-	int t_action;
-};
-
-enum {
-	TCA_TUNNEL_KEY_UNSPEC,
-	TCA_TUNNEL_KEY_TM,
-	TCA_TUNNEL_KEY_PARMS,
-	TCA_TUNNEL_KEY_ENC_IPV4_SRC,	/* be32 */
-	TCA_TUNNEL_KEY_ENC_IPV4_DST,	/* be32 */
-	TCA_TUNNEL_KEY_ENC_IPV6_SRC,	/* struct in6_addr */
-	TCA_TUNNEL_KEY_ENC_IPV6_DST,	/* struct in6_addr */
-	TCA_TUNNEL_KEY_ENC_KEY_ID,	/* be64 */
-	TCA_TUNNEL_KEY_PAD,
-	TCA_TUNNEL_KEY_ENC_DST_PORT,	/* be16 */
-	TCA_TUNNEL_KEY_NO_CSUM,		/* u8 */
-	TCA_TUNNEL_KEY_ENC_OPTS,	/* Nested TCA_TUNNEL_KEY_ENC_OPTS_
-					 * attributes
-					 */
-	TCA_TUNNEL_KEY_ENC_TOS,		/* u8 */
-	TCA_TUNNEL_KEY_ENC_TTL,		/* u8 */
-	__TCA_TUNNEL_KEY_MAX,
-};
-
-#define TCA_TUNNEL_KEY_MAX (__TCA_TUNNEL_KEY_MAX - 1)
-
-enum {
-	TCA_TUNNEL_KEY_ENC_OPTS_UNSPEC,
-	TCA_TUNNEL_KEY_ENC_OPTS_GENEVE,		/* Nested
-						 * TCA_TUNNEL_KEY_ENC_OPTS_
-						 * attributes
-						 */
-	__TCA_TUNNEL_KEY_ENC_OPTS_MAX,
-};
-
-#define TCA_TUNNEL_KEY_ENC_OPTS_MAX (__TCA_TUNNEL_KEY_ENC_OPTS_MAX - 1)
-
-enum {
-	TCA_TUNNEL_KEY_ENC_OPT_GENEVE_UNSPEC,
-	TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS,		/* be16 */
-	TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE,		/* u8 */
-	TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA,		/* 4 to 128 bytes */
-
-	__TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX,
-};
-
-#define TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX \
-	(__TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX - 1)
-
-#endif
diff --git a/include/uapi/linux/tc_ematch/tc_em_ipt.h b/include/uapi/linux/tc_ematch/tc_em_ipt.h
deleted file mode 100644
index 49a6553..0000000
--- a/include/uapi/linux/tc_ematch/tc_em_ipt.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef __LINUX_TC_EM_IPT_H
-#define __LINUX_TC_EM_IPT_H
-
-#include <linux/types.h>
-#include <linux/pkt_cls.h>
-
-enum {
-	TCA_EM_IPT_UNSPEC,
-	TCA_EM_IPT_HOOK,
-	TCA_EM_IPT_MATCH_NAME,
-	TCA_EM_IPT_MATCH_REVISION,
-	TCA_EM_IPT_NFPROTO,
-	TCA_EM_IPT_MATCH_DATA,
-	__TCA_EM_IPT_MAX
-};
-
-#define TCA_EM_IPT_MAX (__TCA_EM_IPT_MAX - 1)
-
-#endif
diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
deleted file mode 100644
index d08acf4..0000000
--- a/include/uapi/linux/tcp.h
+++ /dev/null
@@ -1,339 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
-/*
- * INET		An implementation of the TCP/IP protocol suite for the LINUX
- *		operating system.  INET is implemented using the  BSD Socket
- *		interface as the means of communication with the user level.
- *
- *		Definitions for the TCP protocol.
- *
- * Version:	@(#)tcp.h	1.0.2	04/28/93
- *
- * Author:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- *
- *		This program is free software; you can redistribute it and/or
- *		modify it under the terms of the GNU General Public License
- *		as published by the Free Software Foundation; either version
- *		2 of the License, or (at your option) any later version.
- */
-#ifndef _LINUX_TCP_H
-#define _LINUX_TCP_H
-
-#include <linux/types.h>
-#include <asm/byteorder.h>
-#include <linux/socket.h>
-
-struct tcphdr {
-	__be16	source;
-	__be16	dest;
-	__be32	seq;
-	__be32	ack_seq;
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-	__u16	res1:4,
-		doff:4,
-		fin:1,
-		syn:1,
-		rst:1,
-		psh:1,
-		ack:1,
-		urg:1,
-		ece:1,
-		cwr:1;
-#elif defined(__BIG_ENDIAN_BITFIELD)
-	__u16	doff:4,
-		res1:4,
-		cwr:1,
-		ece:1,
-		urg:1,
-		ack:1,
-		psh:1,
-		rst:1,
-		syn:1,
-		fin:1;
-#else
-#error	"Adjust your <asm/byteorder.h> defines"
-#endif	
-	__be16	window;
-	__sum16	check;
-	__be16	urg_ptr;
-};
-
-/*
- *	The union cast uses a gcc extension to avoid aliasing problems
- *  (union is compatible to any of its members)
- *  This means this part of the code is -fstrict-aliasing safe now.
- */
-union tcp_word_hdr { 
-	struct tcphdr hdr;
-	__be32 		  words[5];
-}; 
-
-#define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words [3]) 
-
-enum { 
-	TCP_FLAG_CWR = __constant_cpu_to_be32(0x00800000),
-	TCP_FLAG_ECE = __constant_cpu_to_be32(0x00400000),
-	TCP_FLAG_URG = __constant_cpu_to_be32(0x00200000),
-	TCP_FLAG_ACK = __constant_cpu_to_be32(0x00100000),
-	TCP_FLAG_PSH = __constant_cpu_to_be32(0x00080000),
-	TCP_FLAG_RST = __constant_cpu_to_be32(0x00040000),
-	TCP_FLAG_SYN = __constant_cpu_to_be32(0x00020000),
-	TCP_FLAG_FIN = __constant_cpu_to_be32(0x00010000),
-	TCP_RESERVED_BITS = __constant_cpu_to_be32(0x0F000000),
-	TCP_DATA_OFFSET = __constant_cpu_to_be32(0xF0000000)
-}; 
-
-/*
- * TCP general constants
- */
-#define TCP_MSS_DEFAULT		 536U	/* IPv4 (RFC1122, RFC2581) */
-#define TCP_MSS_DESIRED		1220U	/* IPv6 (tunneled), EDNS0 (RFC3226) */
-
-/* TCP socket options */
-#define TCP_NODELAY		1	/* Turn off Nagle's algorithm. */
-#define TCP_MAXSEG		2	/* Limit MSS */
-#define TCP_CORK		3	/* Never send partially complete segments */
-#define TCP_KEEPIDLE		4	/* Start keeplives after this period */
-#define TCP_KEEPINTVL		5	/* Interval between keepalives */
-#define TCP_KEEPCNT		6	/* Number of keepalives before death */
-#define TCP_SYNCNT		7	/* Number of SYN retransmits */
-#define TCP_LINGER2		8	/* Life time of orphaned FIN-WAIT-2 state */
-#define TCP_DEFER_ACCEPT	9	/* Wake up listener only when data arrive */
-#define TCP_WINDOW_CLAMP	10	/* Bound advertised window */
-#define TCP_INFO		11	/* Information about this connection. */
-#define TCP_QUICKACK		12	/* Block/reenable quick acks */
-#define TCP_CONGESTION		13	/* Congestion control algorithm */
-#define TCP_MD5SIG		14	/* TCP MD5 Signature (RFC2385) */
-#define TCP_THIN_LINEAR_TIMEOUTS 16      /* Use linear timeouts for thin streams*/
-#define TCP_THIN_DUPACK         17      /* Fast retrans. after 1 dupack */
-#define TCP_USER_TIMEOUT	18	/* How long for loss retry before timeout */
-#define TCP_REPAIR		19	/* TCP sock is under repair right now */
-#define TCP_REPAIR_QUEUE	20
-#define TCP_QUEUE_SEQ		21
-#define TCP_REPAIR_OPTIONS	22
-#define TCP_FASTOPEN		23	/* Enable FastOpen on listeners */
-#define TCP_TIMESTAMP		24
-#define TCP_NOTSENT_LOWAT	25	/* limit number of unsent bytes in write queue */
-#define TCP_CC_INFO		26	/* Get Congestion Control (optional) info */
-#define TCP_SAVE_SYN		27	/* Record SYN headers for new connections */
-#define TCP_SAVED_SYN		28	/* Get SYN headers recorded for connection */
-#define TCP_REPAIR_WINDOW	29	/* Get/set window parameters */
-#define TCP_FASTOPEN_CONNECT	30	/* Attempt FastOpen with connect */
-#define TCP_ULP			31	/* Attach a ULP to a TCP connection */
-#define TCP_MD5SIG_EXT		32	/* TCP MD5 Signature with extensions */
-#define TCP_FASTOPEN_KEY	33	/* Set the key for Fast Open (cookie) */
-#define TCP_FASTOPEN_NO_COOKIE	34	/* Enable TFO without a TFO cookie */
-#define TCP_ZEROCOPY_RECEIVE	35
-#define TCP_INQ			36	/* Notify bytes available to read as a cmsg on read */
-
-#define TCP_CM_INQ		TCP_INQ
-
-#define TCP_TX_DELAY		37	/* delay outgoing packets by XX usec */
-
-
-#define TCP_REPAIR_ON		1
-#define TCP_REPAIR_OFF		0
-#define TCP_REPAIR_OFF_NO_WP	-1	/* Turn off without window probes */
-
-struct tcp_repair_opt {
-	__u32	opt_code;
-	__u32	opt_val;
-};
-
-struct tcp_repair_window {
-	__u32	snd_wl1;
-	__u32	snd_wnd;
-	__u32	max_window;
-
-	__u32	rcv_wnd;
-	__u32	rcv_wup;
-};
-
-enum {
-	TCP_NO_QUEUE,
-	TCP_RECV_QUEUE,
-	TCP_SEND_QUEUE,
-	TCP_QUEUES_NR,
-};
-
-/* for TCP_INFO socket option */
-#define TCPI_OPT_TIMESTAMPS	1
-#define TCPI_OPT_SACK		2
-#define TCPI_OPT_WSCALE		4
-#define TCPI_OPT_ECN		8 /* ECN was negociated at TCP session init */
-#define TCPI_OPT_ECN_SEEN	16 /* we received at least one packet with ECT */
-#define TCPI_OPT_SYN_DATA	32 /* SYN-ACK acked data in SYN sent or rcvd */
-
-/*
- * Sender's congestion state indicating normal or abnormal situations
- * in the last round of packets sent. The state is driven by the ACK
- * information and timer events.
- */
-enum tcp_ca_state {
-	/*
-	 * Nothing bad has been observed recently.
-	 * No apparent reordering, packet loss, or ECN marks.
-	 */
-	TCP_CA_Open = 0,
-#define TCPF_CA_Open	(1<<TCP_CA_Open)
-	/*
-	 * The sender enters disordered state when it has received DUPACKs or
-	 * SACKs in the last round of packets sent. This could be due to packet
-	 * loss or reordering but needs further information to confirm packets
-	 * have been lost.
-	 */
-	TCP_CA_Disorder = 1,
-#define TCPF_CA_Disorder (1<<TCP_CA_Disorder)
-	/*
-	 * The sender enters Congestion Window Reduction (CWR) state when it
-	 * has received ACKs with ECN-ECE marks, or has experienced congestion
-	 * or packet discard on the sender host (e.g. qdisc).
-	 */
-	TCP_CA_CWR = 2,
-#define TCPF_CA_CWR	(1<<TCP_CA_CWR)
-	/*
-	 * The sender is in fast recovery and retransmitting lost packets,
-	 * typically triggered by ACK events.
-	 */
-	TCP_CA_Recovery = 3,
-#define TCPF_CA_Recovery (1<<TCP_CA_Recovery)
-	/*
-	 * The sender is in loss recovery triggered by retransmission timeout.
-	 */
-	TCP_CA_Loss = 4
-#define TCPF_CA_Loss	(1<<TCP_CA_Loss)
-};
-
-struct tcp_info {
-	__u8	tcpi_state;
-	__u8	tcpi_ca_state;
-	__u8	tcpi_retransmits;
-	__u8	tcpi_probes;
-	__u8	tcpi_backoff;
-	__u8	tcpi_options;
-	__u8	tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4;
-	__u8	tcpi_delivery_rate_app_limited:1;
-
-	__u32	tcpi_rto;
-	__u32	tcpi_ato;
-	__u32	tcpi_snd_mss;
-	__u32	tcpi_rcv_mss;
-
-	__u32	tcpi_unacked;
-	__u32	tcpi_sacked;
-	__u32	tcpi_lost;
-	__u32	tcpi_retrans;
-	__u32	tcpi_fackets;
-
-	/* Times. */
-	__u32	tcpi_last_data_sent;
-	__u32	tcpi_last_ack_sent;     /* Not remembered, sorry. */
-	__u32	tcpi_last_data_recv;
-	__u32	tcpi_last_ack_recv;
-
-	/* Metrics. */
-	__u32	tcpi_pmtu;
-	__u32	tcpi_rcv_ssthresh;
-	__u32	tcpi_rtt;
-	__u32	tcpi_rttvar;
-	__u32	tcpi_snd_ssthresh;
-	__u32	tcpi_snd_cwnd;
-	__u32	tcpi_advmss;
-	__u32	tcpi_reordering;
-
-	__u32	tcpi_rcv_rtt;
-	__u32	tcpi_rcv_space;
-
-	__u32	tcpi_total_retrans;
-
-	__u64	tcpi_pacing_rate;
-	__u64	tcpi_max_pacing_rate;
-	__u64	tcpi_bytes_acked;    /* RFC4898 tcpEStatsAppHCThruOctetsAcked */
-	__u64	tcpi_bytes_received; /* RFC4898 tcpEStatsAppHCThruOctetsReceived */
-	__u32	tcpi_segs_out;	     /* RFC4898 tcpEStatsPerfSegsOut */
-	__u32	tcpi_segs_in;	     /* RFC4898 tcpEStatsPerfSegsIn */
-
-	__u32	tcpi_notsent_bytes;
-	__u32	tcpi_min_rtt;
-	__u32	tcpi_data_segs_in;	/* RFC4898 tcpEStatsDataSegsIn */
-	__u32	tcpi_data_segs_out;	/* RFC4898 tcpEStatsDataSegsOut */
-
-	__u64   tcpi_delivery_rate;
-
-	__u64	tcpi_busy_time;      /* Time (usec) busy sending data */
-	__u64	tcpi_rwnd_limited;   /* Time (usec) limited by receive window */
-	__u64	tcpi_sndbuf_limited; /* Time (usec) limited by send buffer */
-
-	__u32	tcpi_delivered;
-	__u32	tcpi_delivered_ce;
-
-	__u64	tcpi_bytes_sent;     /* RFC4898 tcpEStatsPerfHCDataOctetsOut */
-	__u64	tcpi_bytes_retrans;  /* RFC4898 tcpEStatsPerfOctetsRetrans */
-	__u32	tcpi_dsack_dups;     /* RFC4898 tcpEStatsStackDSACKDups */
-	__u32	tcpi_reord_seen;     /* reordering events seen */
-
-	__u32	tcpi_rcv_ooopack;    /* Out-of-order packets received */
-
-	__u32	tcpi_snd_wnd;	     /* peer's advertised receive window after
-				      * scaling (bytes)
-				      */
-};
-
-/* netlink attributes types for SCM_TIMESTAMPING_OPT_STATS */
-enum {
-	TCP_NLA_PAD,
-	TCP_NLA_BUSY,		/* Time (usec) busy sending data */
-	TCP_NLA_RWND_LIMITED,	/* Time (usec) limited by receive window */
-	TCP_NLA_SNDBUF_LIMITED,	/* Time (usec) limited by send buffer */
-	TCP_NLA_DATA_SEGS_OUT,	/* Data pkts sent including retransmission */
-	TCP_NLA_TOTAL_RETRANS,	/* Data pkts retransmitted */
-	TCP_NLA_PACING_RATE,    /* Pacing rate in bytes per second */
-	TCP_NLA_DELIVERY_RATE,  /* Delivery rate in bytes per second */
-	TCP_NLA_SND_CWND,       /* Sending congestion window */
-	TCP_NLA_REORDERING,     /* Reordering metric */
-	TCP_NLA_MIN_RTT,        /* minimum RTT */
-	TCP_NLA_RECUR_RETRANS,  /* Recurring retransmits for the current pkt */
-	TCP_NLA_DELIVERY_RATE_APP_LMT, /* delivery rate application limited ? */
-	TCP_NLA_SNDQ_SIZE,	/* Data (bytes) pending in send queue */
-	TCP_NLA_CA_STATE,	/* ca_state of socket */
-	TCP_NLA_SND_SSTHRESH,	/* Slow start size threshold */
-	TCP_NLA_DELIVERED,	/* Data pkts delivered incl. out-of-order */
-	TCP_NLA_DELIVERED_CE,	/* Like above but only ones w/ CE marks */
-	TCP_NLA_BYTES_SENT,	/* Data bytes sent including retransmission */
-	TCP_NLA_BYTES_RETRANS,	/* Data bytes retransmitted */
-	TCP_NLA_DSACK_DUPS,	/* DSACK blocks received */
-	TCP_NLA_REORD_SEEN,	/* reordering events seen */
-	TCP_NLA_SRTT,		/* smoothed RTT in usecs */
-};
-
-/* for TCP_MD5SIG socket option */
-#define TCP_MD5SIG_MAXKEYLEN	80
-
-/* tcp_md5sig extension flags for TCP_MD5SIG_EXT */
-#define TCP_MD5SIG_FLAG_PREFIX		1	/* address prefix length */
-
-struct tcp_md5sig {
-	struct __kernel_sockaddr_storage tcpm_addr;	/* address associated */
-	__u8	tcpm_flags;				/* extension flags */
-	__u8	tcpm_prefixlen;				/* address prefix */
-	__u16	tcpm_keylen;				/* key length */
-	__u32	__tcpm_pad;				/* zero */
-	__u8	tcpm_key[TCP_MD5SIG_MAXKEYLEN];		/* key (binary) */
-};
-
-/* INET_DIAG_MD5SIG */
-struct tcp_diag_md5sig {
-	__u8	tcpm_family;
-	__u8	tcpm_prefixlen;
-	__u16	tcpm_keylen;
-	__be32	tcpm_addr[4];
-	__u8	tcpm_key[TCP_MD5SIG_MAXKEYLEN];
-};
-
-/* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
-
-struct tcp_zerocopy_receive {
-	__u64 address;		/* in: address of mapping */
-	__u32 length;		/* in/out: number of bytes to map/mapped */
-	__u32 recv_skip_hint;	/* out: amount of bytes to skip */
-};
-#endif /* _LINUX_TCP_H */
diff --git a/include/uapi/linux/tipc.h b/include/uapi/linux/tipc.h
deleted file mode 100644
index e16cb4e..0000000
--- a/include/uapi/linux/tipc.h
+++ /dev/null
@@ -1,291 +0,0 @@
-/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
-/*
- * include/uapi/linux/tipc.h: Header for TIPC socket interface
- *
- * Copyright (c) 2003-2006, 2015-2016 Ericsson AB
- * Copyright (c) 2005, 2010-2011, Wind River Systems
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the names of the copyright holders nor the names of its
- *    contributors may be used to endorse or promote products derived from
- *    this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _LINUX_TIPC_H_
-#define _LINUX_TIPC_H_
-
-#include <linux/types.h>
-#include <linux/sockios.h>
-
-/*
- * TIPC addressing primitives
- */
-
-struct tipc_socket_addr {
-	__u32 ref;
-	__u32 node;
-};
-
-struct tipc_service_addr {
-	__u32 type;
-	__u32 instance;
-};
-
-struct tipc_service_range {
-	__u32 type;
-	__u32 lower;
-	__u32 upper;
-};
-
-/*
- * Application-accessible service types
- */
-
-#define TIPC_NODE_STATE		0	/* node state service type */
-#define TIPC_TOP_SRV		1	/* topology server service type */
-#define TIPC_LINK_STATE		2	/* link state service type */
-#define TIPC_RESERVED_TYPES	64	/* lowest user-allowed service type */
-
-/*
- * Publication scopes when binding service / service range
- */
-enum tipc_scope {
-	TIPC_CLUSTER_SCOPE = 2, /* 0 can also be used */
-	TIPC_NODE_SCOPE    = 3
-};
-
-/*
- * Limiting values for messages
- */
-
-#define TIPC_MAX_USER_MSG_SIZE	66000U
-
-/*
- * Message importance levels
- */
-
-#define TIPC_LOW_IMPORTANCE		0
-#define TIPC_MEDIUM_IMPORTANCE		1
-#define TIPC_HIGH_IMPORTANCE		2
-#define TIPC_CRITICAL_IMPORTANCE	3
-
-/*
- * Msg rejection/connection shutdown reasons
- */
-
-#define TIPC_OK			0
-#define TIPC_ERR_NO_NAME	1
-#define TIPC_ERR_NO_PORT	2
-#define TIPC_ERR_NO_NODE	3
-#define TIPC_ERR_OVERLOAD	4
-#define TIPC_CONN_SHUTDOWN	5
-
-/*
- * TIPC topology subscription service definitions
- */
-
-#define TIPC_SUB_PORTS          0x01    /* filter: evt at each match */
-#define TIPC_SUB_SERVICE        0x02    /* filter: evt at first up/last down */
-#define TIPC_SUB_CANCEL         0x04    /* filter: cancel a subscription */
-
-#define TIPC_WAIT_FOREVER	(~0)	/* timeout for permanent subscription */
-
-struct tipc_subscr {
-	struct tipc_service_range seq;	/* range of interest */
-	__u32 timeout;			/* subscription duration (in ms) */
-	__u32 filter;			/* bitmask of filter options */
-	char usr_handle[8];		/* available for subscriber use */
-};
-
-#define TIPC_PUBLISHED		1	/* publication event */
-#define TIPC_WITHDRAWN		2	/* withdrawal event */
-#define TIPC_SUBSCR_TIMEOUT	3	/* subscription timeout event */
-
-struct tipc_event {
-	__u32 event;			/* event type */
-	__u32 found_lower;		/* matching range */
-	__u32 found_upper;		/*    "      "    */
-	struct tipc_socket_addr port;	/* associated socket */
-	struct tipc_subscr s;		/* associated subscription */
-};
-
-/*
- * Socket API
- */
-
-#ifndef AF_TIPC
-#define AF_TIPC		30
-#endif
-
-#ifndef PF_TIPC
-#define PF_TIPC		AF_TIPC
-#endif
-
-#ifndef SOL_TIPC
-#define SOL_TIPC	271
-#endif
-
-#define TIPC_ADDR_MCAST         1
-#define TIPC_SERVICE_RANGE      1
-#define TIPC_SERVICE_ADDR       2
-#define TIPC_SOCKET_ADDR        3
-
-struct sockaddr_tipc {
-	unsigned short family;
-	unsigned char  addrtype;
-	signed   char  scope;
-	union {
-		struct tipc_socket_addr id;
-		struct tipc_service_range nameseq;
-		struct {
-			struct tipc_service_addr name;
-			__u32 domain;
-		} name;
-	} addr;
-};
-
-/*
- * Ancillary data objects supported by recvmsg()
- */
-
-#define TIPC_ERRINFO	1	/* error info */
-#define TIPC_RETDATA	2	/* returned data */
-#define TIPC_DESTNAME	3	/* destination name */
-
-/*
- * TIPC-specific socket option names
- */
-
-#define TIPC_IMPORTANCE		127	/* Default: TIPC_LOW_IMPORTANCE */
-#define TIPC_SRC_DROPPABLE	128	/* Default: based on socket type */
-#define TIPC_DEST_DROPPABLE	129	/* Default: based on socket type */
-#define TIPC_CONN_TIMEOUT	130	/* Default: 8000 (ms)  */
-#define TIPC_NODE_RECVQ_DEPTH	131	/* Default: none (read only) */
-#define TIPC_SOCK_RECVQ_DEPTH	132	/* Default: none (read only) */
-#define TIPC_MCAST_BROADCAST    133     /* Default: TIPC selects. No arg */
-#define TIPC_MCAST_REPLICAST    134     /* Default: TIPC selects. No arg */
-#define TIPC_GROUP_JOIN         135     /* Takes struct tipc_group_req* */
-#define TIPC_GROUP_LEAVE        136     /* No argument */
-#define TIPC_SOCK_RECVQ_USED    137     /* Default: none (read only) */
-
-/*
- * Flag values
- */
-#define TIPC_GROUP_LOOPBACK     0x1  /* Receive copy of sent msg when match */
-#define TIPC_GROUP_MEMBER_EVTS  0x2  /* Receive membership events in socket */
-
-struct tipc_group_req {
-	__u32 type;      /* group id */
-	__u32 instance;  /* member id */
-	__u32 scope;     /* cluster/node */
-	__u32 flags;
-};
-
-/*
- * Maximum sizes of TIPC bearer-related names (including terminating NULL)
- * The string formatting for each name element is:
- * media: media
- * interface: media:interface name
- * link: node:interface-node:interface
- */
-#define TIPC_NODEID_LEN         16
-#define TIPC_MAX_MEDIA_NAME	16
-#define TIPC_MAX_IF_NAME	16
-#define TIPC_MAX_BEARER_NAME	32
-#define TIPC_MAX_LINK_NAME	68
-
-#define SIOCGETLINKNAME        SIOCPROTOPRIVATE
-#define SIOCGETNODEID          (SIOCPROTOPRIVATE + 1)
-
-struct tipc_sioc_ln_req {
-	__u32 peer;
-	__u32 bearer_id;
-	char linkname[TIPC_MAX_LINK_NAME];
-};
-
-struct tipc_sioc_nodeid_req {
-	__u32 peer;
-	char node_id[TIPC_NODEID_LEN];
-};
-
-/* The macros and functions below are deprecated:
- */
-
-#define TIPC_CFG_SRV		0
-#define TIPC_ZONE_SCOPE         1
-
-#define TIPC_ADDR_NAMESEQ	1
-#define TIPC_ADDR_NAME		2
-#define TIPC_ADDR_ID		3
-
-#define TIPC_NODE_BITS          12
-#define TIPC_CLUSTER_BITS       12
-#define TIPC_ZONE_BITS          8
-
-#define TIPC_NODE_OFFSET        0
-#define TIPC_CLUSTER_OFFSET     TIPC_NODE_BITS
-#define TIPC_ZONE_OFFSET        (TIPC_CLUSTER_OFFSET + TIPC_CLUSTER_BITS)
-
-#define TIPC_NODE_SIZE          ((1UL << TIPC_NODE_BITS) - 1)
-#define TIPC_CLUSTER_SIZE       ((1UL << TIPC_CLUSTER_BITS) - 1)
-#define TIPC_ZONE_SIZE          ((1UL << TIPC_ZONE_BITS) - 1)
-
-#define TIPC_NODE_MASK		(TIPC_NODE_SIZE << TIPC_NODE_OFFSET)
-#define TIPC_CLUSTER_MASK	(TIPC_CLUSTER_SIZE << TIPC_CLUSTER_OFFSET)
-#define TIPC_ZONE_MASK		(TIPC_ZONE_SIZE << TIPC_ZONE_OFFSET)
-
-#define TIPC_ZONE_CLUSTER_MASK (TIPC_ZONE_MASK | TIPC_CLUSTER_MASK)
-
-#define tipc_portid tipc_socket_addr
-#define tipc_name tipc_service_addr
-#define tipc_name_seq tipc_service_range
-
-static __inline__ __u32 tipc_addr(unsigned int zone,
-			      unsigned int cluster,
-			      unsigned int node)
-{
-	return (zone << TIPC_ZONE_OFFSET) |
-		(cluster << TIPC_CLUSTER_OFFSET) |
-		node;
-}
-
-static __inline__ unsigned int tipc_zone(__u32 addr)
-{
-	return addr >> TIPC_ZONE_OFFSET;
-}
-
-static __inline__ unsigned int tipc_cluster(__u32 addr)
-{
-	return (addr & TIPC_CLUSTER_MASK) >> TIPC_CLUSTER_OFFSET;
-}
-
-static __inline__ unsigned int tipc_node(__u32 addr)
-{
-	return addr & TIPC_NODE_MASK;
-}
-
-#endif
diff --git a/include/uapi/linux/tipc_sockets_diag.h b/include/uapi/linux/tipc_sockets_diag.h
deleted file mode 100644
index 21b766e..0000000
--- a/include/uapi/linux/tipc_sockets_diag.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* AF_TIPC sock_diag interface for querying open sockets */
-
-#ifndef __TIPC_SOCKETS_DIAG_H__
-#define __TIPC_SOCKETS_DIAG_H__
-
-#include <linux/types.h>
-#include <linux/sock_diag.h>
-
-/* Request */
-struct tipc_sock_diag_req {
-	__u8	sdiag_family;	/* must be AF_TIPC */
-	__u8	sdiag_protocol;	/* must be 0 */
-	__u16	pad;		/* must be 0 */
-	__u32	tidiag_states;	/* query*/
-};
-#endif /* __TIPC_SOCKETS_DIAG_H__ */
diff --git a/include/uapi/linux/types.h b/include/uapi/linux/types.h
deleted file mode 100644
index 999cb0f..0000000
--- a/include/uapi/linux/types.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _LINUX_TYPES_H
-#define _LINUX_TYPES_H
-
-#include <asm/types.h>
-
-#ifndef __ASSEMBLY__
-
-#include <linux/posix_types.h>
-
-
-/*
- * Below are truly Linux-specific types that should never collide with
- * any application/library that wants linux/types.h.
- */
-
-#ifdef __CHECKER__
-#define __bitwise__ __attribute__((bitwise))
-#else
-#define __bitwise__
-#endif
-#define __bitwise __bitwise__
-
-typedef __u16 __bitwise __le16;
-typedef __u16 __bitwise __be16;
-typedef __u32 __bitwise __le32;
-typedef __u32 __bitwise __be32;
-typedef __u64 __bitwise __le64;
-typedef __u64 __bitwise __be64;
-
-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)))
-
-typedef unsigned __bitwise __poll_t;
-
-#endif /*  __ASSEMBLY__ */
-#endif /* _LINUX_TYPES_H */
diff --git a/include/uapi/linux/vm_sockets_diag.h b/include/uapi/linux/vm_sockets_diag.h
deleted file mode 100644
index 6da42f9..0000000
--- a/include/uapi/linux/vm_sockets_diag.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* AF_VSOCK sock_diag(7) interface for querying open sockets */
-
-#ifndef __VM_SOCKETS_DIAG_H__
-#define __VM_SOCKETS_DIAG_H__
-
-#include <linux/types.h>
-
-/* Request */
-struct vsock_diag_req {
-	__u8	sdiag_family;	/* must be AF_VSOCK */
-	__u8	sdiag_protocol;	/* must be 0 */
-	__u16	pad;		/* must be 0 */
-	__u32	vdiag_states;	/* query bitmap (e.g. 1 << TCP_LISTEN) */
-	__u32	vdiag_ino;	/* must be 0 (reserved) */
-	__u32	vdiag_show;	/* must be 0 (reserved) */
-	__u32	vdiag_cookie[2];
-};
-
-/* Response */
-struct vsock_diag_msg {
-	__u8	vdiag_family;	/* AF_VSOCK */
-	__u8	vdiag_type;	/* SOCK_STREAM or SOCK_DGRAM */
-	__u8	vdiag_state;	/* sk_state (e.g. TCP_LISTEN) */
-	__u8	vdiag_shutdown; /* local RCV_SHUTDOWN | SEND_SHUTDOWN */
-	__u32   vdiag_src_cid;
-	__u32   vdiag_src_port;
-	__u32   vdiag_dst_cid;
-	__u32   vdiag_dst_port;
-	__u32	vdiag_ino;
-	__u32	vdiag_cookie[2];
-};
-
-#endif /* __VM_SOCKETS_DIAG_H__ */
diff --git a/include/uapi/linux/xdp_diag.h b/include/uapi/linux/xdp_diag.h
deleted file mode 100644
index 78b2591..0000000
--- a/include/uapi/linux/xdp_diag.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/*
- * xdp_diag: interface for query/monitor XDP sockets
- * Copyright(c) 2019 Intel Corporation.
- */
-
-#ifndef _LINUX_XDP_DIAG_H
-#define _LINUX_XDP_DIAG_H
-
-#include <linux/types.h>
-
-struct xdp_diag_req {
-	__u8	sdiag_family;
-	__u8	sdiag_protocol;
-	__u16	pad;
-	__u32	xdiag_ino;
-	__u32	xdiag_show;
-	__u32	xdiag_cookie[2];
-};
-
-struct xdp_diag_msg {
-	__u8	xdiag_family;
-	__u8	xdiag_type;
-	__u16	pad;
-	__u32	xdiag_ino;
-	__u32	xdiag_cookie[2];
-};
-
-#define XDP_SHOW_INFO		(1 << 0) /* Basic information */
-#define XDP_SHOW_RING_CFG	(1 << 1)
-#define XDP_SHOW_UMEM		(1 << 2)
-#define XDP_SHOW_MEMINFO	(1 << 3)
-
-enum {
-	XDP_DIAG_NONE,
-	XDP_DIAG_INFO,
-	XDP_DIAG_UID,
-	XDP_DIAG_RX_RING,
-	XDP_DIAG_TX_RING,
-	XDP_DIAG_UMEM,
-	XDP_DIAG_UMEM_FILL_RING,
-	XDP_DIAG_UMEM_COMPLETION_RING,
-	XDP_DIAG_MEMINFO,
-	__XDP_DIAG_MAX,
-};
-
-#define XDP_DIAG_MAX (__XDP_DIAG_MAX - 1)
-
-struct xdp_diag_info {
-	__u32	ifindex;
-	__u32	queue_id;
-};
-
-struct xdp_diag_ring {
-	__u32	entries; /*num descs */
-};
-
-#define XDP_DU_F_ZEROCOPY (1 << 0)
-
-struct xdp_diag_umem {
-	__u64	size;
-	__u32	id;
-	__u32	num_pages;
-	__u32	chunk_size;
-	__u32	headroom;
-	__u32	ifindex;
-	__u32	queue_id;
-	__u32	flags;
-	__u32	refs;
-};
-
-#endif /* _LINUX_XDP_DIAG_H */
diff --git a/include/utils.h b/include/utils.h
index 794d360..7310f4e 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef __UTILS_H__
 #define __UTILS_H__ 1
 
@@ -7,16 +6,10 @@
 #include <resolv.h>
 #include <stdlib.h>
 #include <stdbool.h>
-#include <time.h>
-
-#ifdef HAVE_LIBBSD
-#include <bsd/string.h>
-#endif
 
 #include "libnetlink.h"
 #include "ll_map.h"
 #include "rtm_map.h"
-#include "json_print.h"
 
 extern int preferred_family;
 extern int human_readable;
@@ -27,16 +20,26 @@
 extern int resolve_hosts;
 extern int oneline;
 extern int brief;
-extern int json;
-extern int pretty;
 extern int timestamp;
 extern int timestamp_short;
 extern const char * _SL_;
 extern int max_flush_loops;
 extern int batch_mode;
-extern int numeric;
 extern bool do_all;
 
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP	50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH	51
+#endif
+#ifndef IPPROTO_COMP
+#define IPPROTO_COMP	108
+#endif
+#ifndef IPSEC_PROTO_ANY
+#define IPSEC_PROTO_ANY	255
+#endif
+
 #ifndef CONFDIR
 #define CONFDIR		"/etc/iproute2"
 #endif
@@ -51,11 +54,6 @@
 #define NEXT_ARG_FWD() do { argv++; argc--; } while(0)
 #define PREV_ARG() do { argv--; argc++; } while(0)
 
-#define TIME_UNITS_PER_SEC	1000000
-#define NSEC_PER_USEC 1000
-#define NSEC_PER_MSEC 1000000
-#define NSEC_PER_SEC 1000000000LL
-
 typedef struct
 {
 	__u16 flags;
@@ -63,48 +61,10 @@
 	__s16 bitlen;
 	/* These next two fields match rtvia */
 	__u16 family;
-	__u32 data[64];
+	__u32 data[8];
 } inet_prefix;
 
-enum {
-	PREFIXLEN_SPECIFIED	= (1 << 0),
-	ADDRTYPE_INET		= (1 << 1),
-	ADDRTYPE_UNSPEC		= (1 << 2),
-	ADDRTYPE_MULTI		= (1 << 3),
-
-	ADDRTYPE_INET_UNSPEC	= ADDRTYPE_INET | ADDRTYPE_UNSPEC,
-	ADDRTYPE_INET_MULTI	= ADDRTYPE_INET | ADDRTYPE_MULTI
-};
-
-static inline void inet_prefix_reset(inet_prefix *p)
-{
-	p->flags = 0;
-}
-
-static inline bool is_addrtype_inet(const inet_prefix *p)
-{
-	return p->flags & ADDRTYPE_INET;
-}
-
-static inline bool is_addrtype_inet_unspec(const inet_prefix *p)
-{
-	return (p->flags & ADDRTYPE_INET_UNSPEC) == ADDRTYPE_INET_UNSPEC;
-}
-
-static inline bool is_addrtype_inet_multi(const inet_prefix *p)
-{
-	return (p->flags & ADDRTYPE_INET_MULTI) == ADDRTYPE_INET_MULTI;
-}
-
-static inline bool is_addrtype_inet_not_unspec(const inet_prefix *p)
-{
-	return (p->flags & ADDRTYPE_INET_UNSPEC) == ADDRTYPE_INET;
-}
-
-static inline bool is_addrtype_inet_not_multi(const inet_prefix *p)
-{
-	return (p->flags & ADDRTYPE_INET_MULTI) == ADDRTYPE_INET;
-}
+#define PREFIXLEN_SPECIFIED 1
 
 #define DN_MAXADDL 20
 #ifndef AF_DECnet
@@ -117,24 +77,19 @@
         unsigned char a_addr[DN_MAXADDL];
 };
 
+#define IPX_NODE_LEN 6
+
+struct ipx_addr {
+	u_int32_t ipx_net;
+	u_int8_t  ipx_node[IPX_NODE_LEN];
+};
+
 #ifndef AF_MPLS
 # define AF_MPLS 28
 #endif
-#ifndef IPPROTO_MPLS
-#define IPPROTO_MPLS	137
-#endif
 
-#ifndef CLOCK_TAI
-# define CLOCK_TAI 11
-#endif
-
-#ifndef AF_XDP
-# define AF_XDP 44
-# if AF_MAX < 45
-#  undef AF_MAX
-#  define AF_MAX 45
-# endif
-#endif
+/* Maximum number of labels the mpls helpers support */
+#define MPLS_MAX_LABELS 8
 
 __u32 get_addr32(const char *name);
 int get_addr_1(inet_prefix *dst, const char *arg, int family);
@@ -142,50 +97,35 @@
 int get_addr(inet_prefix *dst, const char *arg, int family);
 int get_prefix(inet_prefix *dst, char *arg, int family);
 int mask2bits(__u32 netmask);
-int get_addr_rta(inet_prefix *dst, const struct rtattr *rta, int family);
 int get_addr_ila(__u64 *val, const char *arg);
 
-int read_prop(const char *dev, char *prop, long *value);
-int get_hex(char c);
 int get_integer(int *val, const char *arg, int base);
 int get_unsigned(unsigned *val, const char *arg, int base);
 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
-int get_s64(__s64 *val, const char *arg, int base);
 int get_u64(__u64 *val, const char *arg, int base);
 int get_u32(__u32 *val, const char *arg, int base);
 int get_s32(__s32 *val, const char *arg, int base);
 int get_u16(__u16 *val, const char *arg, int base);
+int get_s16(__s16 *val, const char *arg, int base);
 int get_u8(__u8 *val, const char *arg, int base);
-int get_be64(__be64 *val, const char *arg, int base);
-int get_be32(__be32 *val, const char *arg, int base);
-int get_be16(__be16 *val, const char *arg, int base);
+int get_s8(__s8 *val, const char *arg, int base);
 int get_addr64(__u64 *ap, const char *cp);
 
-int hex2mem(const char *buf, uint8_t *mem, int count);
-char *hexstring_n2a(const __u8 *str, int len, char *buf, int blen);
-__u8 *hexstring_a2n(const char *str, __u8 *buf, int blen, unsigned int *len);
+char* hexstring_n2a(const __u8 *str, int len, char *buf, int blen);
+__u8* hexstring_a2n(const char *str, __u8 *buf, int blen);
 #define ADDR64_BUF_SIZE sizeof("xxxx:xxxx:xxxx:xxxx")
 int addr64_n2a(__u64 addr, char *buff, size_t len);
 
 int af_bit_len(int af);
+int af_byte_len(int af);
 
-const char *format_host_r(int af, int len, const void *addr,
+const char *format_host(int af, int len, const void *addr,
 			       char *buf, int buflen);
-#define format_host_rta_r(af, rta, buf, buflen)	\
-	format_host_r(af, RTA_PAYLOAD(rta), RTA_DATA(rta), \
-		      buf, buflen)
-
-const char *format_host(int af, int lne, const void *addr);
-#define format_host_rta(af, rta) \
-	format_host(af, RTA_PAYLOAD(rta), RTA_DATA(rta))
-const char *rt_addr_n2a_r(int af, int len, const void *addr,
+const char *rt_addr_n2a(int af, int len, const void *addr,
 			       char *buf, int buflen);
-const char *rt_addr_n2a(int af, int len, const void *addr);
-#define rt_addr_n2a_rta(af, rta) \
-	rt_addr_n2a(af, RTA_PAYLOAD(rta), RTA_DATA(rta))
 
 int read_family(const char *name);
 const char *family_name(int family);
@@ -194,16 +134,17 @@
 void invarg(const char *, const char *) __attribute__((noreturn));
 void duparg(const char *, const char *) __attribute__((noreturn));
 void duparg2(const char *, const char *) __attribute__((noreturn));
-int nodev(const char *dev);
-int check_ifname(const char *);
-int get_ifname(char *, const char *);
-const char *get_ifname_rta(int ifindex, const struct rtattr *rta);
-bool matches(const char *prefix, const char *string);
+int matches(const char *arg, const char *pattern);
 int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits);
-int inet_addr_match_rta(const inet_prefix *m, const struct rtattr *rta);
+
+const char *dnet_ntop(int af, const void *addr, char *str, size_t len);
+int dnet_pton(int af, const char *src, void *addr);
+
+const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
+int ipx_pton(int af, const char *src, void *addr);
 
 const char *mpls_ntop(int af, const void *addr, char *str, size_t len);
-int mpls_pton(int af, const char *src, void *addr, size_t alen);
+int mpls_pton(int af, const char *src, void *addr);
 
 extern int __iproute2_hz_internal;
 int __get_hz(void);
@@ -234,35 +175,10 @@
 	return group ? (1 << (group - 1)) : 0;
 }
 
-/* courtesy of bridge-utils */
-static inline unsigned long __tv_to_jiffies(const struct timeval *tv)
-{
-	unsigned long long jif;
-
-	jif = 1000000ULL * tv->tv_sec + tv->tv_usec;
-
-	return jif/10000;
-}
-
-static inline void __jiffies_to_tv(struct timeval *tv, unsigned long jiffies)
-{
-	unsigned long long tvusec;
-
-	tvusec = 10000ULL*jiffies;
-	tv->tv_sec = tvusec/1000000;
-	tv->tv_usec = tvusec - 1000000 * tv->tv_sec;
-}
-
-void print_escape_buf(const __u8 *buf, size_t len, const char *escape);
 
 int print_timestamp(FILE *fp);
 void print_nlmsg_timestamp(FILE *fp, const struct nlmsghdr *n);
 
-unsigned int print_name_and_link(const char *fmt,
-				 const char *name, struct rtattr *tb[]);
-
-#define BIT(nr)                 (1UL << (nr))
-
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
 #define BUILD_BUG_ON(cond) ((void)sizeof(char[1 - 2 * !!(cond)]))
@@ -293,30 +209,16 @@
 extern int cmdlineno;
 ssize_t getcmdline(char **line, size_t *len, FILE *in);
 int makeargs(char *line, char *argv[], int maxargs);
+int inet_get_addr(const char *src, __u32 *dst, struct in6_addr *dst6);
+
+struct iplink_req;
+int iplink_parse(int argc, char **argv, struct iplink_req *req,
+		char **name, char **type, char **link, char **dev,
+		int *group, int *index);
+
+int do_each_netns(int (*func)(char *nsname, void *arg), void *arg,
+		bool show_label);
 
 char *int_to_str(int val, char *buf);
-int get_guid(__u64 *guid, const char *arg);
-int get_real_family(int rtm_type, int rtm_family);
-
-int cmd_exec(const char *cmd, char **argv, bool do_fork,
-	     int (*setup)(void *), void *arg);
-int make_path(const char *path, mode_t mode);
-char *find_cgroup2_mount(void);
-int get_command_name(const char *pid, char *comm, size_t len);
-
-int get_rtnl_link_stats_rta(struct rtnl_link_stats64 *stats64,
-			    struct rtattr *tb[]);
-
-#ifdef NEED_STRLCPY
-size_t strlcpy(char *dst, const char *src, size_t size);
-size_t strlcat(char *dst, const char *src, size_t size);
-#endif
-
-void drop_cap(void);
-
-int get_time(unsigned int *time, const char *str);
-int get_time64(__s64 *time, const char *str);
-char *sprint_time(__u32 time, char *buf);
-char *sprint_time64(__s64 time, char *buf);
 
 #endif /* __UTILS_H__ */
diff --git a/include/xt-internal.h b/include/xt-internal.h
index 89c73e4..b8ea67d 100644
--- a/include/xt-internal.h
+++ b/include/xt-internal.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _XTABLES_INTERNAL_H
 #define _XTABLES_INTERNAL_H 1
 
diff --git a/include/xtables.h b/include/xtables.h
index 583619f..978ae0d 100644
--- a/include/xtables.h
+++ b/include/xtables.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _XTABLES_H
 #define _XTABLES_H
 
@@ -206,24 +205,6 @@
 	XTABLES_EXT_ALIAS = 1 << 0,
 };
 
-#if XTABLES_VERSION_CODE >= 12
-struct xt_xlate;
-
-struct xt_xlate_mt_params {
-	const void			*ip;
-	const struct xt_entry_match	*match;
-	int				numeric;
-	bool				escape_quotes;
-};
-
-struct xt_xlate_tg_params {
-	const void			*ip;
-	const struct xt_entry_target	*target;
-	int				numeric;
-	bool				escape_quotes;
-};
-#endif
-
 /* Include file for additions: new matches and targets. */
 struct xtables_match
 {
@@ -288,12 +269,6 @@
 	void (*x6_fcheck)(struct xt_fcheck_call *);
 	const struct xt_option_entry *x6_options;
 
-#if XTABLES_VERSION_CODE >= 12
-	/* Translate iptables to nft */
-	int (*xlate)(struct xt_xlate *xl,
-		     const struct xt_xlate_mt_params *params);
-#endif
-
 	/* Size of per-extension instance extra "global" scratch space */
 	size_t udata_size;
 
@@ -371,12 +346,6 @@
 	void (*x6_fcheck)(struct xt_fcheck_call *);
 	const struct xt_option_entry *x6_options;
 
-#if XTABLES_VERSION_CODE >= 12
-	/* Translate iptables to nft */
-	int (*xlate)(struct xt_xlate *xl,
-		     const struct xt_xlate_tg_params *params);
-#endif
-
 	size_t udata_size;
 
 	/* Ignore these men behind the curtain: */
diff --git a/ip/Android.mk b/ip/Android.mk
index 8e6724b..5c45bc7 100644
--- a/ip/Android.mk
+++ b/ip/Android.mk
@@ -1,34 +1,24 @@
 LOCAL_PATH := $(call my-dir)
 
-IPROUTE2_USE_CONFIG ?= no
-
-etc_iproute2_dir := etc/iproute2
-
 include $(CLEAR_VARS)
 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 iptoken.c \
-        ipxfrm.c xfrm_state.c xfrm_policy.c xfrm_monitor.c iplink_dummy.c \
-        iplink_vlan.c link_veth.c link_gre.c iplink_can.c iplink_xdp.c \
-        iplink_macvlan.c ipl2tp.c link_vti.c link_vti6.c link_xfrm.c \
-        iplink_vxlan.c tcp_metrics.c iplink_ipoib.c ipnetconf.c link_ip6tnl.c \
-        link_iptnl.c link_gre6.c iplink_bond.c iplink_bond_slave.c iplink_hsr.c \
-        iplink_bridge.c iplink_bridge_slave.c ipfou.c iplink_ipvlan.c \
-        iplink_geneve.c iplink_vrf.c iproute_lwtunnel.c ipmacsec.c ipila.c \
-        ipvrf.c iplink_xstats.c ipseg6.c iplink_netdevsim.c iplink_rmnet.c \
-        ipnexthop.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 ipl2tp.c \
+        ipfou.c iptoken.c tcp_metrics.c ipnetconf.c \
+        iproute_lwtunnel.c
 
 LOCAL_MODULE := ip
 
-ifeq ($(USE_IP_USER_BUILD),1)
-LOCAL_MODULE_TAGS := user eng
-else
-LOCAL_MODULE_TAGS := eng
-endif
+LOCAL_MODULE_TAGS := optional
 
-LOCAL_STATIC_LIBRARIES += libiprouteutil libnetlink
+LOCAL_SHARED_LIBRARIES := libc libm libdl
 
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include $(LOCAL_PATH)/../include/uapi
+LOCAL_SHARED_LIBRARIES += libiprouteutil libnetlink
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include
 
 LOCAL_CFLAGS := \
     -O2 -g \
@@ -39,92 +29,10 @@
     -Wno-sign-compare \
     -Wno-unused-parameter \
     -Werror \
-    -D_GNU_SOURCE
-
-ifeq ($(BUILD_EUREKA),true)
-LOCAL_CFLAGS += -DHAVE_SETNS
-endif
+    -D_GNU_SOURCE \
+    -DHAVE_SETNS \
 
 LOCAL_LDFLAGS := -Wl,-export-dynamic -Wl,--no-gc-sections
 
-ifeq ($(IPROUTE2_USE_CONFIG), yes)
-LOCAL_REQUIRED_MODULES += ematch_map group rt_dsfield rt_protos rt_realms rt_scopes rt_tables
-endif
-
-LOCAL_REQUIRED_MODULES += rtmon
-
 include $(BUILD_EXECUTABLE)
 
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := rtmon.c
-LOCAL_MODULE := rtmon
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include $(LOCAL_PATH)/../include/uapi
-LOCAL_STATIC_LIBRARIES += libiprouteutil libnetlink
-LOCAL_CFLAGS := \
-    -O2 -g \
-    -W -Wall \
-    -Wno-implicit-function-declaration \
-    -Wno-missing-field-initializers \
-    -Wno-pointer-arith \
-    -Wno-sign-compare \
-    -Wno-unused-parameter \
-    -Werror \
-    -D_GNU_SOURCE
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := ematch_map
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT)/$(etc_iproute2_dir)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := ../$(etc_iproute2_dir)/$(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := group
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT)/$(etc_iproute2_dir)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := ../$(etc_iproute2_dir)/$(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := rt_dsfield
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT)/$(etc_iproute2_dir)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := ../$(etc_iproute2_dir)/$(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := rt_protos
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT)/$(etc_iproute2_dir)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := ../$(etc_iproute2_dir)/$(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := rt_realms
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT)/$(etc_iproute2_dir)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := ../$(etc_iproute2_dir)/$(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := rt_scopes
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT)/$(etc_iproute2_dir)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := ../$(etc_iproute2_dir)/$(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := rt_tables
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT)/$(etc_iproute2_dir)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := ../$(etc_iproute2_dir)/$(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
diff --git a/ip/Makefile b/ip/Makefile
index 5ab78d7..f3d2987 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -1,21 +1,21 @@
-# SPDX-License-Identifier: GPL-2.0
 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 iptoken.o \
-    ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o iplink_dummy.o \
-    iplink_ifb.o iplink_nlmon.o iplink_team.o iplink_vcan.o iplink_vxcan.o \
-    iplink_vlan.o link_veth.o link_gre.o iplink_can.o iplink_xdp.o \
-    iplink_macvlan.o ipl2tp.o link_vti.o link_vti6.o link_xfrm.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 ipl2tp.o link_vti.o link_vti6.o \
     iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o link_ip6tnl.o \
     link_iptnl.o link_gre6.o iplink_bond.o iplink_bond_slave.o iplink_hsr.o \
     iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \
-    iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \
-    ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o \
-    ipnexthop.o
+    iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o
 
 RTMONOBJ=rtmon.o
 
-include ../config.mk
+include ../Config
+
+ifeq ($(IP_CONFIG_SETNS),y)
+	CFLAGS += -DHAVE_SETNS
+endif
 
 ALLOBJ=$(IPOBJ) $(RTMONOBJ)
 SCRIPTS=ifcfg rtpr routel routef
@@ -24,10 +24,8 @@
 all: $(TARGETS) $(SCRIPTS)
 
 ip: $(IPOBJ) $(LIBNETLINK)
-	$(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
 
 rtmon: $(RTMONOBJ)
-	$(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
 
 install: all
 	install -m 0755 $(TARGETS) $(DESTDIR)$(SBINDIR)
diff --git a/ip/ifcfg b/ip/ifcfg
index 5b34dec..083d9df 100644
--- a/ip/ifcfg
+++ b/ip/ifcfg
@@ -1,5 +1,4 @@
-#! /bin/sh
-# SPDX-License-Identifier: GPL-2.0
+#! /bin/bash
 
 CheckForwarding () {
   local sbase fwd
@@ -7,7 +6,7 @@
   fwd=0
   if [ -d $sbase ]; then
     for dir in $sbase/*/forwarding; do
-      fwd=$(( fwd + $(cat "$dir") ))
+      fwd=$[$fwd + `cat $dir`]
     done
   else
     fwd=2
@@ -128,12 +127,12 @@
 arping -q -A -c 1 -I $dev $ipaddr
 noarp=$?
 ( sleep 2 ;
-  arping -q -U -c 1 -I $dev $ipaddr ) >/dev/null 2>&1 </dev/null &
+  arping -q -U -c 1 -I $dev $ipaddr ) >& /dev/null </dev/null &
 
-ip route add unreachable 224.0.0.0/24 >/dev/null 2>&1
-ip route add unreachable 255.255.255.255 >/dev/null 2>&1
-if [ "`ip link ls $dev | grep -c MULTICAST`" -ge 1 ]; then
-  ip route add 224.0.0.0/4 dev $dev scope global >/dev/null 2>&1
+ip route add unreachable 224.0.0.0/24 >& /dev/null
+ip route add unreachable 255.255.255.255 >& /dev/null
+if [ `ip link ls $dev | grep -c MULTICAST` -ge 1 ]; then
+  ip route add 224.0.0.0/4 dev $dev scope global >& /dev/null
 fi
 
 if [ $fwd -eq 0 ]; then
diff --git a/ip/ila_common.h b/ip/ila_common.h
deleted file mode 100644
index f99c267..0000000
--- a/ip/ila_common.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _ILA_COMMON_H_
-#define _ILA_COMMON_H_
-
-#include <linux/ila.h>
-#include <string.h>
-
-static inline char *ila_csum_mode2name(__u8 csum_mode)
-{
-	switch (csum_mode) {
-	case ILA_CSUM_ADJUST_TRANSPORT:
-		return "adj-transport";
-	case ILA_CSUM_NEUTRAL_MAP:
-		return "neutral-map";
-	case ILA_CSUM_NO_ACTION:
-		return "no-action";
-	case ILA_CSUM_NEUTRAL_MAP_AUTO:
-		return "neutral-map-auto";
-	default:
-		return "unknown";
-	}
-}
-
-static inline int ila_csum_name2mode(char *name)
-{
-	if (strcmp(name, "adj-transport") == 0)
-		return ILA_CSUM_ADJUST_TRANSPORT;
-	else if (strcmp(name, "neutral-map") == 0)
-		return ILA_CSUM_NEUTRAL_MAP;
-	else if (strcmp(name, "neutral-map-auto") == 0)
-		return ILA_CSUM_NEUTRAL_MAP_AUTO;
-	else if (strcmp(name, "no-action") == 0)
-		return ILA_CSUM_NO_ACTION;
-	else if (strcmp(name, "neutral-map-auto") == 0)
-		return ILA_CSUM_NEUTRAL_MAP_AUTO;
-	else
-		return -1;
-}
-
-static inline char *ila_ident_type2name(__u8 ident_type)
-{
-	switch (ident_type) {
-	case ILA_ATYPE_IID:
-		return "iid";
-	case ILA_ATYPE_LUID:
-		return "luid";
-	case ILA_ATYPE_VIRT_V4:
-		return "virt-v4";
-	case ILA_ATYPE_VIRT_UNI_V6:
-		return "virt-uni-v6";
-	case ILA_ATYPE_VIRT_MULTI_V6:
-		return "virt-multi-v6";
-	case ILA_ATYPE_NONLOCAL_ADDR:
-		return "nonlocal-addr";
-	case ILA_ATYPE_USE_FORMAT:
-		return "use-format";
-	default:
-		return "unknown";
-	}
-}
-
-static inline int ila_ident_name2type(char *name)
-{
-	if (!strcmp(name, "luid"))
-		return ILA_ATYPE_LUID;
-	else if (!strcmp(name, "use-format"))
-		return ILA_ATYPE_USE_FORMAT;
-#if 0 /* No kernel support for configuring these yet */
-	else if (!strcmp(name, "iid"))
-		return ILA_ATYPE_IID;
-	else if (!strcmp(name, "virt-v4"))
-		return ILA_ATYPE_VIRT_V4;
-	else if (!strcmp(name, "virt-uni-v6"))
-		return ILA_ATYPE_VIRT_UNI_V6;
-	else if (!strcmp(name, "virt-multi-v6"))
-		return ILA_ATYPE_VIRT_MULTI_V6;
-	else if (!strcmp(name, "nonlocal-addr"))
-		return ILA_ATYPE_NONLOCAL_ADDR;
-#endif
-	else
-		return -1;
-}
-
-static inline char *ila_hook_type2name(__u8 hook_type)
-{
-	switch (hook_type) {
-	case ILA_HOOK_ROUTE_OUTPUT:
-		return "output";
-	case ILA_HOOK_ROUTE_INPUT:
-		return "input";
-	default:
-		return "unknown";
-	}
-}
-
-static inline int ila_hook_name2type(char *name)
-{
-	if (!strcmp(name, "output"))
-		return ILA_HOOK_ROUTE_OUTPUT;
-	else if (!strcmp(name, "input"))
-		return ILA_HOOK_ROUTE_INPUT;
-	else
-		return -1;
-}
-
-#endif /* _ILA_COMMON_H_ */
diff --git a/ip/ip.c b/ip/ip.c
index fed26f8..5162100 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -12,6 +12,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -29,14 +30,14 @@
 int use_iec;
 int show_stats;
 int show_details;
+int resolve_hosts;
 int oneline;
 int brief;
-int json;
 int timestamp;
+const char *_SL_;
 int force;
 int max_flush_loops = 10;
 int batch_mode;
-int numeric;
 bool do_all;
 
 struct rtnl_handle rth = { .fd = -1 };
@@ -46,20 +47,18 @@
 static void usage(void)
 {
 	fprintf(stderr,
-		"Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n"
-		"       ip [ -force ] -batch filename\n"
-		"where  OBJECT := { link | address | addrlabel | route | rule | neigh | ntable |\n"
-		"                   tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm |\n"
-		"                   netns | l2tp | fou | macsec | tcp_metrics | token | netconf | ila |\n"
-		"                   vrf | sr | nexthop }\n"
-		"       OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
-		"                    -h[uman-readable] | -iec | -j[son] | -p[retty] |\n"
-		"                    -f[amily] { inet | inet6 | mpls | bridge | link } |\n"
-		"                    -4 | -6 | -I | -D | -M | -B | -0 |\n"
-		"                    -l[oops] { maximum-addr-flush-attempts } | -br[ief] |\n"
-		"                    -o[neline] | -t[imestamp] | -ts[hort] | -b[atch] [filename] |\n"
-		"                    -rc[vbuf] [size] | -n[etns] name | -N[umeric] | -a[ll] |\n"
-		"                    -c[olor]}\n");
+"Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n"
+"       ip [ -force ] -batch filename\n"
+"where  OBJECT := { link | address | addrlabel | route | rule | neighbor | ntable |\n"
+"                   tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm |\n"
+"                   netns | l2tp | fou | tcp_metrics | token | netconf }\n"
+"       OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
+"                    -h[uman-readable] | -iec |\n"
+"                    -f[amily] { inet | inet6 | ipx | dnet | mpls | bridge | link } |\n"
+"                    -4 | -6 | -I | -D | -B | -0 |\n"
+"                    -l[oops] { maximum-addr-flush-attempts } | -br[ief] |\n"
+"                    -o[neline] | -t[imestamp] | -ts[hort] | -b[atch] [filename] |\n"
+"                    -rc[vbuf] [size] | -n[etns] name | -a[ll] | -c[olor]}\n");
 	exit(-1);
 }
 
@@ -85,8 +84,6 @@
 	{ "link",	do_iplink },
 	{ "l2tp",	do_ipl2tp },
 	{ "fou",	do_ipfou },
-	{ "ila",	do_ipila },
-	{ "macsec",	do_ipmacsec },
 	{ "tunnel",	do_iptunnel },
 	{ "tunl",	do_iptunnel },
 	{ "tuntap",	do_iptuntap },
@@ -100,9 +97,6 @@
 	{ "mrule",	do_multirule },
 	{ "netns",	do_netns },
 	{ "netconf",	do_ipnetconf },
-	{ "vrf",	do_ipvrf},
-	{ "sr",		do_seg6 },
-	{ "nexthop",	do_ipnh },
 	{ "help",	do_help },
 	{ 0 }
 };
@@ -120,6 +114,7 @@
 	return EXIT_FAILURE;
 }
 
+#ifndef ANDROID
 static int batch(const char *name)
 {
 	char *line = NULL;
@@ -168,25 +163,15 @@
 	rtnl_close(&rth);
 	return ret;
 }
+#endif
 
 
 int main(int argc, char **argv)
 {
 	char *basename;
+#ifndef ANDROID
 	char *batch_file = NULL;
-	int color = 0;
-
-	/* to run vrf exec without root, capabilities might be set, drop them
-	 * if not needed as the first thing.
-	 * execv will drop them for the child command.
-	 * vrf exec requires:
-	 * - cap_dac_override to create the cgroup subdir in /sys
-	 * - cap_sys_admin to load the BPF program
-	 * - cap_net_admin to set the socket into the cgroup
-	 */
-	if (argc < 3 || strcmp(argv[1], "vrf") != 0 ||
-			strcmp(argv[2], "exec") != 0)
-		drop_cap();
+#endif
 
 	basename = strrchr(argv[0], '/');
 	if (basename == NULL)
@@ -228,6 +213,8 @@
 			preferred_family = AF_INET6;
 		} else if (strcmp(opt, "-0") == 0) {
 			preferred_family = AF_PACKET;
+		} else if (strcmp(opt, "-I") == 0) {
+			preferred_family = AF_IPX;
 		} else if (strcmp(opt, "-D") == 0) {
 			preferred_family = AF_DECnet;
 		} else if (strcmp(opt, "-M") == 0) {
@@ -253,23 +240,25 @@
 		} else if (matches(opt, "-tshort") == 0) {
 			++timestamp;
 			++timestamp_short;
+#if 0
+		} else if (matches(opt, "-numeric") == 0) {
+			rtnl_names_numeric++;
+#endif
 		} else if (matches(opt, "-Version") == 0) {
 			printf("ip utility, iproute2-ss%s\n", SNAPSHOT);
 			exit(0);
 		} else if (matches(opt, "-force") == 0) {
 			++force;
+#ifndef ANDROID
 		} else if (matches(opt, "-batch") == 0) {
 			argc--;
 			argv++;
 			if (argc <= 1)
 				usage();
 			batch_file = argv[1];
+#endif
 		} else if (matches(opt, "-brief") == 0) {
 			++brief;
-		} else if (matches(opt, "-json") == 0) {
-			++json;
-		} else if (matches(opt, "-pretty") == 0) {
-			++pretty;
 		} else if (matches(opt, "-rcvbuf") == 0) {
 			unsigned int size;
 
@@ -283,15 +272,14 @@
 				exit(-1);
 			}
 			rcvbuf = size;
-		} else if (matches_color(opt, &color)) {
+		} else if (matches(opt, "-color") == 0) {
+			enable_color();
 		} else if (matches(opt, "-help") == 0) {
 			usage();
 		} else if (matches(opt, "-netns") == 0) {
 			NEXT_ARG();
 			if (netns_switch(argv[1]))
 				exit(-1);
-		} else if (matches(opt, "-Numeric") == 0) {
-			++numeric;
 		} else if (matches(opt, "-all") == 0) {
 			do_all = true;
 		} else {
@@ -305,16 +293,14 @@
 
 	_SL_ = oneline ? "\\" : "\n";
 
-	check_enable_color(color, json);
-
+#ifndef ANDROID
 	if (batch_file)
 		return batch(batch_file);
+#endif
 
 	if (rtnl_open(&rth, 0) < 0)
 		exit(1);
 
-	rtnl_set_strict_dump(&rth);
-
 	if (strlen(basename) > 2)
 		return do_cmd(basename+2, argc, argv);
 
diff --git a/ip/ip6tunnel.c b/ip/ip6tunnel.c
index 5399f91..7a3cd04 100644
--- a/ip/ip6tunnel.c
+++ b/ip/ip6tunnel.c
@@ -46,107 +46,96 @@
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: ip -f inet6 tunnel { add | change | del | show } [ NAME ]\n"
-		"          [ mode { ip6ip6 | ipip6 | ip6gre | vti6 | any } ]\n"
-		"          [ remote ADDR local ADDR ] [ dev PHYS_DEV ]\n"
-		"          [ encaplimit ELIM ]\n"
-		"          [ hoplimit TTL ] [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n"
-		"          [ dscp inherit ]\n"
-		"          [ [no]allow-localremote ]\n"
-		"          [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n"
-		"\n"
-		"Where: NAME      := STRING\n"
-		"       ADDR      := IPV6_ADDRESS\n"
-		"       ELIM      := { none | 0..255 }(default=%d)\n"
-		"       TTL       := 0..255 (default=%d)\n"
-		"       TCLASS    := { 0x0..0xff | inherit }\n"
-		"       FLOWLABEL := { 0x0..0xfffff | inherit }\n"
-		"       KEY       := { DOTTED_QUAD | NUMBER }\n",
-		IPV6_DEFAULT_TNL_ENCAP_LIMIT,
+	fprintf(stderr, "Usage: ip -f inet6 tunnel { add | change | del | show } [ NAME ]\n");
+	fprintf(stderr, "          [ mode { ip6ip6 | ipip6 | ip6gre | vti6 | any } ]\n");
+	fprintf(stderr, "          [ remote ADDR local ADDR ] [ dev PHYS_DEV ]\n");
+	fprintf(stderr, "          [ encaplimit ELIM ]\n");
+	fprintf(stderr ,"          [ hoplimit TTL ] [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n");
+	fprintf(stderr, "          [ dscp inherit ]\n");
+	fprintf(stderr, "          [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME      := STRING\n");
+	fprintf(stderr, "       ADDR      := IPV6_ADDRESS\n");
+	fprintf(stderr, "       ELIM      := { none | 0..255 }(default=%d)\n",
+		IPV6_DEFAULT_TNL_ENCAP_LIMIT);
+	fprintf(stderr, "       TTL       := 0..255 (default=%d)\n",
 		DEFAULT_TNL_HOP_LIMIT);
+	fprintf(stderr, "       TCLASS    := { 0x0..0xff | inherit }\n");
+	fprintf(stderr, "       FLOWLABEL := { 0x0..0xfffff | inherit }\n");
+	fprintf(stderr, "       KEY       := { DOTTED_QUAD | NUMBER }\n");
 	exit(-1);
 }
 
-static void print_tunnel(const void *t)
+static void print_tunnel(struct ip6_tnl_parm2 *p)
 {
-	const struct ip6_tnl_parm2 *p = t;
-	SPRINT_BUF(b1);
+	char s1[1024];
+	char s2[1024];
 
 	/* Do not use format_host() for local addr,
 	 * symbolic name will not be useful.
 	 */
-	open_json_object(NULL);
-	print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", "%s: ", p->name);
-	snprintf(b1, sizeof(b1), "%s/ipv6", tnl_strproto(p->proto));
-	print_string(PRINT_ANY, "mode", "%s ", b1);
-	print_string(PRINT_FP, NULL, "%s", "remote ");
-	print_color_string(PRINT_ANY, COLOR_INET6, "remote", "%s ",
-			   format_host_r(AF_INET6, 16, &p->raddr, b1, sizeof(b1)));
-	print_string(PRINT_FP, NULL, "%s", "local ");
-	print_color_string(PRINT_ANY, COLOR_INET6, "local", "%s",
-			   rt_addr_n2a_r(AF_INET6, 16, &p->laddr, b1, sizeof(b1)));
-
+	printf("%s: %s/ipv6 remote %s local %s",
+	       p->name,
+	       tnl_strproto(p->proto),
+	       format_host(AF_INET6, 16, &p->raddr, s1, sizeof(s1)),
+	       rt_addr_n2a(AF_INET6, 16, &p->laddr, s2, sizeof(s2)));
 	if (p->link) {
 		const char *n = ll_index_to_name(p->link);
-
 		if (n)
-			print_string(PRINT_ANY, "link", " dev %s", n);
+			printf(" dev %s", n);
 	}
 
 	if (p->flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
-		print_null(PRINT_ANY, "ip6_tnl_f_ign_encap_limit",
-			   " encaplimit none", NULL);
+		printf(" encaplimit none");
 	else
-		print_uint(PRINT_ANY, "encap_limit", " encaplimit %u",
-			   p->encap_limit);
+		printf(" encaplimit %u", p->encap_limit);
 
-	if (p->hop_limit)
-		print_uint(PRINT_ANY, "hoplimit", " hoplimit %u", p->hop_limit);
-	else
-		print_string(PRINT_FP, "hoplimit", " hoplimit %s", "inherit");
+	printf(" hoplimit %u", p->hop_limit);
 
-	if (p->flags & IP6_TNL_F_USE_ORIG_TCLASS) {
-		print_null(PRINT_ANY, "ip6_tnl_f_use_orig_tclass",
-			   " tclass inherit", NULL);
-	} else {
+	if (p->flags & IP6_TNL_F_USE_ORIG_TCLASS)
+		printf(" tclass inherit");
+	else {
 		__u32 val = ntohl(p->flowinfo & IP6_FLOWINFO_TCLASS);
-
-		snprintf(b1, sizeof(b1), "0x%02x", (__u8)(val >> 20));
-		print_string(PRINT_ANY, "tclass", " tclass %s", b1);
+		printf(" tclass 0x%02x", (__u8)(val >> 20));
 	}
 
-	if (p->flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) {
-		print_null(PRINT_ANY, "ip6_tnl_f_use_orig_flowlabel",
-			   " flowlabel inherit", NULL);
-	} else {
-		__u32 val = ntohl(p->flowinfo & IP6_FLOWINFO_FLOWLABEL);
+	if (p->flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
+		printf(" flowlabel inherit");
+	else
+		printf(" flowlabel 0x%05x", ntohl(p->flowinfo & IP6_FLOWINFO_FLOWLABEL));
 
-		snprintf(b1, sizeof(b1), "0x%05x", val);
-		print_string(PRINT_ANY, "flowlabel", " flowlabel %s", b1);
-	}
-
-	snprintf(b1, sizeof(b1), "0x%08x", ntohl(p->flowinfo));
-	print_string(PRINT_ANY, "flowinfo", " (flowinfo %s)", b1);
+	printf(" (flowinfo 0x%08x)", ntohl(p->flowinfo));
 
 	if (p->flags & IP6_TNL_F_RCV_DSCP_COPY)
-		print_null(PRINT_ANY, "ip6_tnl_f_rcv_dscp_copy",
-			   " dscp inherit", NULL);
+		printf(" dscp inherit");
 
-	if (p->flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE)
-		print_null(PRINT_ANY, "ip6_tnl_f_allow_local_remote",
-			   " allow-localremote", NULL);
+	if (p->proto == IPPROTO_GRE) {
+		if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key)
+			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 %u", ntohl(p->i_key));
+			if (p->o_flags & GRE_KEY)
+				printf(" okey %u", ntohl(p->o_key));
+		}
 
-	tnl_print_gre_flags(p->proto, p->i_flags, p->o_flags,
-			    p->i_key, p->o_key);
-
-	close_json_object();
+		if (p->i_flags & GRE_SEQ)
+			printf("%s  Drop packets out of sequence.", _SL_);
+		if (p->i_flags & GRE_CSUM)
+			printf("%s  Checksum in received packet is required.", _SL_);
+		if (p->o_flags & GRE_SEQ)
+			printf("%s  Sequence packets on output.", _SL_);
+		if (p->o_flags & GRE_CSUM)
+			printf("%s  Checksum output packets.", _SL_);
+	}
 }
 
 static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm2 *p)
 {
 	int count = 0;
-	const char *medium = NULL;
+	char medium[IFNAMSIZ];
+
+	memset(medium, 0, sizeof(medium));
 
 	while (argc > 0) {
 		if (strcmp(*argv, "mode") == 0) {
@@ -169,31 +158,32 @@
 				 strcmp(*argv, "any") == 0)
 				p->proto = 0;
 			else {
-				fprintf(stderr, "Unknown tunnel mode \"%s\"\n", *argv);
+				fprintf(stderr,"Unknown tunnel mode \"%s\"\n", *argv);
 				exit(-1);
 			}
 		} else if (strcmp(*argv, "remote") == 0) {
 			inet_prefix raddr;
-
 			NEXT_ARG();
-			get_addr(&raddr, *argv, AF_INET6);
+			get_prefix(&raddr, *argv, preferred_family);
+			if (raddr.family == AF_UNSPEC)
+				invarg("\"remote\" address family is AF_UNSPEC", *argv);
 			memcpy(&p->raddr, &raddr.data, sizeof(p->raddr));
 		} else if (strcmp(*argv, "local") == 0) {
 			inet_prefix laddr;
-
 			NEXT_ARG();
-			get_addr(&laddr, *argv, AF_INET6);
+			get_prefix(&laddr, *argv, preferred_family);
+			if (laddr.family == AF_UNSPEC)
+				invarg("\"local\" address family is AF_UNSPEC", *argv);
 			memcpy(&p->laddr, &laddr.data, sizeof(p->laddr));
 		} else if (strcmp(*argv, "dev") == 0) {
 			NEXT_ARG();
-			medium = *argv;
+			strncpy(medium, *argv, IFNAMSIZ - 1);
 		} else if (strcmp(*argv, "encaplimit") == 0) {
 			NEXT_ARG();
 			if (strcmp(*argv, "none") == 0) {
 				p->flags |= IP6_TNL_F_IGN_ENCAP_LIMIT;
 			} else {
 				__u8 uval;
-
 				if (get_u8(&uval, *argv, 0) < -1)
 					invarg("invalid ELIM", *argv);
 				p->encap_limit = uval;
@@ -203,7 +193,6 @@
 			   strcmp(*argv, "ttl") == 0 ||
 			   strcmp(*argv, "hlim") == 0) {
 			__u8 uval;
-
 			NEXT_ARG();
 			if (get_u8(&uval, *argv, 0))
 				invarg("invalid TTL", *argv);
@@ -213,7 +202,6 @@
 			   strcmp(*argv, "tos") == 0 ||
 			   matches(*argv, "dsfield") == 0) {
 			__u8 uval;
-
 			NEXT_ARG();
 			p->flowinfo &= ~IP6_FLOWINFO_TCLASS;
 			if (strcmp(*argv, "inherit") == 0)
@@ -227,7 +215,6 @@
 		} else if (strcmp(*argv, "flowlabel") == 0 ||
 			   strcmp(*argv, "fl") == 0) {
 			__u32 uval;
-
 			NEXT_ARG();
 			p->flowinfo &= ~IP6_FLOWINFO_FLOWLABEL;
 			if (strcmp(*argv, "inherit") == 0)
@@ -245,10 +232,6 @@
 			if (strcmp(*argv, "inherit") != 0)
 				invarg("not inherit", *argv);
 			p->flags |= IP6_TNL_F_RCV_DSCP_COPY;
-		} else if (strcmp(*argv, "allow-localremote") == 0) {
-			p->flags |= IP6_TNL_F_ALLOW_LOCAL_REMOTE;
-		} else if (strcmp(*argv, "noallow-localremote") == 0) {
-			p->flags &= ~IP6_TNL_F_ALLOW_LOCAL_REMOTE;
 		} else if (strcmp(*argv, "key") == 0) {
 			NEXT_ARG();
 			p->i_flags |= GRE_KEY;
@@ -283,11 +266,10 @@
 				usage();
 			if (p->name[0])
 				duparg2("name", *argv);
-			if (get_ifname(p->name, *argv))
-				invarg("\"name\" not a valid ifname", *argv);
+			strncpy(p->name, *argv, IFNAMSIZ - 1);
 			if (cmd == SIOCCHGTUNNEL && count == 0) {
-				struct ip6_tnl_parm2 old_p = {};
-
+				struct ip6_tnl_parm2 old_p;
+				memset(&old_p, 0, sizeof(old_p));
 				if (tnl_get_ioctl(*argv, &old_p))
 					return -1;
 				*p = old_p;
@@ -296,10 +278,12 @@
 		count++;
 		argc--; argv++;
 	}
-	if (medium) {
+	if (medium[0]) {
 		p->link = ll_name_to_index(medium);
-		if (!p->link)
-			return nodev(medium);
+		if (p->link == 0) {
+			fprintf(stderr, "Cannot find device \"%s\"\n", medium);
+			return -1;
+		}
 	}
 	return 0;
 }
@@ -314,30 +298,19 @@
 	}
 }
 
-static void ip6_tnl_parm_initialize(const struct tnl_print_nlmsg_info *info)
+/*
+ * @p1: user specified parameter
+ * @p2: database entry
+ */
+static int ip6_tnl_parm_match(const struct ip6_tnl_parm2 *p1,
+			      const struct ip6_tnl_parm2 *p2)
 {
-	const struct ifinfomsg *ifi = info->ifi;
-	const struct ip6_tnl_parm2 *p1 = info->p1;
-	struct ip6_tnl_parm2 *p2 = info->p2;
-
-	ip6_tnl_parm_init(p2, 0);
-	if (ifi->ifi_type == ARPHRD_IP6GRE)
-		p2->proto = IPPROTO_GRE;
-	p2->link = ifi->ifi_index;
-	strcpy(p2->name, p1->name);
-}
-
-static bool ip6_tnl_parm_match(const struct tnl_print_nlmsg_info *info)
-{
-	const struct ip6_tnl_parm2 *p1 = info->p1;
-	const struct ip6_tnl_parm2 *p2 = info->p2;
-
 	return ((!p1->link || p1->link == p2->link) &&
 		(!p1->name[0] || strcmp(p1->name, p2->name) == 0) &&
-		(IN6_IS_ADDR_UNSPECIFIED(&p1->laddr) ||
-		 IN6_ARE_ADDR_EQUAL(&p1->laddr, &p2->laddr)) &&
-		(IN6_IS_ADDR_UNSPECIFIED(&p1->raddr) ||
-		 IN6_ARE_ADDR_EQUAL(&p1->raddr, &p2->raddr)) &&
+		(memcmp(&p1->laddr, &in6addr_any, sizeof(p1->laddr)) == 0 ||
+		 memcmp(&p1->laddr, &p2->laddr, sizeof(p1->laddr)) == 0) &&
+		(memcmp(&p1->raddr, &in6addr_any, sizeof(p1->raddr)) == 0 ||
+		 memcmp(&p1->raddr, &p2->raddr, sizeof(p1->raddr)) == 0) &&
 		(!p1->proto || !p2->proto || p1->proto == p2->proto) &&
 		(!p1->encap_limit || p1->encap_limit == p2->encap_limit) &&
 		(!p1->hop_limit || p1->hop_limit == p2->hop_limit) &&
@@ -348,33 +321,91 @@
 		(!p1->flags || (p1->flags & p2->flags)));
 }
 
+static int do_tunnels_list(struct ip6_tnl_parm2 *p)
+{
+	char buf[512];
+	int err = -1;
+	FILE *fp = fopen("/proc/net/dev", "r");
+	if (fp == NULL) {
+		perror("fopen");
+		return -1;
+	}
+
+	/* skip two lines at the begenning of the file */
+	if (!fgets(buf, sizeof(buf), fp) ||
+	    !fgets(buf, sizeof(buf), fp)) {
+		fprintf(stderr, "/proc/net/dev read error\n");
+		goto end;
+	}
+
+	while (fgets(buf, sizeof(buf), fp) != NULL) {
+		char name[IFNAMSIZ];
+		int index, type;
+		struct ip6_tnl_parm2 p1;
+		char *ptr;
+
+		buf[sizeof(buf) - 1] = '\0';
+		if ((ptr = strchr(buf, ':')) == NULL ||
+		    (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
+			fprintf(stderr, "Wrong format for /proc/net/dev. Giving up.\n");
+			goto end;
+		}
+		if (p->name[0] && strcmp(p->name, name))
+			continue;
+		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;
+		}
+		if (type != ARPHRD_TUNNEL6 && type != ARPHRD_IP6GRE)
+			continue;
+		memset(&p1, 0, sizeof(p1));
+		ip6_tnl_parm_init(&p1, 0);
+		if (type == ARPHRD_IP6GRE)
+			p1.proto = IPPROTO_GRE;
+		strcpy(p1.name, name);
+		p1.link = ll_name_to_index(p1.name);
+		if (p1.link == 0)
+			continue;
+		if (tnl_get_ioctl(p1.name, &p1))
+			continue;
+		if (!ip6_tnl_parm_match(p, &p1))
+			continue;
+		print_tunnel(&p1);
+		if (show_stats)
+			tnl_print_stats(ptr);
+		printf("\n");
+	}
+	err = 0;
+ end:
+	fclose(fp);
+	return err;
+}
+
 static int do_show(int argc, char **argv)
 {
-	struct ip6_tnl_parm2 p, p1;
+        struct ip6_tnl_parm2 p;
 
+	ll_init_map(&rth);
 	ip6_tnl_parm_init(&p, 0);
 	p.proto = 0;  /* default to any */
 
-	if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
-		return -1;
+        if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
+                return -1;
 
-	if (!p.name[0] || show_stats) {
-		struct tnl_print_nlmsg_info info = {
-			.p1    = &p,
-			.p2    = &p1,
-			.init  = ip6_tnl_parm_initialize,
-			.match = ip6_tnl_parm_match,
-			.print = print_tunnel,
-		};
-
-		return do_tunnels_list(&info);
+	if (!p.name[0] || show_stats)
+		do_tunnels_list(&p);
+	else {
+		if (tnl_get_ioctl(p.name, &p))
+			return -1;
+		print_tunnel(&p);
+		printf("\n");
 	}
 
-	if (tnl_get_ioctl(p.name, &p))
-		return -1;
-
-	print_tunnel(&p);
-	return 0;
+        return 0;
 }
 
 static int do_add(int cmd, int argc, char **argv)
@@ -387,9 +418,6 @@
 	if (parse_args(argc, argv, cmd, &p) < 0)
 		return -1;
 
-	if (!*p.name)
-		fprintf(stderr, "Tunnel interface name not specified\n");
-
 	if (p.proto == IPPROTO_GRE)
 		basedev = "ip6gre0";
 	else if (p.i_flags & VTI_ISVTI)
diff --git a/ip/ip_common.h b/ip/ip_common.h
index 879287e..9a846df 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -1,39 +1,16 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _IP_COMMON_H_
-#define _IP_COMMON_H_
-
-#include <stdbool.h>
-
-#include "json_print.h"
-
-struct link_filter {
-	int ifindex;
-	int family;
-	int oneline;
-	int showqueue;
-	inet_prefix pfx;
-	int scope, scopemask;
-	int flags, flagmask;
-	int up;
-	char *label;
-	int flushed;
-	char *flushb;
-	int flushp;
-	int flushe;
-	int group;
-	int master;
-	char *kind;
-	char *slave_kind;
-	int target_nsid;
-};
-
 int get_operstate(const char *name);
-int print_linkinfo(struct nlmsghdr *n, void *arg);
-int print_addrinfo(struct nlmsghdr *n, void *arg);
-int print_addrlabel(struct nlmsghdr *n, void *arg);
-int print_neigh(struct nlmsghdr *n, void *arg);
+int print_linkinfo(const struct sockaddr_nl *who,
+		   struct nlmsghdr *n, void *arg);
+int print_linkinfo_brief(const struct sockaddr_nl *who,
+			 struct nlmsghdr *n, void *arg);
+int print_addrinfo(const struct sockaddr_nl *who,
+		   struct nlmsghdr *n, void *arg);
+int print_addrlabel(const struct sockaddr_nl *who,
+		    struct nlmsghdr *n, void *arg);
+int print_neigh(const struct sockaddr_nl *who,
+	        struct nlmsghdr *n, void *arg);
 int ipaddr_list_link(int argc, char **argv);
-void ipaddr_get_vf_rate(int, int *, int *, const char *);
+void ipaddr_get_vf_rate(int, int *, int *, int);
 void iplink_usage(void) __attribute__((noreturn));
 
 void iproute_reset_filter(int ifindex);
@@ -42,19 +19,20 @@
 void ipneigh_reset_filter(int ifindex);
 void ipnetconf_reset_filter(int ifindex);
 
-int print_route(struct nlmsghdr *n, void *arg);
-int print_mroute(struct nlmsghdr *n, void *arg);
-int print_prefix(struct nlmsghdr *n, void *arg);
-int print_rule(struct nlmsghdr *n, void *arg);
-int print_netconf(struct rtnl_ctrl_data *ctrl,
+int print_route(const struct sockaddr_nl *who,
+		struct nlmsghdr *n, void *arg);
+int print_mroute(const struct sockaddr_nl *who,
+		 struct nlmsghdr *n, void *arg);
+int print_prefix(const struct sockaddr_nl *who,
+		 struct nlmsghdr *n, void *arg);
+int print_rule(const struct sockaddr_nl *who,
+	       struct nlmsghdr *n, void *arg);
+int print_netconf(const struct sockaddr_nl *who,
+		  struct rtnl_ctrl_data *ctrl,
 		  struct nlmsghdr *n, void *arg);
-int print_nexthop(struct nlmsghdr *n, void *arg);
 void netns_map_init(void);
-void netns_nsid_socket_init(void);
-int print_nsid(struct nlmsghdr *n, void *arg);
-char *get_name_from_nsid(int nsid);
-int get_netnsid_from_name(const char *name);
-int set_netnsid_from_name(const char *name, int nsid);
+int print_nsid(const struct sockaddr_nl *who,
+	       struct nlmsghdr *n, void *arg);
 int do_ipaddr(int argc, char **argv);
 int do_ipaddrlabel(int argc, char **argv);
 int do_iproute(int argc, char **argv);
@@ -65,7 +43,6 @@
 int do_ip6tunnel(int argc, char **argv);
 int do_iptuntap(int argc, char **argv);
 int do_iplink(int argc, char **argv);
-int do_ipmacsec(int argc, char **argv);
 int do_ipmonitor(int argc, char **argv);
 int do_multiaddr(int argc, char **argv);
 int do_multiroute(int argc, char **argv);
@@ -74,26 +51,14 @@
 int do_xfrm(int argc, char **argv);
 int do_ipl2tp(int argc, char **argv);
 int do_ipfou(int argc, char **argv);
-int do_ipila(int argc, char **argv);
 int do_tcp_metrics(int argc, char **argv);
 int do_ipnetconf(int argc, char **argv);
 int do_iptoken(int argc, char **argv);
-int do_ipvrf(int argc, char **argv);
-void vrf_reset(void);
-int netns_identify_pid(const char *pidstr, char *name, int len);
-int do_seg6(int argc, char **argv);
-int do_ipnh(int argc, char **argv);
-
-int iplink_get(char *name, __u32 filt_mask);
-int iplink_ifla_xstats(int argc, char **argv);
-
-int ip_link_list(req_filter_fn_t filter_fn, struct nlmsg_chain *linfo);
-void free_nlmsg_chain(struct nlmsg_chain *info);
+int iplink_get(unsigned int flags, char *name, __u32 filt_mask);
 
 static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb)
 {
 	__u32 table = r->rtm_table;
-
 	if (tb[RTA_TABLE])
 		table = rta_getattr_u32(tb[RTA_TABLE]);
 	return table;
@@ -101,13 +66,10 @@
 
 extern struct rtnl_handle rth;
 
-struct iplink_req {
-	struct nlmsghdr		n;
-	struct ifinfomsg	i;
-	char			buf[1024];
-};
+#include <stdbool.h>
 
-struct link_util {
+struct link_util
+{
 	struct link_util	*next;
 	const char		*id;
 	int			maxattr;
@@ -116,51 +78,15 @@
 	void			(*print_opt)(struct link_util *, FILE *,
 					     struct rtattr *[]);
 	void			(*print_xstats)(struct link_util *, FILE *,
-						struct rtattr *);
+					     struct rtattr *);
 	void			(*print_help)(struct link_util *, int, char **,
-					      FILE *);
-	int			(*parse_ifla_xstats)(struct link_util *,
-						     int, char **);
-	int			(*print_ifla_xstats)(struct nlmsghdr *, void *);
+					     FILE *);
+	bool			slave;
 };
 
 struct link_util *get_link_kind(const char *kind);
-
-int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type);
-
-/* iplink_bridge.c */
-void br_dump_bridge_id(const struct ifla_bridge_id *id, char *buf, size_t len);
-int bridge_parse_xstats(struct link_util *lu, int argc, char **argv);
-int bridge_print_xstats(struct nlmsghdr *n, void *arg);
-
-int bond_parse_xstats(struct link_util *lu, int argc, char **argv);
-int bond_print_xstats(struct nlmsghdr *n, void *arg);
-
-/* iproute_lwtunnel.c */
-int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp,
-		    int encap_attr, int encap_type_attr);
-void lwt_print_encap(FILE *fp, struct rtattr *encap_type, struct rtattr *encap);
-
-/* iplink_xdp.c */
-int xdp_parse(int *argc, char ***argv, struct iplink_req *req, const char *ifname,
-	      bool generic, bool drv, bool offload);
-void xdp_dump(FILE *fp, struct rtattr *tb, bool link, bool details);
-
-/* iplink_vrf.c */
-__u32 ipvrf_get_table(const char *name);
-int name_is_vrf(const char *name);
+struct link_util *get_link_slave_kind(const char *slave_kind);
 
 #ifndef	INFINITY_LIFE_TIME
 #define     INFINITY_LIFE_TIME      0xFFFFFFFFU
 #endif
-
-#ifndef LABEL_MAX_MASK
-#define     LABEL_MAX_MASK          0xFFFFFU
-#endif
-
-void print_num(FILE *fp, unsigned int width, uint64_t count);
-void print_rt_flags(FILE *fp, unsigned int flags);
-void print_rta_if(FILE *fp, const struct rtattr *rta, const char *prefix);
-void print_rta_gateway(FILE *fp, unsigned char family,
-		       const struct rtattr *rta);
-#endif /* _IP_COMMON_H_ */
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index 0c65f34..9d254d2 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -13,11 +13,12 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <inttypes.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
-#include <sys/param.h>
+#include <sys/ioctl.h>
 #include <errno.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -26,11 +27,9 @@
 
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
-#include <linux/if_infiniband.h>
 #include <linux/sockios.h>
 #include <linux/net_namespace.h>
 
-#include "utils.h"
 #include "rt_names.h"
 #include "utils.h"
 #include "ll_map.h"
@@ -43,57 +42,66 @@
 	IPADD_SAVE,
 };
 
-static struct link_filter filter;
+static struct
+{
+	int ifindex;
+	int family;
+	int oneline;
+	int showqueue;
+	inet_prefix pfx;
+	int scope, scopemask;
+	int flags, flagmask;
+	int up;
+	char *label;
+	int flushed;
+	char *flushb;
+	int flushp;
+	int flushe;
+	int group;
+	int master;
+	char *kind;
+} filter;
+
 static int do_link;
 
 static void usage(void) __attribute__((noreturn));
 
 static void usage(void)
 {
-	if (do_link)
+	if (do_link) {
 		iplink_usage();
-
-	fprintf(stderr,
-		"Usage: ip address {add|change|replace} IFADDR dev IFNAME [ LIFETIME ]\n"
-		"                                                      [ CONFFLAG-LIST ]\n"
-		"       ip address del IFADDR dev IFNAME [mngtmpaddr]\n"
-		"       ip address {save|flush} [ dev IFNAME ] [ scope SCOPE-ID ]\n"
-		"                            [ to PREFIX ] [ FLAG-LIST ] [ label LABEL ] [up]\n"
-		"       ip address [ show [ dev IFNAME ] [ scope SCOPE-ID ] [ master DEVICE ]\n"
-		"                         [ type TYPE ] [ to PREFIX ] [ FLAG-LIST ]\n"
-		"                         [ label LABEL ] [up] [ vrf NAME ] ]\n"
-		"       ip address {showdump|restore}\n"
-		"IFADDR := PREFIX | ADDR peer PREFIX\n"
-		"          [ broadcast ADDR ] [ anycast ADDR ]\n"
-		"          [ label IFNAME ] [ scope SCOPE-ID ] [ metric METRIC ]\n"
-		"SCOPE-ID := [ host | link | global | NUMBER ]\n"
-		"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
-		"FLAG  := [ permanent | dynamic | secondary | primary |\n"
-		"           [-]tentative | [-]deprecated | [-]dadfailed | temporary |\n"
-		"           CONFFLAG-LIST ]\n"
-		"CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG\n"
-		"CONFFLAG  := [ home | nodad | optimistic | mngtmpaddr | noprefixroute | autojoin ]\n"
-		"LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n"
-		"LFT := forever | SECONDS\n"
-		"TYPE := { vlan | veth | vcan | vxcan | dummy | ifb | macvlan | macvtap |\n"
-		"          bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | lowpan |\n"
-		"          gre | gretap | erspan | ip6gre | ip6gretap | ip6erspan | vti |\n"
-		"          nlmon | can | bond_slave | ipvlan | geneve | bridge_slave |\n"
-		"          hsr | macsec | netdevsim }\n");
+	}
+	fprintf(stderr, "Usage: ip address {add|change|replace} IFADDR dev IFNAME [ LIFETIME ]\n");
+	fprintf(stderr, "                                                      [ CONFFLAG-LIST ]\n");
+	fprintf(stderr, "       ip address del IFADDR dev IFNAME [mngtmpaddr]\n");
+	fprintf(stderr, "       ip address {show|save|flush} [ dev IFNAME ] [ scope SCOPE-ID ]\n");
+	fprintf(stderr, "                            [ to PREFIX ] [ FLAG-LIST ] [ label LABEL ] [up]\n");
+	fprintf(stderr, "       ip address {showdump|restore}\n");
+	fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n");
+	fprintf(stderr, "          [ broadcast ADDR ] [ anycast ADDR ]\n");
+	fprintf(stderr, "          [ label IFNAME ] [ scope SCOPE-ID ]\n");
+	fprintf(stderr, "SCOPE-ID := [ host | link | global | NUMBER ]\n");
+	fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
+	fprintf(stderr, "FLAG  := [ permanent | dynamic | secondary | primary |\n");
+	fprintf(stderr, "           [-]tentative | [-]deprecated | [-]dadfailed | temporary |\n");
+	fprintf(stderr, "           CONFFLAG-LIST ]\n");
+	fprintf(stderr, "CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG\n");
+	fprintf(stderr, "CONFFLAG  := [ home | nodad | mngtmpaddr | noprefixroute | autojoin ]\n");
+	fprintf(stderr, "LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n");
+	fprintf(stderr, "LFT := forever | SECONDS\n");
 
 	exit(-1);
 }
 
-static void print_link_flags(FILE *fp, unsigned int flags, unsigned int mdown)
+static void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
 {
-	open_json_array(PRINT_ANY, is_json_context() ? "flags" : "<");
+	fprintf(fp, "<");
 	if (flags & IFF_UP && !(flags & IFF_RUNNING))
-		print_string(PRINT_ANY, NULL,
-			     flags ? "%s," : "%s", "NO-CARRIER");
+		fprintf(fp, "NO-CARRIER%s", flags ? "," : "");
 	flags &= ~IFF_RUNNING;
-#define _PF(f) if (flags&IFF_##f) {					\
-		flags &= ~IFF_##f ;					\
-		print_string(PRINT_ANY, NULL, flags ? "%s," : "%s", #f); }
+#define _PF(f) if (flags&IFF_##f) { \
+		  flags &= ~IFF_##f ; \
+		  fprintf(fp, #f "%s", flags ? "," : ""); }
 	_PF(LOOPBACK);
 	_PF(BROADCAST);
 	_PF(POINTOPOINT);
@@ -114,10 +122,10 @@
 	_PF(ECHO);
 #undef _PF
 	if (flags)
-		print_hex(PRINT_ANY, NULL, "%x", flags);
+		fprintf(fp, "%x", flags);
 	if (mdown)
-		print_string(PRINT_ANY, NULL, ",%s", "M-DOWN");
-	close_json_array(PRINT_ANY, "> ");
+		fprintf(fp, ",M-DOWN");
+	fprintf(fp, "> ");
 }
 
 static const char *oper_states[] = {
@@ -127,26 +135,24 @@
 
 static void print_operstate(FILE *f, __u8 state)
 {
-	if (state >= ARRAY_SIZE(oper_states)) {
-		if (is_json_context())
-			print_uint(PRINT_JSON, "operstate_index", NULL, state);
-		else
-			print_0xhex(PRINT_FP, NULL, "state %#llx", state);
-	} else if (brief) {
-		print_color_string(PRINT_ANY,
-				   oper_state_color(state),
-				   "operstate",
-				   "%-14s ",
-				   oper_states[state]);
-	} else {
-		if (is_json_context())
-			print_string(PRINT_JSON,
-				     "operstate",
-				     NULL, oper_states[state]);
-		else {
+	if (state >= sizeof(oper_states)/sizeof(oper_states[0]))
+		fprintf(f, "state %#x ", state);
+	else {
+		if (brief) {
+			if (strcmp(oper_states[state], "UP") == 0)
+				color_fprintf(f, COLOR_OPERSTATE_UP, "%-14s ", oper_states[state]);
+			else if (strcmp(oper_states[state], "DOWN") == 0)
+				color_fprintf(f, COLOR_OPERSTATE_DOWN, "%-14s ", oper_states[state]);
+			else
+				fprintf(f, "%-14s ", oper_states[state]);
+		} else {
 			fprintf(f, "state ");
-			color_fprintf(f, oper_state_color(state),
-				      "%s ", oper_states[state]);
+			if (strcmp(oper_states[state], "UP") == 0)
+				color_fprintf(f, COLOR_OPERSTATE_UP, "%s ", oper_states[state]);
+			else if (strcmp(oper_states[state], "DOWN") == 0)
+				color_fprintf(f, COLOR_OPERSTATE_DOWN, "%s ", oper_states[state]);
+			else
+				fprintf(f, "%s ", oper_states[state]);
 		}
 	}
 }
@@ -155,7 +161,7 @@
 {
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(oper_states); i++)
+	for (i = 0; i < sizeof(oper_states)/sizeof(oper_states[0]); i++)
 		if (strcasecmp(name, oper_states[i]) == 0)
 			return i;
 	return -1;
@@ -166,19 +172,18 @@
 	int qlen;
 
 	if (tb[IFLA_TXQLEN])
-		qlen = rta_getattr_u32(tb[IFLA_TXQLEN]);
+		qlen = *(int *)RTA_DATA(tb[IFLA_TXQLEN]);
 	else {
-		struct ifreq ifr = {};
+		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(stderr,
-				"ioctl(SIOCGIFTXQLEN) failed: %s\n",
-				strerror(errno));
+			fprintf(f, "ioctl(SIOCGIFTXQLEN) failed: %s\n", strerror(errno));
 			close(s);
 			return;
 		}
@@ -186,7 +191,7 @@
 		qlen = ifr.ifr_qlen;
 	}
 	if (qlen)
-		print_int(PRINT_ANY, "txqlen", "qlen %d", qlen);
+		fprintf(f, "qlen %d", qlen);
 }
 
 static const char *link_modes[] = {
@@ -197,55 +202,39 @@
 {
 	unsigned int mode = rta_getattr_u8(tb);
 
-	if (mode >= ARRAY_SIZE(link_modes))
-		print_int(PRINT_ANY,
-			  "linkmode_index",
-			  "mode %d ",
-			  mode);
+	if (mode >= sizeof(link_modes) / sizeof(link_modes[0]))
+		fprintf(f, "mode %d ", mode);
 	else
-		print_string(PRINT_ANY,
-			     "linkmode",
-			     "mode %s "
-			     , link_modes[mode]);
+		fprintf(f, "mode %s ", link_modes[mode]);
 }
 
-static char *parse_link_kind(struct rtattr *tb, bool slave)
+static char *parse_link_kind(struct rtattr *tb)
 {
 	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
-	int attr = slave ? IFLA_INFO_SLAVE_KIND : IFLA_INFO_KIND;
 
 	parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb);
 
-	if (linkinfo[attr])
-		return RTA_DATA(linkinfo[attr]);
+	if (linkinfo[IFLA_INFO_KIND])
+		return RTA_DATA(linkinfo[IFLA_INFO_KIND]);
 
 	return "";
 }
 
-static int match_link_kind(struct rtattr **tb, const char *kind, bool slave)
-{
-	if (!tb[IFLA_LINKINFO])
-		return -1;
-
-	return strcmp(parse_link_kind(tb[IFLA_LINKINFO], slave), kind);
-}
-
 static void print_linktype(FILE *fp, struct rtattr *tb)
 {
 	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
 	struct link_util *lu;
 	struct link_util *slave_lu;
-	char slave[32];
+	char *kind;
+	char *slave_kind;
 
 	parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb);
-	open_json_object("linkinfo");
 
 	if (linkinfo[IFLA_INFO_KIND]) {
-		const char *kind
-			= rta_getattr_str(linkinfo[IFLA_INFO_KIND]);
+		kind = RTA_DATA(linkinfo[IFLA_INFO_KIND]);
 
-		print_nl();
-		print_string(PRINT_ANY, "info_kind", "    %s ", kind);
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "    %s ", kind);
 
 		lu = get_link_kind(kind);
 		if (lu && lu->print_opt) {
@@ -256,32 +245,21 @@
 						    linkinfo[IFLA_INFO_DATA]);
 				data = attr;
 			}
-			open_json_object("info_data");
 			lu->print_opt(lu, fp, data);
-			close_json_object();
 
 			if (linkinfo[IFLA_INFO_XSTATS] && show_stats &&
-			    lu->print_xstats) {
-				open_json_object("info_xstats");
+			    lu->print_xstats)
 				lu->print_xstats(lu, fp, linkinfo[IFLA_INFO_XSTATS]);
-				close_json_object();
-			}
 		}
 	}
 
 	if (linkinfo[IFLA_INFO_SLAVE_KIND]) {
-		const char *slave_kind
-			= rta_getattr_str(linkinfo[IFLA_INFO_SLAVE_KIND]);
+		slave_kind = RTA_DATA(linkinfo[IFLA_INFO_SLAVE_KIND]);
 
-		print_nl();
-		print_string(PRINT_ANY,
-			     "info_slave_kind",
-			     "    %s_slave ",
-			     slave_kind);
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "    %s_slave ", slave_kind);
 
-		snprintf(slave, sizeof(slave), "%s_slave", slave_kind);
-
-		slave_lu = get_link_kind(slave);
+		slave_lu = get_link_slave_kind(slave_kind);
 		if (slave_lu && slave_lu->print_opt) {
 			struct rtattr *attr[slave_lu->maxattr+1], **data = NULL;
 
@@ -290,12 +268,9 @@
 						    linkinfo[IFLA_INFO_SLAVE_DATA]);
 				data = attr;
 			}
-			open_json_object("info_slave_data");
 			slave_lu->print_opt(slave_lu, fp, data);
-			close_json_object();
 		}
 	}
-	close_json_object();
 }
 
 static void print_af_spec(FILE *fp, struct rtattr *af_spec_attr)
@@ -311,39 +286,21 @@
 
 	if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
 		__u8 mode = rta_getattr_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
-		SPRINT_BUF(b1);
-
 		switch (mode) {
 		case IN6_ADDR_GEN_MODE_EUI64:
-			print_string(PRINT_ANY,
-				     "inet6_addr_gen_mode",
-				     "addrgenmode %s ",
-				     "eui64");
+			fprintf(fp, "addrgenmode eui64 ");
 			break;
 		case IN6_ADDR_GEN_MODE_NONE:
-			print_string(PRINT_ANY,
-				     "inet6_addr_gen_mode",
-				     "addrgenmode %s ",
-				     "none");
+			fprintf(fp, "addrgenmode none ");
 			break;
 		case IN6_ADDR_GEN_MODE_STABLE_PRIVACY:
-			print_string(PRINT_ANY,
-				     "inet6_addr_gen_mode",
-				     "addrgenmode %s ",
-				     "stable_secret");
+			fprintf(fp, "addrgenmode stable_secret ");
 			break;
 		case IN6_ADDR_GEN_MODE_RANDOM:
-			print_string(PRINT_ANY,
-				     "inet6_addr_gen_mode",
-				     "addrgenmode %s ",
-				     "random");
+			fprintf(fp, "addrgenmode random ");
 			break;
 		default:
-			snprintf(b1, sizeof(b1), "%#.2hhx", mode);
-			print_string(PRINT_ANY,
-				     "inet6_addr_gen_mode",
-				     "addrgenmode %s ",
-				     b1);
+			fprintf(fp, "addrgenmode %#.2hhx ", mode);
 			break;
 		}
 	}
@@ -351,13 +308,15 @@
 
 static void print_vf_stats64(FILE *fp, struct rtattr *vfstats);
 
-static void print_vfinfo(FILE *fp, struct ifinfomsg *ifi, struct rtattr *vfinfo)
+static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
 {
 	struct ifla_vf_mac *vf_mac;
-	struct ifla_vf_broadcast *vf_broadcast;
+	struct ifla_vf_vlan *vf_vlan;
 	struct ifla_vf_tx_rate *vf_tx_rate;
+	struct ifla_vf_spoofchk *vf_spoofchk;
+	struct ifla_vf_link_state *vf_linkstate;
 	struct rtattr *vf[IFLA_VF_MAX + 1] = {};
-
+	struct rtattr *tmp;
 	SPRINT_BUF(b1);
 
 	if (vfinfo->rta_type != IFLA_VF_INFO) {
@@ -368,170 +327,72 @@
 	parse_rtattr_nested(vf, IFLA_VF_MAX, vfinfo);
 
 	vf_mac = RTA_DATA(vf[IFLA_VF_MAC]);
-	vf_broadcast = RTA_DATA(vf[IFLA_VF_BROADCAST]);
+	vf_vlan = RTA_DATA(vf[IFLA_VF_VLAN]);
 	vf_tx_rate = RTA_DATA(vf[IFLA_VF_TX_RATE]);
 
-	print_string(PRINT_FP, NULL, "%s    ", _SL_);
-	print_int(PRINT_ANY, "vf", "vf %d ", vf_mac->vf);
+	/* 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);
 
-	print_string(PRINT_ANY,
-		     "link_type",
-		     "    link/%s ",
-		     ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
+	if (tmp->rta_type != IFLA_VF_SPOOFCHK)
+		vf_spoofchk = NULL;
+	else
+		vf_spoofchk = RTA_DATA(vf[IFLA_VF_SPOOFCHK]);
 
-	print_color_string(PRINT_ANY, COLOR_MAC,
-			   "address", "%s",
-			   ll_addr_n2a((unsigned char *) &vf_mac->mac,
-				       ifi->ifi_type == ARPHRD_ETHER ?
-				       ETH_ALEN : INFINIBAND_ALEN,
-				       ifi->ifi_type,
-				       b1, sizeof(b1)));
+	if (vf_spoofchk) {
+		/* Check if the link state vf info type is supported by
+		 * this kernel.
+		 */
+		tmp = (struct rtattr *)((char *)vf[IFLA_VF_SPOOFCHK] +
+				vf[IFLA_VF_SPOOFCHK]->rta_len);
 
-	if (vf[IFLA_VF_BROADCAST]) {
-		if (ifi->ifi_flags&IFF_POINTOPOINT) {
-			print_string(PRINT_FP, NULL, " peer ", NULL);
-			print_bool(PRINT_JSON,
-				   "link_pointtopoint", NULL, true);
-		} else
-			print_string(PRINT_FP, NULL, " brd ", NULL);
+		if (tmp->rta_type != IFLA_VF_LINK_STATE)
+			vf_linkstate = NULL;
+		else
+			vf_linkstate = RTA_DATA(vf[IFLA_VF_LINK_STATE]);
+	} else
+		vf_linkstate = NULL;
 
-		print_color_string(PRINT_ANY, COLOR_MAC,
-				   "broadcast", "%s",
-				   ll_addr_n2a((unsigned char *) &vf_broadcast->broadcast,
-					       ifi->ifi_type == ARPHRD_ETHER ?
-					       ETH_ALEN : INFINIBAND_ALEN,
-					       ifi->ifi_type,
-					       b1, sizeof(b1)));
-	}
-
-	if (vf[IFLA_VF_VLAN_LIST]) {
-		struct rtattr *i, *vfvlanlist = vf[IFLA_VF_VLAN_LIST];
-		int rem = RTA_PAYLOAD(vfvlanlist);
-
-		open_json_array(PRINT_JSON, "vlan_list");
-		for (i = RTA_DATA(vfvlanlist);
-		     RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
-			struct ifla_vf_vlan_info *vf_vlan_info = RTA_DATA(i);
-			SPRINT_BUF(b2);
-
-			open_json_object(NULL);
-			if (vf_vlan_info->vlan)
-				print_int(PRINT_ANY,
-					  "vlan",
-					  ", vlan %d",
-					  vf_vlan_info->vlan);
-			if (vf_vlan_info->qos)
-				print_int(PRINT_ANY,
-					  "qos",
-					  ", qos %d",
-					  vf_vlan_info->qos);
-			if (vf_vlan_info->vlan_proto &&
-			    vf_vlan_info->vlan_proto != htons(ETH_P_8021Q))
-				print_string(PRINT_ANY,
-					     "protocol",
-					     ", vlan protocol %s",
-					     ll_proto_n2a(
-						     vf_vlan_info->vlan_proto,
-						     b2, sizeof(b2)));
-			close_json_object();
-		}
-		close_json_array(PRINT_JSON, NULL);
-	} else {
-		struct ifla_vf_vlan *vf_vlan = RTA_DATA(vf[IFLA_VF_VLAN]);
-
-		if (vf_vlan->vlan)
-			print_int(PRINT_ANY,
-				  "vlan",
-				  ", vlan %d",
-				  vf_vlan->vlan);
-		if (vf_vlan->qos)
-			print_int(PRINT_ANY, "qos", ", qos %d", vf_vlan->qos);
-	}
-
+	fprintf(fp, "%s    vf %d MAC %s", _SL_, vf_mac->vf,
+		ll_addr_n2a((unsigned char *)&vf_mac->mac,
+		ETH_ALEN, 0, b1, sizeof(b1)));
+	if (vf_vlan->vlan)
+		fprintf(fp, ", vlan %d", vf_vlan->vlan);
+	if (vf_vlan->qos)
+		fprintf(fp, ", qos %d", vf_vlan->qos);
 	if (vf_tx_rate->rate)
-		print_uint(PRINT_ANY,
-			   "tx_rate",
-			   ", tx rate %u (Mbps)",
-			   vf_tx_rate->rate);
+		fprintf(fp, ", tx rate %d (Mbps)", vf_tx_rate->rate);
 
 	if (vf[IFLA_VF_RATE]) {
 		struct ifla_vf_rate *vf_rate = RTA_DATA(vf[IFLA_VF_RATE]);
-		int max_tx = vf_rate->max_tx_rate;
-		int min_tx = vf_rate->min_tx_rate;
 
-		if (is_json_context()) {
-			open_json_object("rate");
-			print_uint(PRINT_JSON, "max_tx", NULL, max_tx);
-			print_uint(PRINT_ANY, "min_tx", NULL, min_tx);
-			close_json_object();
-		} else {
-			if (max_tx)
-				fprintf(fp, ", max_tx_rate %uMbps", max_tx);
-			if (min_tx)
-				fprintf(fp, ", min_tx_rate %uMbps", min_tx);
-		}
+		if (vf_rate->max_tx_rate)
+			fprintf(fp, ", max_tx_rate %dMbps", vf_rate->max_tx_rate);
+		if (vf_rate->min_tx_rate)
+			fprintf(fp, ", min_tx_rate %dMbps", vf_rate->min_tx_rate);
 	}
 
-	if (vf[IFLA_VF_SPOOFCHK]) {
-		struct ifla_vf_spoofchk *vf_spoofchk =
-			RTA_DATA(vf[IFLA_VF_SPOOFCHK]);
-
-		if (vf_spoofchk->setting != -1)
-			print_bool(PRINT_ANY,
-				   "spoofchk",
-				   vf_spoofchk->setting ?
-				   ", spoof checking on" : ", spoof checking off",
-				   vf_spoofchk->setting);
-	}
-
-	if (vf[IFLA_VF_LINK_STATE]) {
-		struct ifla_vf_link_state *vf_linkstate =
-			RTA_DATA(vf[IFLA_VF_LINK_STATE]);
-
-		if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_AUTO)
-			print_string(PRINT_ANY,
-				     "link_state",
-				     ", link-state %s",
-				     "auto");
-		else if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_ENABLE)
-			print_string(PRINT_ANY,
-				     "link_state",
-				     ", link-state %s",
-				     "enable");
+	if (vf_spoofchk && vf_spoofchk->setting != -1) {
+		if (vf_spoofchk->setting)
+			fprintf(fp, ", spoof checking on");
 		else
-			print_string(PRINT_ANY,
-				     "link_state",
-				     ", link-state %s",
-				     "disable");
+			fprintf(fp, ", spoof checking off");
 	}
-
-	if (vf[IFLA_VF_TRUST]) {
-		struct ifla_vf_trust *vf_trust = RTA_DATA(vf[IFLA_VF_TRUST]);
-
-		if (vf_trust->setting != -1)
-			print_bool(PRINT_ANY,
-				   "trust",
-				   vf_trust->setting ? ", trust on" : ", trust off",
-				   vf_trust->setting);
+	if (vf_linkstate) {
+		if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_AUTO)
+			fprintf(fp, ", link-state auto");
+		else if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_ENABLE)
+			fprintf(fp, ", link-state enable");
+		else
+			fprintf(fp, ", link-state disable");
 	}
-
-	if (vf[IFLA_VF_RSS_QUERY_EN]) {
-		struct ifla_vf_rss_query_en *rss_query =
-			RTA_DATA(vf[IFLA_VF_RSS_QUERY_EN]);
-
-		if (rss_query->setting != -1)
-			print_bool(PRINT_ANY,
-				   "query_rss_en",
-				   rss_query->setting ? ", query_rss on"
-				   : ", query_rss off",
-				   rss_query->setting);
-	}
-
 	if (vf[IFLA_VF_STATS] && show_stats)
 		print_vf_stats64(fp, vf[IFLA_VF_STATS]);
 }
 
-void print_num(FILE *fp, unsigned int width, uint64_t count)
+static void print_num(FILE *fp, unsigned width, uint64_t count)
 {
 	const char *prefix = "kMGTPE";
 	const unsigned int base = use_iec ? 1024 : 1000;
@@ -546,9 +407,8 @@
 	}
 
 	/* increase value by a factor of 1000/1024 and print
-	 * if result is something a human can read
-	 */
-	for (;;) {
+	 * if result is something a human can read */
+	for(;;) {
 		powi *= base;
 		if (count / base < powi)
 			break;
@@ -566,309 +426,202 @@
 	}
 
 	snprintf(buf, sizeof(buf), "%.*f%c%s", precision,
-		 (double) count / powi, *prefix, use_iec ? "i" : "");
+		(double) count / powi, *prefix, use_iec ? "i" : "");
 
 	fprintf(fp, "%-*s ", width, buf);
 }
 
 static void print_vf_stats64(FILE *fp, struct rtattr *vfstats)
 {
-	struct rtattr *vf[IFLA_VF_STATS_MAX + 1];
+	struct rtattr *vf[IFLA_VF_STATS_MAX + 1] = {};
 
 	if (vfstats->rta_type != IFLA_VF_STATS) {
 		fprintf(stderr, "BUG: rta type is %d\n", vfstats->rta_type);
 		return;
 	}
 
-	parse_rtattr_nested(vf, IFLA_VF_STATS_MAX, vfstats);
+	parse_rtattr_nested(vf, IFLA_VF_MAX, vfstats);
 
-	if (is_json_context()) {
-		open_json_object("stats");
+	/* RX stats */
+	fprintf(fp, "%s", _SL_);
+	fprintf(fp, "    RX: bytes  packets  mcast   bcast %s", _SL_);
+	fprintf(fp, "    ");
 
-		/* RX stats */
-		open_json_object("rx");
-		print_u64(PRINT_JSON, "bytes", NULL,
-			   rta_getattr_u64(vf[IFLA_VF_STATS_RX_BYTES]));
-		print_u64(PRINT_JSON, "packets", NULL,
-			   rta_getattr_u64(vf[IFLA_VF_STATS_RX_PACKETS]));
-		print_u64(PRINT_JSON, "multicast", NULL,
-			   rta_getattr_u64(vf[IFLA_VF_STATS_MULTICAST]));
-		print_u64(PRINT_JSON, "broadcast", NULL,
-			   rta_getattr_u64(vf[IFLA_VF_STATS_BROADCAST]));
-		if (vf[IFLA_VF_STATS_RX_DROPPED])
-			print_u64(PRINT_JSON, "dropped", NULL,
-				  rta_getattr_u64(vf[IFLA_VF_STATS_RX_DROPPED]));
-		close_json_object();
+	print_num(fp, 10, *(__u64 *)RTA_DATA(vf[IFLA_VF_STATS_RX_BYTES]));
+	print_num(fp, 8, *(__u64 *)RTA_DATA(vf[IFLA_VF_STATS_RX_PACKETS]));
+	print_num(fp, 7, *(__u64 *)RTA_DATA(vf[IFLA_VF_STATS_MULTICAST]));
+	print_num(fp, 7, *(__u64 *)RTA_DATA(vf[IFLA_VF_STATS_BROADCAST]));
 
-		/* TX stats */
-		open_json_object("tx");
-		print_u64(PRINT_JSON, "tx_bytes", NULL,
-			   rta_getattr_u64(vf[IFLA_VF_STATS_TX_BYTES]));
-		print_u64(PRINT_JSON, "tx_packets", NULL,
-			   rta_getattr_u64(vf[IFLA_VF_STATS_TX_PACKETS]));
-		if (vf[IFLA_VF_STATS_TX_DROPPED])
-			print_u64(PRINT_JSON, "dropped", NULL,
-				  rta_getattr_u64(vf[IFLA_VF_STATS_TX_DROPPED]));
-		close_json_object();
-		close_json_object();
-	} else {
-		/* RX stats */
+	/* TX stats */
+	fprintf(fp, "%s", _SL_);
+	fprintf(fp, "    TX: bytes  packets %s", _SL_);
+	fprintf(fp, "    ");
+
+	print_num(fp, 10, *(__u64 *)RTA_DATA(vf[IFLA_VF_STATS_TX_BYTES]));
+	print_num(fp, 8, *(__u64 *)RTA_DATA(vf[IFLA_VF_STATS_TX_PACKETS]));
+}
+
+static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s,
+                               const struct rtattr *carrier_changes)
+{
+	/* RX stats */
+	fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
+		s->rx_compressed ? "compressed" : "", _SL_);
+
+	fprintf(fp, "    ");
+	print_num(fp, 10, s->rx_bytes);
+	print_num(fp, 8, s->rx_packets);
+	print_num(fp, 7, s->rx_errors);
+	print_num(fp, 7, s->rx_dropped);
+	print_num(fp, 7, s->rx_over_errors);
+	print_num(fp, 7, s->multicast);
+	if (s->rx_compressed)
+		print_num(fp, 7, s->rx_compressed);
+
+	/* RX error stats */
+	if (show_stats > 1) {
 		fprintf(fp, "%s", _SL_);
-		fprintf(fp, "    RX: bytes  packets  mcast   bcast ");
-		if (vf[IFLA_VF_STATS_RX_DROPPED])
-			fprintf(fp, "  dropped ");
-		fprintf(fp, "%s", _SL_);
-		fprintf(fp, "    ");
+		fprintf(fp, "    RX errors: length   crc     frame   fifo    missed%s", _SL_);
 
-		print_num(fp, 10, rta_getattr_u64(vf[IFLA_VF_STATS_RX_BYTES]));
-		print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_RX_PACKETS]));
-		print_num(fp, 7, rta_getattr_u64(vf[IFLA_VF_STATS_MULTICAST]));
-		print_num(fp, 7, rta_getattr_u64(vf[IFLA_VF_STATS_BROADCAST]));
-		if (vf[IFLA_VF_STATS_RX_DROPPED])
-			print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_RX_DROPPED]));
+		fprintf(fp, "               ");
+		print_num(fp, 8, s->rx_length_errors);
+		print_num(fp, 7, s->rx_crc_errors);
+		print_num(fp, 7, s->rx_frame_errors);
+		print_num(fp, 7, s->rx_fifo_errors);
+		print_num(fp, 7, s->rx_missed_errors);
+	}
+	fprintf(fp, "%s", _SL_);
 
-		/* TX stats */
-		fprintf(fp, "%s", _SL_);
-		fprintf(fp, "    TX: bytes  packets ");
-		if (vf[IFLA_VF_STATS_TX_DROPPED])
-			fprintf(fp, "  dropped ");
-		fprintf(fp, "%s", _SL_);
-		fprintf(fp, "    ");
+	/* TX stats */
+	fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
+		s->tx_compressed ? "compressed" : "", _SL_);
 
-		print_num(fp, 10, rta_getattr_u64(vf[IFLA_VF_STATS_TX_BYTES]));
-		print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_TX_PACKETS]));
-		if (vf[IFLA_VF_STATS_TX_DROPPED])
-			print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_TX_DROPPED]));
+
+	fprintf(fp, "    ");
+	print_num(fp, 10, s->tx_bytes);
+	print_num(fp, 8, s->tx_packets);
+	print_num(fp, 7, s->tx_errors);
+	print_num(fp, 7, s->tx_dropped);
+	print_num(fp, 7, s->tx_carrier_errors);
+	print_num(fp, 7, s->collisions);
+	if (s->tx_compressed)
+		print_num(fp, 7, s->tx_compressed);
+
+	/* TX error stats */
+	if (show_stats > 1) {
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "    TX errors: aborted  fifo   window heartbeat");
+                if (carrier_changes)
+			fprintf(fp, " transns");
+		fprintf(fp, "%s", _SL_);
+
+		fprintf(fp, "               ");
+		print_num(fp, 8, s->tx_aborted_errors);
+		print_num(fp, 7, s->tx_fifo_errors);
+		print_num(fp, 7, s->tx_window_errors);
+		print_num(fp, 7, s->tx_heartbeat_errors);
+		if (carrier_changes)
+			print_num(fp, 7, *(uint32_t*)RTA_DATA(carrier_changes));
 	}
 }
 
-static void __print_link_stats(FILE *fp, struct rtattr *tb[])
+static void print_link_stats32(FILE *fp, const struct rtnl_link_stats *s,
+			       const struct rtattr *carrier_changes)
 {
-	const struct rtattr *carrier_changes = tb[IFLA_CARRIER_CHANGES];
-	struct rtnl_link_stats64 _s, *s = &_s;
-	int ret;
+	/* RX stats */
+	fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
+		s->rx_compressed ? "compressed" : "", _SL_);
 
-	ret = get_rtnl_link_stats_rta(s, tb);
-	if (ret < 0)
-		return;
 
-	if (is_json_context()) {
-		open_json_object((ret == sizeof(*s)) ? "stats64" : "stats");
+	fprintf(fp, "    ");
+	print_num(fp, 10, s->rx_bytes);
+	print_num(fp, 8, s->rx_packets);
+	print_num(fp, 7, s->rx_errors);
+	print_num(fp, 7, s->rx_dropped);
+	print_num(fp, 7, s->rx_over_errors);
+	print_num(fp, 7, s->multicast);
+	if (s->rx_compressed)
+		print_num(fp, 7, s->rx_compressed);
 
-		/* RX stats */
-		open_json_object("rx");
-		print_u64(PRINT_JSON, "bytes", NULL, s->rx_bytes);
-		print_u64(PRINT_JSON, "packets", NULL, s->rx_packets);
-		print_u64(PRINT_JSON, "errors", NULL, s->rx_errors);
-		print_u64(PRINT_JSON, "dropped", NULL, s->rx_dropped);
-		print_u64(PRINT_JSON, "over_errors", NULL, s->rx_over_errors);
-		print_u64(PRINT_JSON, "multicast", NULL, s->multicast);
-		if (s->rx_compressed)
-			print_u64(PRINT_JSON,
-				   "compressed", NULL, s->rx_compressed);
+	/* RX error stats */
+	if (show_stats > 1) {
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "    RX errors: length   crc     frame   fifo    missed%s", _SL_);
+		fprintf(fp, "               ");
+		print_num(fp, 8, s->rx_length_errors);
+		print_num(fp, 7, s->rx_crc_errors);
+		print_num(fp, 7, s->rx_frame_errors);
+		print_num(fp, 7, s->rx_fifo_errors);
+		print_num(fp, 7, s->rx_missed_errors);
+	}
+	fprintf(fp, "%s", _SL_);
 
-		/* RX error stats */
-		if (show_stats > 1) {
-			print_u64(PRINT_JSON,
-				   "length_errors",
-				   NULL, s->rx_length_errors);
-			print_u64(PRINT_JSON,
-				   "crc_errors",
-				   NULL, s->rx_crc_errors);
-			print_u64(PRINT_JSON,
-				   "frame_errors",
-				   NULL, s->rx_frame_errors);
-			print_u64(PRINT_JSON,
-				   "fifo_errors",
-				   NULL, s->rx_fifo_errors);
-			print_u64(PRINT_JSON,
-				   "missed_errors",
-				   NULL, s->rx_missed_errors);
-			if (s->rx_nohandler)
-				print_u64(PRINT_JSON,
-					   "nohandler", NULL, s->rx_nohandler);
-		}
-		close_json_object();
+	/* TX stats */
+	fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
+		s->tx_compressed ? "compressed" : "", _SL_);
 
-		/* TX stats */
-		open_json_object("tx");
-		print_u64(PRINT_JSON, "bytes", NULL, s->tx_bytes);
-		print_u64(PRINT_JSON, "packets", NULL, s->tx_packets);
-		print_u64(PRINT_JSON, "errors", NULL, s->tx_errors);
-		print_u64(PRINT_JSON, "dropped", NULL, s->tx_dropped);
-		print_u64(PRINT_JSON,
-			   "carrier_errors",
-			   NULL, s->tx_carrier_errors);
-		print_u64(PRINT_JSON, "collisions", NULL, s->collisions);
-		if (s->tx_compressed)
-			print_u64(PRINT_JSON,
-				   "compressed", NULL, s->tx_compressed);
+	fprintf(fp, "    ");
+	print_num(fp, 10, s->tx_bytes);
+	print_num(fp, 8, s->tx_packets);
+	print_num(fp, 7, s->tx_errors);
+	print_num(fp, 7, s->tx_dropped);
+	print_num(fp, 7, s->tx_carrier_errors);
+	print_num(fp, 7, s->collisions);
+	if (s->tx_compressed)
+		print_num(fp, 7, s->tx_compressed);
 
-		/* TX error stats */
-		if (show_stats > 1) {
-			print_u64(PRINT_JSON,
-				   "aborted_errors",
-				   NULL, s->tx_aborted_errors);
-			print_u64(PRINT_JSON,
-				   "fifo_errors",
-				   NULL, s->tx_fifo_errors);
-			print_u64(PRINT_JSON,
-				   "window_errors",
-				   NULL, s->tx_window_errors);
-			print_u64(PRINT_JSON,
-				   "heartbeat_errors",
-				   NULL, s->tx_heartbeat_errors);
-			if (carrier_changes)
-				print_u64(PRINT_JSON, "carrier_changes", NULL,
-					   rta_getattr_u32(carrier_changes));
-		}
-
-		close_json_object();
-		close_json_object();
-	} else {
-		/* RX stats */
-		fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
-			s->rx_compressed ? "compressed" : "", _SL_);
-
-		fprintf(fp, "    ");
-		print_num(fp, 10, s->rx_bytes);
-		print_num(fp, 8, s->rx_packets);
-		print_num(fp, 7, s->rx_errors);
-		print_num(fp, 7, s->rx_dropped);
-		print_num(fp, 7, s->rx_over_errors);
-		print_num(fp, 7, s->multicast);
-		if (s->rx_compressed)
-			print_num(fp, 7, s->rx_compressed);
-
-		/* RX error stats */
-		if (show_stats > 1) {
-			fprintf(fp, "%s", _SL_);
-			fprintf(fp, "    RX errors: length   crc     frame   fifo    missed%s%s",
-				s->rx_nohandler ? "   nohandler" : "", _SL_);
-			fprintf(fp, "               ");
-			print_num(fp, 8, s->rx_length_errors);
-			print_num(fp, 7, s->rx_crc_errors);
-			print_num(fp, 7, s->rx_frame_errors);
-			print_num(fp, 7, s->rx_fifo_errors);
-			print_num(fp, 7, s->rx_missed_errors);
-			if (s->rx_nohandler)
-				print_num(fp, 7, s->rx_nohandler);
-		}
+	/* TX error stats */
+	if (show_stats > 1) {
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "    TX errors: aborted  fifo   window heartbeat");
+                if (carrier_changes)
+			fprintf(fp, " transns");
 		fprintf(fp, "%s", _SL_);
 
-		/* TX stats */
-		fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
-			s->tx_compressed ? "compressed" : "", _SL_);
-
-		fprintf(fp, "    ");
-		print_num(fp, 10, s->tx_bytes);
-		print_num(fp, 8, s->tx_packets);
-		print_num(fp, 7, s->tx_errors);
-		print_num(fp, 7, s->tx_dropped);
-		print_num(fp, 7, s->tx_carrier_errors);
-		print_num(fp, 7, s->collisions);
-		if (s->tx_compressed)
-			print_num(fp, 7, s->tx_compressed);
-
-		/* TX error stats */
-		if (show_stats > 1) {
-			fprintf(fp, "%s", _SL_);
-			fprintf(fp, "    TX errors: aborted  fifo   window heartbeat");
-			if (carrier_changes)
-				fprintf(fp, " transns");
-			fprintf(fp, "%s", _SL_);
-
-			fprintf(fp, "               ");
-			print_num(fp, 8, s->tx_aborted_errors);
-			print_num(fp, 7, s->tx_fifo_errors);
-			print_num(fp, 7, s->tx_window_errors);
-			print_num(fp, 7, s->tx_heartbeat_errors);
-			if (carrier_changes)
-				print_num(fp, 7,
-					  rta_getattr_u32(carrier_changes));
-		}
+		fprintf(fp, "               ");
+		print_num(fp, 8, s->tx_aborted_errors);
+		print_num(fp, 7, s->tx_fifo_errors);
+		print_num(fp, 7, s->tx_window_errors);
+		print_num(fp, 7, s->tx_heartbeat_errors);
+		if (carrier_changes)
+			print_num(fp, 7, *(uint32_t*)RTA_DATA(carrier_changes));
 	}
 }
 
+static void __print_link_stats(FILE *fp, struct rtattr **tb)
+{
+	if (tb[IFLA_STATS64])
+		print_link_stats64(fp, RTA_DATA(tb[IFLA_STATS64]),
+					tb[IFLA_CARRIER_CHANGES]);
+	else if (tb[IFLA_STATS])
+		print_link_stats32(fp, RTA_DATA(tb[IFLA_STATS]),
+					tb[IFLA_CARRIER_CHANGES]);
+}
+
 static void print_link_stats(FILE *fp, struct nlmsghdr *n)
 {
 	struct ifinfomsg *ifi = NLMSG_DATA(n);
-	struct rtattr *tb[IFLA_MAX+1];
+	struct rtattr * tb[IFLA_MAX+1];
 
 	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi),
 		     n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)));
 	__print_link_stats(fp, tb);
-	print_nl();
+	fprintf(fp, "%s", _SL_);
 }
 
-static int print_linkinfo_brief(FILE *fp, const char *name,
-				const struct ifinfomsg *ifi,
-				struct rtattr *tb[])
+int print_linkinfo_brief(const struct sockaddr_nl *who,
+				struct nlmsghdr *n, void *arg)
 {
-	unsigned int m_flag = 0;
-
-	m_flag = print_name_and_link("%-16s ", name, tb);
-
-	if (tb[IFLA_OPERSTATE])
-		print_operstate(fp, rta_getattr_u8(tb[IFLA_OPERSTATE]));
-
-	if (filter.family == AF_PACKET) {
-		SPRINT_BUF(b1);
-
-		if (tb[IFLA_ADDRESS]) {
-			print_color_string(PRINT_ANY, COLOR_MAC,
-					   "address", "%s ",
-					   ll_addr_n2a(
-						   RTA_DATA(tb[IFLA_ADDRESS]),
-						   RTA_PAYLOAD(tb[IFLA_ADDRESS]),
-						   ifi->ifi_type,
-						   b1, sizeof(b1)));
-		}
-	}
-
-	if (filter.family == AF_PACKET) {
-		print_link_flags(fp, ifi->ifi_flags, m_flag);
-		print_string(PRINT_FP, NULL, "%s", "\n");
-	}
-
-	fflush(fp);
-	return 0;
-}
-
-static const char *link_events[] = {
-	[IFLA_EVENT_NONE] = "NONE",
-	[IFLA_EVENT_REBOOT] = "REBOOT",
-	[IFLA_EVENT_FEATURES] = "FEATURE CHANGE",
-	[IFLA_EVENT_BONDING_FAILOVER] = "BONDING FAILOVER",
-	[IFLA_EVENT_NOTIFY_PEERS] = "NOTIFY PEERS",
-	[IFLA_EVENT_IGMP_RESEND] = "RESEND IGMP",
-	[IFLA_EVENT_BONDING_OPTIONS] = "BONDING OPTION"
-};
-
-static void print_link_event(FILE *f, __u32 event)
-{
-	if (event >= ARRAY_SIZE(link_events))
-		print_int(PRINT_ANY, "event", "event %d ", event);
-	else {
-		if (event)
-			print_string(PRINT_ANY,
-				     "event", "event %s ",
-				     link_events[event]);
-	}
-}
-
-int print_linkinfo(struct nlmsghdr *n, void *arg)
-{
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 	struct ifinfomsg *ifi = NLMSG_DATA(n);
-	struct rtattr *tb[IFLA_MAX+1];
+	struct rtattr * tb[IFLA_MAX+1];
 	int len = n->nlmsg_len;
-	const char *name;
-	unsigned int m_flag = 0;
-	SPRINT_BUF(b1);
+	char *name;
+	char buf[32] = { 0, };
+	unsigned m_flag = 0;
 
 	if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
-		return 0;
+		return -1;
 
 	len -= NLMSG_LENGTH(sizeof(*ifi));
 	if (len < 0)
@@ -880,63 +633,187 @@
 		return -1;
 
 	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
-
-	name = get_ifname_rta(ifi->ifi_index, tb[IFLA_IFNAME]);
-	if (!name)
+	if (tb[IFLA_IFNAME] == NULL) {
+		fprintf(stderr, "BUG: device with ifindex %d has nil ifname\n", ifi->ifi_index);
+	}
+	if (filter.label &&
+	    (!filter.family || filter.family == AF_PACKET) &&
+	    fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0))
 		return -1;
 
-	if (filter.label)
-		return 0;
-
 	if (tb[IFLA_GROUP]) {
-		int group = rta_getattr_u32(tb[IFLA_GROUP]);
-
+		int group = *(int*)RTA_DATA(tb[IFLA_GROUP]);
 		if (filter.group != -1 && group != filter.group)
 			return -1;
 	}
 
 	if (tb[IFLA_MASTER]) {
-		int master = rta_getattr_u32(tb[IFLA_MASTER]);
-
+		int master = *(int*)RTA_DATA(tb[IFLA_MASTER]);
 		if (filter.master > 0 && master != filter.master)
 			return -1;
-	} else if (filter.master > 0)
+	}
+	else if (filter.master > 0)
 		return -1;
 
-	if (filter.kind && match_link_kind(tb, filter.kind, 0))
-		return -1;
+	if (filter.kind) {
+		if (tb[IFLA_LINKINFO]) {
+			char *kind = parse_link_kind(tb[IFLA_LINKINFO]);
 
-	if (filter.slave_kind && match_link_kind(tb, filter.slave_kind, 1))
-		return -1;
+			if (strcmp(kind, filter.kind))
+				return -1;
+		} else {
+			return -1;
+		}
+	}
 
 	if (n->nlmsg_type == RTM_DELLINK)
-		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
+		fprintf(fp, "Deleted ");
 
-	if (brief)
-		return print_linkinfo_brief(fp, name, ifi, tb);
+	name = (char *)(tb[IFLA_IFNAME] ? rta_getattr_str(tb[IFLA_IFNAME]) : "<nil>");
 
-	print_int(PRINT_ANY, "ifindex", "%d: ", ifi->ifi_index);
+	if (tb[IFLA_LINK]) {
+		SPRINT_BUF(b1);
+		int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
+		if (iflink == 0)
+			snprintf(buf, sizeof(buf), "%s@NONE", name);
+		else {
+			snprintf(buf, sizeof(buf),
+				 "%s@%s", name, ll_idx_n2a(iflink, b1));
+			m_flag = ll_index_to_flags(iflink);
+			m_flag = !(m_flag & IFF_UP);
+		}
+	} else
+		snprintf(buf, sizeof(buf), "%s", name);
 
-	m_flag = print_name_and_link("%s: ", name, tb);
+	fprintf(fp, "%-16s ", buf);
+
+	if (tb[IFLA_OPERSTATE])
+		print_operstate(fp, rta_getattr_u8(tb[IFLA_OPERSTATE]));
+
+	if (filter.family == AF_PACKET) {
+		SPRINT_BUF(b1);
+		if (tb[IFLA_ADDRESS]) {
+			color_fprintf(fp, COLOR_MAC, "%s ",
+					ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
+						RTA_PAYLOAD(tb[IFLA_ADDRESS]),
+						ifi->ifi_type,
+						b1, sizeof(b1)));
+		}
+	}
+
+	if (filter.family == AF_PACKET)
+		print_link_flags(fp, ifi->ifi_flags, m_flag);
+
+	if (filter.family == AF_PACKET)
+		fprintf(fp, "\n");
+	fflush(fp);
+	return 0;
+}
+
+int print_linkinfo(const struct sockaddr_nl *who,
+		   struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct ifinfomsg *ifi = NLMSG_DATA(n);
+	struct rtattr * tb[IFLA_MAX+1];
+	int len = n->nlmsg_len;
+	unsigned m_flag = 0;
+
+	if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
+		return 0;
+
+	len -= NLMSG_LENGTH(sizeof(*ifi));
+	if (len < 0)
+		return -1;
+
+	if (filter.ifindex && ifi->ifi_index != filter.ifindex)
+		return 0;
+	if (filter.up && !(ifi->ifi_flags&IFF_UP))
+		return 0;
+
+	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+	if (tb[IFLA_IFNAME] == NULL) {
+		fprintf(stderr, "BUG: device with ifindex %d has nil ifname\n", ifi->ifi_index);
+	}
+	if (filter.label &&
+	    (!filter.family || filter.family == AF_PACKET) &&
+	    fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0))
+		return 0;
+
+	if (tb[IFLA_GROUP]) {
+		int group = *(int*)RTA_DATA(tb[IFLA_GROUP]);
+		if (filter.group != -1 && group != filter.group)
+			return -1;
+	}
+
+	if (tb[IFLA_MASTER]) {
+		int master = *(int*)RTA_DATA(tb[IFLA_MASTER]);
+		if (filter.master > 0 && master != filter.master)
+			return -1;
+	}
+	else if (filter.master > 0)
+		return -1;
+
+	if (filter.kind) {
+		if (tb[IFLA_LINKINFO]) {
+			char *kind = parse_link_kind(tb[IFLA_LINKINFO]);
+
+			if (strcmp(kind, filter.kind))
+				return -1;
+		} else {
+			return -1;
+		}
+	}
+
+	if (n->nlmsg_type == RTM_DELLINK)
+		fprintf(fp, "Deleted ");
+
+	fprintf(fp, "%d: ", ifi->ifi_index);
+	color_fprintf(fp, COLOR_IFNAME, "%s",
+		tb[IFLA_IFNAME] ? rta_getattr_str(tb[IFLA_IFNAME]) : "<nil>");
+
+	if (tb[IFLA_LINK]) {
+		SPRINT_BUF(b1);
+		int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
+		if (iflink == 0)
+			fprintf(fp, "@NONE: ");
+		else {
+			if (tb[IFLA_LINK_NETNSID])
+				fprintf(fp, "@if%d: ", iflink);
+			else {
+				fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1));
+				m_flag = ll_index_to_flags(iflink);
+				m_flag = !(m_flag & IFF_UP);
+			}
+		}
+	} else {
+		fprintf(fp, ": ");
+	}
 	print_link_flags(fp, ifi->ifi_flags, m_flag);
 
 	if (tb[IFLA_MTU])
-		print_int(PRINT_ANY,
-			  "mtu", "mtu %u ",
-			  rta_getattr_u32(tb[IFLA_MTU]));
-	if (tb[IFLA_XDP])
-		xdp_dump(fp, tb[IFLA_XDP], do_link, false);
+		fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
 	if (tb[IFLA_QDISC])
-		print_string(PRINT_ANY,
-			     "qdisc",
-			     "qdisc %s ",
-			     rta_getattr_str(tb[IFLA_QDISC]));
+		fprintf(fp, "qdisc %s ", rta_getattr_str(tb[IFLA_QDISC]));
 	if (tb[IFLA_MASTER]) {
-		int master = rta_getattr_u32(tb[IFLA_MASTER]);
+		SPRINT_BUF(b1);
+		fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
+	}
 
-		print_string(PRINT_ANY,
-			     "master", "master %s ",
-			     ll_index_to_name(master));
+	if (tb[IFLA_PHYS_PORT_ID]) {
+		SPRINT_BUF(b1);
+		fprintf(fp, "portid %s ",
+			hexstring_n2a(RTA_DATA(tb[IFLA_PHYS_PORT_ID]),
+				      RTA_PAYLOAD(tb[IFLA_PHYS_PORT_ID]),
+				      b1, sizeof(b1)));
+	}
+
+	if (tb[IFLA_PHYS_SWITCH_ID]) {
+		SPRINT_BUF(b1);
+		fprintf(fp, "switchid %s ",
+			hexstring_n2a(RTA_DATA(tb[IFLA_PHYS_SWITCH_ID]),
+				      RTA_PAYLOAD(tb[IFLA_PHYS_SWITCH_ID]),
+				      b1, sizeof(b1)));
 	}
 
 	if (tb[IFLA_OPERSTATE])
@@ -946,200 +823,80 @@
 		print_linkmode(fp, tb[IFLA_LINKMODE]);
 
 	if (tb[IFLA_GROUP]) {
-		int group = rta_getattr_u32(tb[IFLA_GROUP]);
-
-		print_string(PRINT_ANY,
-			     "group",
-			     "group %s ",
-			     rtnl_group_n2a(group, b1, sizeof(b1)));
+		SPRINT_BUF(b1);
+		int group = *(int*)RTA_DATA(tb[IFLA_GROUP]);
+		fprintf(fp, "group %s ", rtnl_group_n2a(group, b1, sizeof(b1)));
 	}
 
 	if (filter.showqueue)
 		print_queuelen(fp, tb);
 
-	if (tb[IFLA_EVENT])
-		print_link_event(fp, rta_getattr_u32(tb[IFLA_EVENT]));
-
 	if (!filter.family || filter.family == AF_PACKET || show_details) {
-		print_nl();
-		print_string(PRINT_ANY,
-			     "link_type",
-			     "    link/%s ",
-			     ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
+		SPRINT_BUF(b1);
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "    link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
+
 		if (tb[IFLA_ADDRESS]) {
-			print_color_string(PRINT_ANY,
-					   COLOR_MAC,
-					   "address",
-					   "%s",
-					   ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
-						       RTA_PAYLOAD(tb[IFLA_ADDRESS]),
-						       ifi->ifi_type,
-						       b1, sizeof(b1)));
+			color_fprintf(fp, COLOR_MAC, "%s",
+					ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
+						RTA_PAYLOAD(tb[IFLA_ADDRESS]),
+						ifi->ifi_type,
+						b1, sizeof(b1)));
 		}
 		if (tb[IFLA_BROADCAST]) {
-			if (ifi->ifi_flags&IFF_POINTOPOINT) {
-				print_string(PRINT_FP, NULL, " peer ", NULL);
-				print_bool(PRINT_JSON,
-					   "link_pointtopoint", NULL, true);
-			} else {
-				print_string(PRINT_FP, NULL, " brd ", NULL);
-			}
-			print_color_string(PRINT_ANY,
-					   COLOR_MAC,
-					   "broadcast",
-					   "%s",
-					   ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
-						       RTA_PAYLOAD(tb[IFLA_BROADCAST]),
-						       ifi->ifi_type,
-						       b1, sizeof(b1)));
+			if (ifi->ifi_flags&IFF_POINTOPOINT)
+				fprintf(fp, " peer ");
+			else
+				fprintf(fp, " brd ");
+			fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
+						      RTA_PAYLOAD(tb[IFLA_BROADCAST]),
+						      ifi->ifi_type,
+						      b1, sizeof(b1)));
 		}
 	}
 
 	if (tb[IFLA_LINK_NETNSID]) {
-		int id = rta_getattr_u32(tb[IFLA_LINK_NETNSID]);
+		int id = *(int*)RTA_DATA(tb[IFLA_LINK_NETNSID]);
 
-		if (is_json_context()) {
-			print_int(PRINT_JSON, "link_netnsid", NULL, id);
-		} else {
-			if (id >= 0) {
-				char *name = get_name_from_nsid(id);
-
-				if (name)
-					print_string(PRINT_FP, NULL,
-						     " link-netns %s", name);
-				else
-					print_int(PRINT_FP, NULL,
-						  " link-netnsid %d", id);
-			} else
-				print_string(PRINT_FP, NULL,
-					     " link-netnsid %s", "unknown");
-		}
-	}
-
-	if (tb[IFLA_NEW_NETNSID]) {
-		int id = rta_getattr_u32(tb[IFLA_NEW_NETNSID]);
-		char *name = get_name_from_nsid(id);
-
-		if (name)
-			print_string(PRINT_FP, NULL, " new-netns %s", name);
+		if (id >= 0)
+			fprintf(fp, " link-netnsid %d", id);
 		else
-			print_int(PRINT_FP, NULL, " new-netnsid %d", id);
-	}
-	if (tb[IFLA_NEW_IFINDEX]) {
-		int id = rta_getattr_u32(tb[IFLA_NEW_IFINDEX]);
-
-		print_int(PRINT_FP, NULL, " new-ifindex %d", id);
+			fprintf(fp, " link-netnsid unknown");
 	}
 
 	if (tb[IFLA_PROTO_DOWN]) {
 		if (rta_getattr_u8(tb[IFLA_PROTO_DOWN]))
-			print_bool(PRINT_ANY,
-				   "proto_down", " protodown on ", true);
+			fprintf(fp, " protodown on ");
 	}
 
-	if (show_details) {
-		if (tb[IFLA_PROMISCUITY])
-			print_uint(PRINT_ANY,
-				   "promiscuity",
-				   " promiscuity %u ",
-				   rta_getattr_u32(tb[IFLA_PROMISCUITY]));
+	if (tb[IFLA_PROMISCUITY] && show_details)
+		fprintf(fp, " promiscuity %u ",
+			*(int*)RTA_DATA(tb[IFLA_PROMISCUITY]));
 
-		if (tb[IFLA_MIN_MTU])
-			print_uint(PRINT_ANY,
-				   "min_mtu", "minmtu %u ",
-				   rta_getattr_u32(tb[IFLA_MIN_MTU]));
+	if (tb[IFLA_LINKINFO] && show_details)
+		print_linktype(fp, tb[IFLA_LINKINFO]);
 
-		if (tb[IFLA_MAX_MTU])
-			print_uint(PRINT_ANY,
-				   "max_mtu", "maxmtu %u ",
-				   rta_getattr_u32(tb[IFLA_MAX_MTU]));
-
-		if (tb[IFLA_LINKINFO])
-			print_linktype(fp, tb[IFLA_LINKINFO]);
-
-		if (do_link && tb[IFLA_AF_SPEC])
-			print_af_spec(fp, tb[IFLA_AF_SPEC]);
-
-		if (tb[IFLA_NUM_TX_QUEUES])
-			print_uint(PRINT_ANY,
-				   "num_tx_queues",
-				   "numtxqueues %u ",
-				   rta_getattr_u32(tb[IFLA_NUM_TX_QUEUES]));
-
-		if (tb[IFLA_NUM_RX_QUEUES])
-			print_uint(PRINT_ANY,
-				   "num_rx_queues",
-				   "numrxqueues %u ",
-				   rta_getattr_u32(tb[IFLA_NUM_RX_QUEUES]));
-
-		if (tb[IFLA_GSO_MAX_SIZE])
-			print_uint(PRINT_ANY,
-				   "gso_max_size",
-				   "gso_max_size %u ",
-				   rta_getattr_u32(tb[IFLA_GSO_MAX_SIZE]));
-
-		if (tb[IFLA_GSO_MAX_SEGS])
-			print_uint(PRINT_ANY,
-				   "gso_max_segs",
-				   "gso_max_segs %u ",
-				   rta_getattr_u32(tb[IFLA_GSO_MAX_SEGS]));
-
-		if (tb[IFLA_PHYS_PORT_NAME])
-			print_string(PRINT_ANY,
-				     "phys_port_name",
-				     "portname %s ",
-				     rta_getattr_str(tb[IFLA_PHYS_PORT_NAME]));
-
-		if (tb[IFLA_PHYS_PORT_ID]) {
-			print_string(PRINT_ANY,
-				     "phys_port_id",
-				     "portid %s ",
-				     hexstring_n2a(
-					     RTA_DATA(tb[IFLA_PHYS_PORT_ID]),
-					     RTA_PAYLOAD(tb[IFLA_PHYS_PORT_ID]),
-					     b1, sizeof(b1)));
-		}
-
-		if (tb[IFLA_PHYS_SWITCH_ID]) {
-			print_string(PRINT_ANY,
-				     "phys_switch_id",
-				     "switchid %s ",
-				     hexstring_n2a(RTA_DATA(tb[IFLA_PHYS_SWITCH_ID]),
-						   RTA_PAYLOAD(tb[IFLA_PHYS_SWITCH_ID]),
-						   b1, sizeof(b1)));
-		}
-	}
+	if (do_link && tb[IFLA_AF_SPEC] && show_details)
+		print_af_spec(fp, tb[IFLA_AF_SPEC]);
 
 	if ((do_link || show_details) && tb[IFLA_IFALIAS]) {
-		print_string(PRINT_FP, NULL, "%s    ", _SL_);
-		print_string(PRINT_ANY,
-			     "ifalias",
-			     "alias %s",
-			     rta_getattr_str(tb[IFLA_IFALIAS]));
+		fprintf(fp, "%s    alias %s", _SL_,
+			rta_getattr_str(tb[IFLA_IFALIAS]));
 	}
 
-	if ((do_link || show_details) && tb[IFLA_XDP])
-		xdp_dump(fp, tb[IFLA_XDP], true, true);
-
 	if (do_link && show_stats) {
-		print_nl();
+		fprintf(fp, "%s", _SL_);
 		__print_link_stats(fp, tb);
 	}
 
 	if ((do_link || show_details) && tb[IFLA_VFINFO_LIST] && tb[IFLA_NUM_VF]) {
 		struct rtattr *i, *vflist = tb[IFLA_VFINFO_LIST];
 		int rem = RTA_PAYLOAD(vflist);
-
-		open_json_array(PRINT_JSON, "vfinfo_list");
-		for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
-			open_json_object(NULL);
-			print_vfinfo(fp, ifi, i);
-			close_json_object();
-		}
-		close_json_array(PRINT_JSON, NULL);
+		for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
+			print_vfinfo(fp, i);
 	}
 
-	print_string(PRINT_FP, NULL, "%s", "\n");
+	fprintf(fp, "\n");
 	fflush(fp);
 	return 1;
 }
@@ -1178,126 +935,20 @@
 				  struct rtattr *ifa_flags_attr)
 {
 	return ifa_flags_attr ? rta_getattr_u32(ifa_flags_attr) :
-		ifa->ifa_flags;
+				ifa->ifa_flags;
 }
 
-/* Mapping from argument to address flag mask */
-static const struct {
-	const char *name;
-	unsigned long value;
-} ifa_flag_names[] = {
-	{ "secondary",		IFA_F_SECONDARY },
-	{ "temporary",		IFA_F_SECONDARY },
-	{ "nodad",		IFA_F_NODAD },
-	{ "optimistic",		IFA_F_OPTIMISTIC },
-	{ "dadfailed",		IFA_F_DADFAILED },
-	{ "home",		IFA_F_HOMEADDRESS },
-	{ "deprecated",		IFA_F_DEPRECATED },
-	{ "tentative",		IFA_F_TENTATIVE },
-	{ "permanent",		IFA_F_PERMANENT },
-	{ "mngtmpaddr",		IFA_F_MANAGETEMPADDR },
-	{ "noprefixroute",	IFA_F_NOPREFIXROUTE },
-	{ "autojoin",		IFA_F_MCAUTOJOIN },
-	{ "stable-privacy",	IFA_F_STABLE_PRIVACY },
-};
-
-static void print_ifa_flags(FILE *fp, const struct ifaddrmsg *ifa,
-			    unsigned int flags)
-{
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(ifa_flag_names); i++) {
-		unsigned long mask = ifa_flag_names[i].value;
-
-		if (mask == IFA_F_PERMANENT) {
-			if (!(flags & mask))
-				print_bool(PRINT_ANY,
-					   "dynamic", "dynamic ", true);
-		} else if (flags & mask) {
-			if (mask == IFA_F_SECONDARY &&
-			    ifa->ifa_family == AF_INET6) {
-				print_bool(PRINT_ANY,
-					   "temporary", "temporary ", true);
-			} else {
-				print_string(PRINT_FP, NULL,
-					     "%s ", ifa_flag_names[i].name);
-				print_bool(PRINT_JSON,
-					   ifa_flag_names[i].name, NULL, true);
-			}
-		}
-
-		flags &= ~mask;
-	}
-
-	if (flags) {
-		if (is_json_context()) {
-			SPRINT_BUF(b1);
-
-			snprintf(b1, sizeof(b1), "%02x", flags);
-			print_string(PRINT_JSON, "ifa_flags", NULL, b1);
-		} else {
-			fprintf(fp, "flags %02x ", flags);
-		}
-	}
-
-}
-
-static int get_filter(const char *arg)
-{
-	bool inv = false;
-	unsigned int i;
-
-	if (arg[0] == '-') {
-		inv = true;
-		arg++;
-	}
-
-	/* Special cases */
-	if (strcmp(arg, "dynamic") == 0) {
-		inv = !inv;
-		arg = "permanent";
-	} else if (strcmp(arg, "primary") == 0) {
-		inv = !inv;
-		arg = "secondary";
-	}
-
-	for (i = 0; i < ARRAY_SIZE(ifa_flag_names); i++) {
-		if (strcmp(arg, ifa_flag_names[i].name))
-			continue;
-
-		if (inv)
-			filter.flags &= ~ifa_flag_names[i].value;
-		else
-			filter.flags |= ifa_flag_names[i].value;
-		filter.flagmask |= ifa_flag_names[i].value;
-		return 0;
-	}
-	return -1;
-}
-
-static int ifa_label_match_rta(int ifindex, const struct rtattr *rta)
-{
-	const char *label;
-
-	if (!filter.label)
-		return 0;
-
-	if (rta)
-		label = RTA_DATA(rta);
-	else
-		label = ll_index_to_name(ifindex);
-
-	return fnmatch(filter.label, label, 0);
-}
-
-int print_addrinfo(struct nlmsghdr *n, void *arg)
+int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		   void *arg)
 {
 	FILE *fp = arg;
 	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];
-
+	struct rtattr * rta_tb[IFA_MAX+1];
+	char abuf[256];
 	SPRINT_BUF(b1);
 
 	if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
@@ -1327,175 +978,209 @@
 		return 0;
 	if ((filter.flags ^ ifa_flags) & filter.flagmask)
 		return 0;
+	if (filter.label) {
+		SPRINT_BUF(b1);
+		const char *label;
+		if (rta_tb[IFA_LABEL])
+			label = RTA_DATA(rta_tb[IFA_LABEL]);
+		else
+			label = ll_idx_n2a(ifa->ifa_index, b1);
+		if (fnmatch(filter.label, label, 0) != 0)
+			return 0;
+	}
+	if (filter.pfx.family) {
+		if (rta_tb[IFA_LOCAL]) {
+			inet_prefix dst;
+			memset(&dst, 0, sizeof(dst));
+			dst.family = ifa->ifa_family;
+			memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL]));
+			if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
+				return 0;
+		}
+	}
 
 	if (filter.family && filter.family != ifa->ifa_family)
 		return 0;
 
-	if (ifa_label_match_rta(ifa->ifa_index, rta_tb[IFA_LABEL]))
-		return 0;
-
-	if (inet_addr_match_rta(&filter.pfx, rta_tb[IFA_LOCAL]))
-		return 0;
-
 	if (filter.flushb) {
 		struct nlmsghdr *fn;
-
 		if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
 			if (flush_update())
 				return -1;
 		}
-		fn = (struct nlmsghdr *)(filter.flushb + NLMSG_ALIGN(filter.flushp));
+		fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
 		memcpy(fn, n, n->nlmsg_len);
 		fn->nlmsg_type = RTM_DELADDR;
 		fn->nlmsg_flags = NLM_F_REQUEST;
 		fn->nlmsg_seq = ++rth.seq;
-		filter.flushp = (((char *)fn) + n->nlmsg_len) - filter.flushb;
+		filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
 		filter.flushed++;
 		if (show_stats < 2)
 			return 0;
 	}
 
 	if (n->nlmsg_type == RTM_DELADDR)
-		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
+		fprintf(fp, "Deleted ");
 
 	if (!brief) {
-		const char *name;
-
-		if (filter.oneline || filter.flushb) {
-			const char *dev = ll_index_to_name(ifa->ifa_index);
-
-			if (is_json_context()) {
-				print_int(PRINT_JSON,
-					  "index", NULL, ifa->ifa_index);
-				print_string(PRINT_JSON, "dev", NULL, dev);
-			} else {
-				fprintf(fp, "%u: %s", ifa->ifa_index, dev);
-			}
-		}
-
-		name = family_name(ifa->ifa_family);
-		if (*name != '?') {
-			print_string(PRINT_ANY, "family", "    %s ", name);
-		} else {
-			print_int(PRINT_ANY, "family_index", "    family %d ",
-				  ifa->ifa_family);
-		}
+		if (filter.oneline || filter.flushb)
+			fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index));
+		if (ifa->ifa_family == AF_INET)
+			fprintf(fp, "    inet ");
+		else if (ifa->ifa_family == AF_INET6)
+			fprintf(fp, "    inet6 ");
+		else if (ifa->ifa_family == AF_DECnet)
+			fprintf(fp, "    dnet ");
+		else if (ifa->ifa_family == AF_IPX)
+			fprintf(fp, "     ipx ");
+		else
+			fprintf(fp, "    family %d ", ifa->ifa_family);
 	}
 
 	if (rta_tb[IFA_LOCAL]) {
-		print_color_string(PRINT_ANY,
-				   ifa_family_color(ifa->ifa_family),
-				   "local", "%s",
-				   format_host_rta(ifa->ifa_family,
-						   rta_tb[IFA_LOCAL]));
-		if (rta_tb[IFA_ADDRESS] &&
-		    memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]),
-			   RTA_DATA(rta_tb[IFA_LOCAL]),
-			   ifa->ifa_family == AF_INET ? 4 : 16)) {
-			print_string(PRINT_FP, NULL, " %s ", "peer");
-			print_color_string(PRINT_ANY,
-					   ifa_family_color(ifa->ifa_family),
-					   "address",
-					   "%s",
-					   format_host_rta(ifa->ifa_family,
-							   rta_tb[IFA_ADDRESS]));
-		}
-		print_int(PRINT_ANY, "prefixlen", "/%d ", ifa->ifa_prefixlen);
+		if (ifa->ifa_family == AF_INET)
+			color_fprintf(fp, COLOR_INET, "%s", format_host(ifa->ifa_family,
+						RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
+						RTA_DATA(rta_tb[IFA_LOCAL]),
+						abuf, sizeof(abuf)));
+		else if (ifa->ifa_family == AF_INET6)
+			color_fprintf(fp, COLOR_INET6, "%s", format_host(ifa->ifa_family,
+						RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
+						RTA_DATA(rta_tb[IFA_LOCAL]),
+						abuf, sizeof(abuf)));
+		else
+			fprintf(fp, "%s", format_host(ifa->ifa_family,
+						RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
+						RTA_DATA(rta_tb[IFA_LOCAL]),
+						abuf, sizeof(abuf)));
 
-		if (rta_tb[IFA_RT_PRIORITY])
-			print_uint(PRINT_ANY, "metric", "metric %u ",
-				   rta_getattr_u32(rta_tb[IFA_RT_PRIORITY]));
+		if (rta_tb[IFA_ADDRESS] == NULL ||
+		    memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]),
+			   ifa->ifa_family == AF_INET ? 4 : 16) == 0) {
+			fprintf(fp, "/%d ", ifa->ifa_prefixlen);
+		} else {
+			fprintf(fp, " peer %s/%d ",
+				format_host(ifa->ifa_family,
+					    RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
+					    RTA_DATA(rta_tb[IFA_ADDRESS]),
+					    abuf, sizeof(abuf)),
+				ifa->ifa_prefixlen);
+		}
 	}
 
 	if (brief)
 		goto brief_exit;
 
 	if (rta_tb[IFA_BROADCAST]) {
-		print_string(PRINT_FP, NULL, "%s ", "brd");
-		print_color_string(PRINT_ANY,
-				   ifa_family_color(ifa->ifa_family),
-				   "broadcast",
-				   "%s ",
-				   format_host_rta(ifa->ifa_family,
-						   rta_tb[IFA_BROADCAST]));
+		fprintf(fp, "brd %s ",
+			format_host(ifa->ifa_family,
+				    RTA_PAYLOAD(rta_tb[IFA_BROADCAST]),
+				    RTA_DATA(rta_tb[IFA_BROADCAST]),
+				    abuf, sizeof(abuf)));
 	}
-
 	if (rta_tb[IFA_ANYCAST]) {
-		print_string(PRINT_FP, NULL, "%s ", "any");
-		print_color_string(PRINT_ANY,
-				   ifa_family_color(ifa->ifa_family),
-				   "anycast",
-				   "%s ",
-				   format_host_rta(ifa->ifa_family,
-						   rta_tb[IFA_ANYCAST]));
+		fprintf(fp, "any %s ",
+			format_host(ifa->ifa_family,
+				    RTA_PAYLOAD(rta_tb[IFA_ANYCAST]),
+				    RTA_DATA(rta_tb[IFA_ANYCAST]),
+				    abuf, sizeof(abuf)));
 	}
-
-	print_string(PRINT_ANY,
-		     "scope",
-		     "scope %s ",
-		     rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
-
-	print_ifa_flags(fp, ifa, ifa_flags);
-
+	fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
+	if (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_flags & IFA_F_TENTATIVE) {
+		ifa_flags &= ~IFA_F_TENTATIVE;
+		fprintf(fp, "tentative ");
+	}
+	if (ifa_flags & IFA_F_DEPRECATED) {
+		ifa_flags &= ~IFA_F_DEPRECATED;
+		deprecated = 1;
+		fprintf(fp, "deprecated ");
+	}
+	if (ifa_flags & IFA_F_HOMEADDRESS) {
+		ifa_flags &= ~IFA_F_HOMEADDRESS;
+		fprintf(fp, "home ");
+	}
+	if (ifa_flags & IFA_F_NODAD) {
+		ifa_flags &= ~IFA_F_NODAD;
+		fprintf(fp, "nodad ");
+	}
+	if (ifa_flags & IFA_F_MANAGETEMPADDR) {
+		ifa_flags &= ~IFA_F_MANAGETEMPADDR;
+		fprintf(fp, "mngtmpaddr ");
+	}
+	if (ifa_flags & IFA_F_NOPREFIXROUTE) {
+		ifa_flags &= ~IFA_F_NOPREFIXROUTE;
+		fprintf(fp, "noprefixroute ");
+	}
+	if (ifa_flags & IFA_F_MCAUTOJOIN) {
+		ifa_flags &= ~IFA_F_MCAUTOJOIN;
+		fprintf(fp, "autojoin ");
+	}
+	if (!(ifa_flags & IFA_F_PERMANENT)) {
+		fprintf(fp, "dynamic ");
+	} else
+		ifa_flags &= ~IFA_F_PERMANENT;
+	if (ifa_flags & IFA_F_DADFAILED) {
+		ifa_flags &= ~IFA_F_DADFAILED;
+		fprintf(fp, "dadfailed ");
+	}
+	if (ifa_flags)
+		fprintf(fp, "flags %02x ", ifa_flags);
 	if (rta_tb[IFA_LABEL])
-		print_string(PRINT_ANY,
-			     "label",
-			     "%s",
-			     rta_getattr_str(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]);
-
-		print_nl();
-		print_string(PRINT_FP, NULL, "       valid_lft ", NULL);
-
-		if (ci->ifa_valid == INFINITY_LIFE_TIME) {
-			print_uint(PRINT_JSON,
-				   "valid_life_time",
-				   NULL, INFINITY_LIFE_TIME);
-			print_string(PRINT_FP, NULL, "%s", "forever");
-		} else {
-			print_uint(PRINT_ANY,
-				   "valid_life_time", "%usec", ci->ifa_valid);
-		}
-
-		print_string(PRINT_FP, NULL, " preferred_lft ", NULL);
-		if (ci->ifa_prefered == INFINITY_LIFE_TIME) {
-			print_uint(PRINT_JSON,
-				   "preferred_life_time",
-				   NULL, INFINITY_LIFE_TIME);
-			print_string(PRINT_FP, NULL, "%s", "forever");
-		} else {
-			if (ifa_flags & IFA_F_DEPRECATED)
-				print_int(PRINT_ANY,
-					  "preferred_life_time",
-					  "%dsec",
-					  ci->ifa_prefered);
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "       valid_lft ");
+		if (ci->ifa_valid == INFINITY_LIFE_TIME)
+			fprintf(fp, "forever");
+		else
+			fprintf(fp, "%usec", ci->ifa_valid);
+		fprintf(fp, " preferred_lft ");
+		if (ci->ifa_prefered == INFINITY_LIFE_TIME)
+			fprintf(fp, "forever");
+		else {
+			if (deprecated)
+				fprintf(fp, "%dsec", ci->ifa_prefered);
 			else
-				print_uint(PRINT_ANY,
-					   "preferred_life_time",
-					   "%usec",
-					   ci->ifa_prefered);
+				fprintf(fp, "%usec", ci->ifa_prefered);
 		}
 	}
-	print_string(PRINT_FP, NULL, "%s", "\n");
+	fprintf(fp, "\n");
 brief_exit:
 	fflush(fp);
 	return 0;
 }
 
+struct nlmsg_list
+{
+	struct nlmsg_list *next;
+	struct nlmsghdr	  h;
+};
+
+struct nlmsg_chain
+{
+	struct nlmsg_list *head;
+	struct nlmsg_list *tail;
+};
+
 static int print_selected_addrinfo(struct ifinfomsg *ifi,
 				   struct nlmsg_list *ainfo, FILE *fp)
 {
-	open_json_array(PRINT_JSON, "addr_info");
-	for ( ; ainfo ;  ainfo = ainfo->next) {
+	for ( ;ainfo ;  ainfo = ainfo->next) {
 		struct nlmsghdr *n = &ainfo->h;
 		struct ifaddrmsg *ifa = NLMSG_DATA(n);
 
 		if (n->nlmsg_type != RTM_NEWADDR)
 			continue;
 
-		if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifa)))
+		if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa)))
 			return -1;
 
 		if (ifa->ifa_index != ifi->ifi_index ||
@@ -1505,26 +1190,23 @@
 		if (filter.up && !(ifi->ifi_flags&IFF_UP))
 			continue;
 
-		open_json_object(NULL);
-		print_addrinfo(n, fp);
-		close_json_object();
+		print_addrinfo(NULL, n, fp);
 	}
-	close_json_array(PRINT_JSON, NULL);
-
 	if (brief) {
-		print_string(PRINT_FP, NULL, "%s", "\n");
+		fprintf(fp, "\n");
 		fflush(fp);
 	}
 	return 0;
 }
 
 
-static int store_nlmsg(struct nlmsghdr *n, void *arg)
+static int store_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		       void *arg)
 {
 	struct nlmsg_chain *lchain = (struct nlmsg_chain *)arg;
 	struct nlmsg_list *h;
 
-	h = malloc(n->nlmsg_len+sizeof(void *));
+	h = malloc(n->nlmsg_len+sizeof(void*));
 	if (h == NULL)
 		return -1;
 
@@ -1537,7 +1219,7 @@
 		lchain->head = h;
 	lchain->tail = h;
 
-	ll_remember_index(n, NULL);
+	ll_remember_index(who, n, NULL);
 	return 0;
 }
 
@@ -1580,7 +1262,8 @@
 	return 0;
 }
 
-static int save_nlmsg(struct nlmsghdr *n, void *arg)
+static int save_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		       void *arg)
 {
 	int ret;
 
@@ -1593,39 +1276,27 @@
 	return ret == n->nlmsg_len ? 0 : ret;
 }
 
-static int show_handler(struct rtnl_ctrl_data *ctrl,
+static int show_handler(const struct sockaddr_nl *nl,
+			struct rtnl_ctrl_data *ctrl,
 			struct nlmsghdr *n, void *arg)
 {
 	struct ifaddrmsg *ifa = NLMSG_DATA(n);
 
-	open_json_object(NULL);
-	print_int(PRINT_ANY, "index", "if%d:\n", ifa->ifa_index);
-	print_addrinfo(n, stdout);
-	close_json_object();
+	printf("if%d:\n", ifa->ifa_index);
+	print_addrinfo(NULL, n, stdout);
 	return 0;
 }
 
 static int ipaddr_showdump(void)
 {
-	int err;
-
 	if (ipadd_dump_check_magic())
 		exit(-1);
 
-	new_json_obj(json);
-	open_json_object(NULL);
-	open_json_array(PRINT_JSON, "addr_info");
-
-	err = rtnl_from_file(stdin, &show_handler, NULL);
-
-	close_json_array(PRINT_JSON, NULL);
-	close_json_object();
-	delete_json_obj();
-
-	exit(err);
+	exit(rtnl_from_file(stdin, &show_handler, NULL));
 }
 
-static int restore_handler(struct rtnl_ctrl_data *ctrl,
+static int restore_handler(const struct sockaddr_nl *nl,
+			   struct rtnl_ctrl_data *ctrl,
 			   struct nlmsghdr *n, void *arg)
 {
 	int ret;
@@ -1634,7 +1305,7 @@
 
 	ll_init_map(&rth);
 
-	ret = rtnl_talk(&rth, n, NULL);
+	ret = rtnl_talk(&rth, n, n, sizeof(*n));
 	if ((ret < 0) && (errno == EEXIST))
 		ret = 0;
 
@@ -1649,7 +1320,7 @@
 	exit(rtnl_from_file(stdin, &restore_handler, NULL));
 }
 
-void free_nlmsg_chain(struct nlmsg_chain *info)
+static void free_nlmsg_chain(struct nlmsg_chain *info)
 {
 	struct nlmsg_list *l, *n;
 
@@ -1664,7 +1335,7 @@
 	struct nlmsg_list *l, **lp;
 
 	lp = &linfo->head;
-	while ((l = *lp) != NULL) {
+	while ( (l = *lp) != NULL) {
 		int ok = 0;
 		int missing_net_address = 1;
 		struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
@@ -1689,14 +1360,29 @@
 
 			if ((filter.flags ^ ifa_flags) & filter.flagmask)
 				continue;
+			if (filter.pfx.family || filter.label) {
+				if (!tb[IFA_LOCAL])
+					tb[IFA_LOCAL] = tb[IFA_ADDRESS];
 
-			if (ifa_label_match_rta(ifa->ifa_index, tb[IFA_LABEL]))
-				continue;
-
-			if (!tb[IFA_LOCAL])
-				tb[IFA_LOCAL] = tb[IFA_ADDRESS];
-			if (inet_addr_match_rta(&filter.pfx, tb[IFA_LOCAL]))
-				continue;
+				if (filter.pfx.family && tb[IFA_LOCAL]) {
+					inet_prefix dst;
+					memset(&dst, 0, sizeof(dst));
+					dst.family = ifa->ifa_family;
+					memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
+					if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
+						continue;
+				}
+				if (filter.label) {
+					SPRINT_BUF(b1);
+					const char *label;
+					if (tb[IFA_LABEL])
+						label = RTA_DATA(tb[IFA_LABEL]);
+					else
+						label = ll_idx_n2a(ifa->ifa_index, b1);
+					if (fnmatch(filter.label, label, 0) != 0)
+						continue;
+				}
+			}
 
 			ok = 1;
 			break;
@@ -1712,15 +1398,6 @@
 	}
 }
 
-static int ipaddr_dump_filter(struct nlmsghdr *nlh, int reqlen)
-{
-	struct ifaddrmsg *ifa = NLMSG_DATA(nlh);
-
-	ifa->ifa_index = filter.ifindex;
-
-	return 0;
-}
-
 static int ipaddr_flush(void)
 {
 	int round = 0;
@@ -1731,8 +1408,7 @@
 	filter.flushe = sizeof(flushb);
 
 	while ((max_flush_loops == 0) || (round < max_flush_loops)) {
-		if (rtnl_addrdump_req(&rth, filter.family,
-				      ipaddr_dump_filter) < 0) {
+		if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
 			perror("Cannot send dump request");
 			exit(1);
 		}
@@ -1748,7 +1424,7 @@
 				if (round == 0)
 					printf("Nothing to flush.\n");
 				else
-					printf("*** Flush is complete after %d round%s ***\n", round, round > 1?"s":"");
+					printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
 			}
 			fflush(stdout);
 			return 0;
@@ -1775,105 +1451,10 @@
 	return 1;
 }
 
-static int iplink_filter_req(struct nlmsghdr *nlh, int reqlen)
-{
-	int err;
-
-	err = addattr32(nlh, reqlen, IFLA_EXT_MASK, RTEXT_FILTER_VF);
-	if (err)
-		return err;
-
-	if (filter.master) {
-		err = addattr32(nlh, reqlen, IFLA_MASTER, filter.master);
-		if (err)
-			return err;
-	}
-
-	if (filter.kind) {
-		struct rtattr *linkinfo;
-
-		linkinfo = addattr_nest(nlh, reqlen, IFLA_LINKINFO);
-
-		err = addattr_l(nlh, reqlen, IFLA_INFO_KIND, filter.kind,
-				strlen(filter.kind));
-		if (err)
-			return err;
-
-		addattr_nest_end(nlh, linkinfo);
-	}
-
-	return 0;
-}
-
-static int ipaddr_link_get(int index, struct nlmsg_chain *linfo)
-{
-	struct iplink_req req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_GETLINK,
-		.i.ifi_family = filter.family,
-		.i.ifi_index = index,
-	};
-	__u32 filt_mask = RTEXT_FILTER_VF;
-	struct nlmsghdr *answer;
-
-	if (!show_stats)
-		filt_mask |= RTEXT_FILTER_SKIP_STATS;
-
-	addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask);
-
-	if (rtnl_talk(&rth, &req.n, &answer) < 0) {
-		perror("Cannot send link request");
-		return 1;
-	}
-
-	if (store_nlmsg(answer, linfo) < 0) {
-		fprintf(stderr, "Failed to process link information\n");
-		return 1;
-	}
-
-	return 0;
-}
-
-/* fills in linfo with link data and optionally ainfo with address info
- * caller can walk lists as desired and must call free_nlmsg_chain for
- * both when done
- */
-int ip_link_list(req_filter_fn_t filter_fn, struct nlmsg_chain *linfo)
-{
-	if (rtnl_linkdump_req_filter_fn(&rth, preferred_family,
-					filter_fn) < 0) {
-		perror("Cannot send dump request");
-		return 1;
-	}
-
-	if (rtnl_dump_filter(&rth, store_nlmsg, linfo) < 0) {
-		fprintf(stderr, "Dump terminated\n");
-		return 1;
-	}
-
-	return 0;
-}
-
-static int ip_addr_list(struct nlmsg_chain *ainfo)
-{
-	if (rtnl_addrdump_req(&rth, filter.family, ipaddr_dump_filter) < 0) {
-		perror("Cannot send dump request");
-		return 1;
-	}
-
-	if (rtnl_dump_filter(&rth, store_nlmsg, ainfo) < 0) {
-		fprintf(stderr, "Dump terminated\n");
-		return 1;
-	}
-
-	return 0;
-}
-
 static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
 {
 	struct nlmsg_chain linfo = { NULL, NULL};
-	struct nlmsg_chain _ainfo = { NULL, NULL}, *ainfo = &_ainfo;
+	struct nlmsg_chain ainfo = { NULL, NULL};
 	struct nlmsg_list *l;
 	char *filter_dev = NULL;
 	int no_link = 0;
@@ -1881,6 +1462,7 @@
 	ipaddr_reset_filter(oneline, 0);
 	filter.showqueue = 1;
 	filter.family = preferred_family;
+	filter.group = -1;
 
 	if (action == IPADD_FLUSH) {
 		if (argc <= 0) {
@@ -1897,13 +1479,11 @@
 	while (argc > 0) {
 		if (strcmp(*argv, "to") == 0) {
 			NEXT_ARG();
-			if (get_prefix(&filter.pfx, *argv, filter.family))
-				invarg("invalid \"to\"\n", *argv);
+			get_prefix(&filter.pfx, *argv, filter.family);
 			if (filter.family == AF_UNSPEC)
 				filter.family = filter.pfx.family;
 		} else if (strcmp(*argv, "scope") == 0) {
-			unsigned int scope = 0;
-
+			unsigned scope = 0;
 			NEXT_ARG();
 			filter.scopemask = -1;
 			if (rtnl_rtscope_a2n(&scope, *argv)) {
@@ -1915,8 +1495,52 @@
 			filter.scope = scope;
 		} else if (strcmp(*argv, "up") == 0) {
 			filter.up = 1;
-		} else if (get_filter(*argv) == 0) {
-
+		} else if (strcmp(*argv, "dynamic") == 0) {
+			filter.flags &= ~IFA_F_PERMANENT;
+			filter.flagmask |= IFA_F_PERMANENT;
+		} else if (strcmp(*argv, "permanent") == 0) {
+			filter.flags |= IFA_F_PERMANENT;
+			filter.flagmask |= IFA_F_PERMANENT;
+		} else if (strcmp(*argv, "secondary") == 0 ||
+			   strcmp(*argv, "temporary") == 0) {
+			filter.flags |= IFA_F_SECONDARY;
+			filter.flagmask |= IFA_F_SECONDARY;
+		} else if (strcmp(*argv, "primary") == 0) {
+			filter.flags &= ~IFA_F_SECONDARY;
+			filter.flagmask |= IFA_F_SECONDARY;
+		} else if (strcmp(*argv, "tentative") == 0) {
+			filter.flags |= IFA_F_TENTATIVE;
+			filter.flagmask |= IFA_F_TENTATIVE;
+		} else if (strcmp(*argv, "-tentative") == 0) {
+			filter.flags &= ~IFA_F_TENTATIVE;
+			filter.flagmask |= IFA_F_TENTATIVE;
+		} else if (strcmp(*argv, "deprecated") == 0) {
+			filter.flags |= IFA_F_DEPRECATED;
+			filter.flagmask |= IFA_F_DEPRECATED;
+		} else if (strcmp(*argv, "-deprecated") == 0) {
+			filter.flags &= ~IFA_F_DEPRECATED;
+			filter.flagmask |= IFA_F_DEPRECATED;
+		} else if (strcmp(*argv, "home") == 0) {
+			filter.flags |= IFA_F_HOMEADDRESS;
+			filter.flagmask |= IFA_F_HOMEADDRESS;
+		} else if (strcmp(*argv, "nodad") == 0) {
+			filter.flags |= IFA_F_NODAD;
+			filter.flagmask |= IFA_F_NODAD;
+		} else if (strcmp(*argv, "mngtmpaddr") == 0) {
+			filter.flags |= IFA_F_MANAGETEMPADDR;
+			filter.flagmask |= IFA_F_MANAGETEMPADDR;
+		} else if (strcmp(*argv, "noprefixroute") == 0) {
+			filter.flags |= IFA_F_NOPREFIXROUTE;
+			filter.flagmask |= IFA_F_NOPREFIXROUTE;
+		} else if (strcmp(*argv, "autojoin") == 0) {
+			filter.flags |= IFA_F_MCAUTOJOIN;
+			filter.flagmask |= IFA_F_MCAUTOJOIN;
+		} else if (strcmp(*argv, "dadfailed") == 0) {
+			filter.flags |= IFA_F_DADFAILED;
+			filter.flagmask |= IFA_F_DADFAILED;
+		} else if (strcmp(*argv, "-dadfailed") == 0) {
+			filter.flags &= ~IFA_F_DADFAILED;
+			filter.flagmask |= IFA_F_DADFAILED;
 		} else if (strcmp(*argv, "label") == 0) {
 			NEXT_ARG();
 			filter.label = *argv;
@@ -1926,36 +1550,18 @@
 				invarg("Invalid \"group\" value\n", *argv);
 		} else if (strcmp(*argv, "master") == 0) {
 			int ifindex;
-
 			NEXT_ARG();
 			ifindex = ll_name_to_index(*argv);
 			if (!ifindex)
 				invarg("Device does not exist\n", *argv);
 			filter.master = ifindex;
-		} else if (strcmp(*argv, "vrf") == 0) {
-			int ifindex;
-
+		} else if (do_link && strcmp(*argv, "type") == 0) {
 			NEXT_ARG();
-			ifindex = ll_name_to_index(*argv);
-			if (!ifindex)
-				invarg("Not a valid VRF name\n", *argv);
-			if (!name_is_vrf(*argv))
-				invarg("Not a valid VRF name\n", *argv);
-			filter.master = ifindex;
-		} else if (strcmp(*argv, "type") == 0) {
-			int soff;
-
-			NEXT_ARG();
-			soff = strlen(*argv) - strlen("_slave");
-			if (!strcmp(*argv + soff, "_slave")) {
-				(*argv)[soff] = '\0';
-				filter.slave_kind = *argv;
-			} else {
-				filter.kind = *argv;
-			}
+			filter.kind = *argv;
 		} else {
-			if (strcmp(*argv, "dev") == 0)
+			if (strcmp(*argv, "dev") == 0) {
 				NEXT_ARG();
+			}
 			else if (matches(*argv, "help") == 0)
 				usage();
 			if (filter_dev)
@@ -1980,8 +1586,7 @@
 		if (ipadd_save_prep())
 			exit(1);
 
-		if (rtnl_addrdump_req(&rth, preferred_family,
-				      ipaddr_dump_filter) < 0) {
+		if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETADDR) < 0) {
 			perror("Cannot send dump request");
 			exit(1);
 		}
@@ -1995,64 +1600,69 @@
 	}
 
 	/*
-	 * Initialize a json_writer and open an array object
-	 * if -json was specified.
-	 */
-	new_json_obj(json);
-
-	/*
 	 * If only filter_dev present and none of the other
 	 * link filters are present, use RTM_GETLINK to get
 	 * the link device
 	 */
 	if (filter_dev && filter.group == -1 && do_link == 1) {
-		if (iplink_get(filter_dev, RTEXT_FILTER_VF) < 0) {
+		if (iplink_get(0, filter_dev, RTEXT_FILTER_VF) < 0) {
 			perror("Cannot send link get request");
-			delete_json_obj();
 			exit(1);
 		}
-		delete_json_obj();
-		goto out;
+		exit(0);
 	}
 
-	if (filter.ifindex) {
-		if (ipaddr_link_get(filter.ifindex, &linfo) != 0)
-			goto out;
-	} else {
-		if (ip_link_list(iplink_filter_req, &linfo) != 0)
-			goto out;
+	if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
+		perror("Cannot send dump request");
+		exit(1);
+	}
+
+	if (rtnl_dump_filter(&rth, store_nlmsg, &linfo) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		exit(1);
 	}
 
 	if (filter.family != AF_PACKET) {
 		if (filter.oneline)
 			no_link = 1;
 
-		if (ip_addr_list(ainfo) != 0)
-			goto out;
+		if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
+			perror("Cannot send dump request");
+			exit(1);
+		}
 
-		ipaddr_filter(&linfo, ainfo);
+		if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo) < 0) {
+			fprintf(stderr, "Dump terminated\n");
+			exit(1);
+		}
+
+		ipaddr_filter(&linfo, &ainfo);
 	}
 
 	for (l = linfo.head; l; l = l->next) {
-		struct nlmsghdr *n = &l->h;
-		struct ifinfomsg *ifi = NLMSG_DATA(n);
 		int res = 0;
+		struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
 
-		open_json_object(NULL);
-		if (brief || !no_link)
-			res = print_linkinfo(n, stdout);
-		if (res >= 0 && filter.family != AF_PACKET)
-			print_selected_addrinfo(ifi, ainfo->head, stdout);
-		if (res > 0 && !do_link && show_stats)
-			print_link_stats(stdout, n);
-		close_json_object();
+		if (brief) {
+			if (print_linkinfo_brief(NULL, &l->h, stdout) == 0)
+				if (filter.family != AF_PACKET)
+					print_selected_addrinfo(ifi,
+								ainfo.head,
+								stdout);
+		} else if (no_link ||
+			 (res = print_linkinfo(NULL, &l->h, stdout)) >= 0) {
+			if (filter.family != AF_PACKET)
+				print_selected_addrinfo(ifi,
+							ainfo.head, stdout);
+			if (res > 0 && !do_link && show_stats)
+				print_link_stats(stdout, &l->h);
+		}
 	}
 	fflush(stdout);
 
-out:
-	free_nlmsg_chain(ainfo);
+	free_nlmsg_chain(&ainfo);
 	free_nlmsg_chain(&linfo);
-	delete_json_obj();
+
 	return 0;
 }
 
@@ -2068,12 +1678,6 @@
 
 	for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 		parse_rtattr_nested(vf, IFLA_VF_MAX, i);
-
-		if (!vf[IFLA_VF_RATE]) {
-			fprintf(stderr, "VF min/max rate API not supported\n");
-			exit(1);
-		}
-
 		vf_rate = RTA_DATA(vf[IFLA_VF_RATE]);
 		if (vf_rate->vf == vfnum) {
 			*min = vf_rate->min_tx_rate;
@@ -2085,22 +1689,16 @@
 	exit(1);
 }
 
-void ipaddr_get_vf_rate(int vfnum, int *min, int *max, const char *dev)
+void ipaddr_get_vf_rate(int vfnum, int *min, int *max, int idx)
 {
 	struct nlmsg_chain linfo = { NULL, NULL};
 	struct rtattr *tb[IFLA_MAX+1];
 	struct ifinfomsg *ifi;
 	struct nlmsg_list *l;
 	struct nlmsghdr *n;
-	int idx, len;
+	int len;
 
-	idx = ll_name_to_index(dev);
-	if (idx == 0) {
-		fprintf(stderr, "Device %s does not exist\n", dev);
-		exit(1);
-	}
-
-	if (rtnl_linkdump_req(&rth, AF_UNSPEC) < 0) {
+	if (rtnl_wilddump_request(&rth, AF_UNSPEC, RTM_GETLINK) < 0) {
 		perror("Cannot send dump request");
 		exit(1);
 	}
@@ -2137,13 +1735,12 @@
 	memset(&filter, 0, sizeof(filter));
 	filter.oneline = oneline;
 	filter.ifindex = ifindex;
-	filter.group = -1;
 }
 
 static int default_scope(inet_prefix *lcl)
 {
 	if (lcl->family == AF_INET) {
-		if (lcl->bytelen >= 1 && *(__u8 *)&lcl->data == 127)
+		if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127)
 			return RT_SCOPE_HOST;
 	}
 	return 0;
@@ -2159,34 +1756,19 @@
 		return false;
 }
 
-static bool is_valid_label(const char *dev, const char *label)
-{
-	size_t len = strlen(dev);
-
-	if (strncmp(label, dev, len) != 0)
-		return false;
-
-	return label[len] == '\0' || label[len] == ':';
-}
-
 static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
 {
 	struct {
 		struct nlmsghdr	n;
 		struct ifaddrmsg	ifa;
 		char			buf[256];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST | flags,
-		.n.nlmsg_type = cmd,
-		.ifa.ifa_family = preferred_family,
-	};
+	} req;
 	char  *d = NULL;
 	char  *l = NULL;
 	char  *lcl_arg = NULL;
 	char  *valid_lftp = NULL;
 	char  *preferred_lftp = NULL;
-	inet_prefix lcl = {};
+	inet_prefix lcl;
 	inet_prefix peer;
 	int local_len = 0;
 	int peer_len = 0;
@@ -2195,8 +1777,16 @@
 	int scoped = 0;
 	__u32 preferred_lft = INFINITY_LIFE_TIME;
 	__u32 valid_lft = INFINITY_LIFE_TIME;
+	struct ifa_cacheinfo cinfo;
 	unsigned int ifa_flags = 0;
 
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST | flags;
+	req.n.nlmsg_type = cmd;
+	req.ifa.ifa_family = preferred_family;
+
 	while (argc > 0) {
 		if (strcmp(*argv, "peer") == 0 ||
 		    strcmp(*argv, "remote") == 0) {
@@ -2213,7 +1803,6 @@
 		} else if (matches(*argv, "broadcast") == 0 ||
 			   strcmp(*argv, "brd") == 0) {
 			inet_prefix addr;
-
 			NEXT_ARG();
 			if (brd_len)
 				duparg("broadcast", *argv);
@@ -2230,7 +1819,6 @@
 			}
 		} else if (strcmp(*argv, "anycast") == 0) {
 			inet_prefix addr;
-
 			NEXT_ARG();
 			if (any_len)
 				duparg("anycast", *argv);
@@ -2240,8 +1828,7 @@
 			addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
 			any_len = addr.bytelen;
 		} else if (strcmp(*argv, "scope") == 0) {
-			unsigned int scope = 0;
-
+			unsigned scope = 0;
 			NEXT_ARG();
 			if (rtnl_rtscope_a2n(&scope, *argv))
 				invarg("invalid scope value.", *argv);
@@ -2254,15 +1841,6 @@
 			NEXT_ARG();
 			l = *argv;
 			addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
-		} else if (matches(*argv, "metric") == 0 ||
-			   matches(*argv, "priority") == 0 ||
-			   matches(*argv, "preference") == 0) {
-			__u32 metric;
-
-			NEXT_ARG();
-			if (get_u32(&metric, *argv, 0))
-				invarg("\"metric\" value is invalid\n", *argv);
-			addattr32(&req.n, sizeof(req), IFA_RT_PRIORITY, metric);
 		} else if (matches(*argv, "valid_lft") == 0) {
 			if (valid_lftp)
 				duparg("valid_lft", *argv);
@@ -2278,32 +1856,19 @@
 			if (set_lifetime(&preferred_lft, *argv))
 				invarg("preferred_lft value", *argv);
 		} else if (strcmp(*argv, "home") == 0) {
-			if (req.ifa.ifa_family == AF_INET6)
-				ifa_flags |= IFA_F_HOMEADDRESS;
-			else
-				fprintf(stderr, "Warning: home option can be set only for IPv6 addresses\n");
-		} else if (strcmp(*argv, "optimistic") == 0) {
-			if (req.ifa.ifa_family == AF_INET6)
-				ifa_flags |= IFA_F_OPTIMISTIC;
-			else
-				fprintf(stderr, "Warning: optimistic option can be set only for IPv6 addresses\n");
+			ifa_flags |= IFA_F_HOMEADDRESS;
 		} else if (strcmp(*argv, "nodad") == 0) {
-			if (req.ifa.ifa_family == AF_INET6)
-				ifa_flags |= IFA_F_NODAD;
-			else
-				fprintf(stderr, "Warning: nodad option can be set only for IPv6 addresses\n");
+			ifa_flags |= IFA_F_NODAD;
 		} else if (strcmp(*argv, "mngtmpaddr") == 0) {
-			if (req.ifa.ifa_family == AF_INET6)
-				ifa_flags |= IFA_F_MANAGETEMPADDR;
-			else
-				fprintf(stderr, "Warning: mngtmpaddr option can be set only for IPv6 addresses\n");
+			ifa_flags |= IFA_F_MANAGETEMPADDR;
 		} else if (strcmp(*argv, "noprefixroute") == 0) {
 			ifa_flags |= IFA_F_NOPREFIXROUTE;
 		} else if (strcmp(*argv, "autojoin") == 0) {
 			ifa_flags |= IFA_F_MCAUTOJOIN;
 		} else {
-			if (strcmp(*argv, "local") == 0)
+			if (strcmp(*argv, "local") == 0) {
 				NEXT_ARG();
+			}
 			if (matches(*argv, "help") == 0)
 				usage();
 			if (local_len)
@@ -2326,19 +1891,17 @@
 		fprintf(stderr, "Not enough information: \"dev\" argument is required.\n");
 		return -1;
 	}
-	if (l && !is_valid_label(d, l)) {
-		fprintf(stderr,
-			"\"label\" (%s) must match \"dev\" (%s) or be prefixed by \"dev\" with a colon.\n",
-			l, d);
+	if (l && matches(d, l) != 0) {
+		fprintf(stderr, "\"dev\" (%s) must match \"label\" (%s).\n", d, l);
 		return -1;
 	}
 
 	if (peer_len == 0 && local_len) {
 		if (cmd == RTM_DELADDR && lcl.family == AF_INET && !(lcl.flags & PREFIXLEN_SPECIFIED)) {
 			fprintf(stderr,
-			    "Warning: Executing wildcard deletion to stay compatible with old scripts.\n"
-			    "         Explicitly specify the prefix length (%s/%d) to avoid this warning.\n"
-			    "         This special behaviour is likely to disappear in further releases,\n"
+			    "Warning: Executing wildcard deletion to stay compatible with old scripts.\n" \
+			    "         Explicitly specify the prefix length (%s/%d) to avoid this warning.\n" \
+			    "         This special behaviour is likely to disappear in further releases,\n" \
 			    "         fix your scripts!\n", lcl_arg, local_len*8);
 		} else {
 			peer = lcl;
@@ -2351,7 +1914,6 @@
 	if (brd_len < 0 && cmd != RTM_DELADDR) {
 		inet_prefix brd;
 		int i;
-
 		if (req.ifa.ifa_family != AF_INET) {
 			fprintf(stderr, "Broadcast can be set only for IPv4 addresses\n");
 			return -1;
@@ -2371,13 +1933,12 @@
 	if (!scoped && cmd != RTM_DELADDR)
 		req.ifa.ifa_scope = default_scope(&lcl);
 
-	req.ifa.ifa_index = ll_name_to_index(d);
-	if (!req.ifa.ifa_index)
-		return nodev(d);
+	if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) {
+		fprintf(stderr, "Cannot find device \"%s\"\n", d);
+		return -1;
+	}
 
 	if (valid_lftp || preferred_lftp) {
-		struct ifa_cacheinfo cinfo = {};
-
 		if (!valid_lft) {
 			fprintf(stderr, "valid_lft is zero\n");
 			return -1;
@@ -2387,6 +1948,7 @@
 			return -1;
 		}
 
+		memset(&cinfo, 0, sizeof(cinfo));
 		cinfo.ifa_prefered = preferred_lft;
 		cinfo.ifa_valid = valid_lft;
 		addattr_l(&req.n, sizeof(req), IFA_CACHEINFO, &cinfo,
@@ -2398,7 +1960,7 @@
 		return -1;
 	}
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		return -2;
 
 	return 0;
diff --git a/ip/ipaddrlabel.c b/ip/ipaddrlabel.c
index beb08da..f01bc26 100644
--- a/ip/ipaddrlabel.c
+++ b/ip/ipaddrlabel.c
@@ -26,6 +26,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -38,10 +39,9 @@
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
-#include "json_print.h"
 
-#define IFAL_RTA(r)	((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrlblmsg))))
-#define IFAL_PAYLOAD(n)	NLMSG_PAYLOAD(n, sizeof(struct ifaddrlblmsg))
+#define IFAL_RTA(r)	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrlblmsg))))
+#define IFAL_PAYLOAD(n)	NLMSG_PAYLOAD(n,sizeof(struct ifaddrlblmsg))
 
 extern struct rtnl_handle rth;
 
@@ -49,17 +49,17 @@
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: ip addrlabel { add | del } prefix PREFIX [ dev DEV ] [ label LABEL ]\n"
-		"       ip addrlabel [ list | flush | help ]\n");
+	fprintf(stderr, "Usage: ip addrlabel [ list | add | del | flush ] prefix PREFIX [ dev DEV ] [ label LABEL ]\n");
 	exit(-1);
 }
 
-int print_addrlabel(struct nlmsghdr *n, void *arg)
+int print_addrlabel(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
+	FILE *fp = (FILE*)arg;
 	struct ifaddrlblmsg *ifal = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr *tb[IFAL_MAX+1];
+	char abuf[256];
 
 	if (n->nlmsg_type != RTM_NEWADDRLABEL && n->nlmsg_type != RTM_DELADDRLABEL)
 		return 0;
@@ -70,40 +70,29 @@
 
 	parse_rtattr(tb, IFAL_MAX, IFAL_RTA(ifal), len);
 
-	open_json_object(NULL);
 	if (n->nlmsg_type == RTM_DELADDRLABEL)
-		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
+		fprintf(fp, "Deleted ");
 
 	if (tb[IFAL_ADDRESS]) {
-		const char *host
-			= format_host_rta(ifal->ifal_family,
-					  tb[IFAL_ADDRESS]);
-
-		print_string(PRINT_FP, NULL, "prefix ", NULL);
-		print_color_string(PRINT_ANY,
-				   ifa_family_color(ifal->ifal_family),
-				   "address", "%s", host);
-
-		print_uint(PRINT_ANY, "prefixlen", "/%u ",
-			   ifal->ifal_prefixlen);
+		fprintf(fp, "prefix %s/%u ",
+			format_host(ifal->ifal_family,
+				    RTA_PAYLOAD(tb[IFAL_ADDRESS]),
+				    RTA_DATA(tb[IFAL_ADDRESS]),
+				    abuf, sizeof(abuf)),
+			ifal->ifal_prefixlen);
 	}
 
-	if (ifal->ifal_index) {
-		print_string(PRINT_FP, NULL, "dev ", NULL);
-		print_color_string(PRINT_ANY, COLOR_IFNAME,
-				   "ifname", "%s ",
-				   ll_index_to_name(ifal->ifal_index));
-	}
+	if (ifal->ifal_index)
+		fprintf(fp, "dev %s ", ll_index_to_name(ifal->ifal_index));
 
 	if (tb[IFAL_LABEL] && RTA_PAYLOAD(tb[IFAL_LABEL]) == sizeof(uint32_t)) {
-		uint32_t label = rta_getattr_u32(tb[IFAL_LABEL]);
-
-		print_uint(PRINT_ANY,
-			   "label", "label %u ", label);
+		uint32_t label;
+		memcpy(&label, RTA_DATA(tb[IFAL_LABEL]), sizeof(label));
+		fprintf(fp, "label %u ", label);
 	}
-	print_string(PRINT_FP, NULL, "\n", "");
-	close_json_object();
 
+	fprintf(fp, "\n");
+	fflush(fp);
 	return 0;
 }
 
@@ -119,17 +108,15 @@
 		return -1;
 	}
 
-	if (rtnl_addrlbldump_req(&rth, af) < 0) {
+	if (rtnl_wilddump_request(&rth, af, RTM_GETADDRLABEL) < 0) {
 		perror("Cannot send dump request");
 		return 1;
 	}
 
-	new_json_obj(json);
 	if (rtnl_dump_filter(&rth, print_addrlabel, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		return 1;
 	}
-	delete_json_obj();
 
 	return 0;
 }
@@ -140,19 +127,24 @@
 	struct {
 		struct nlmsghdr	n;
 		struct ifaddrlblmsg	ifal;
-		char			buf[1024];
-	} req = {
-		.n.nlmsg_type = cmd,
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrlblmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.ifal.ifal_family = preferred_family,
-	};
+		char  			buf[1024];
+	} req;
 
-	inet_prefix prefix = {};
+	inet_prefix prefix;
 	uint32_t label = 0xffffffffUL;
 	char *p = NULL;
 	char *l = NULL;
 
+	memset(&req, 0, sizeof(req));
+	memset(&prefix, 0, sizeof(prefix));
+
+	req.n.nlmsg_type = cmd;
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrlblmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.ifal.ifal_family = preferred_family;
+	req.ifal.ifal_prefixlen = 0;
+	req.ifal.ifal_index = 0;
+
 	if (cmd == RTM_NEWADDRLABEL) {
 		req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
 	}
@@ -190,19 +182,19 @@
 	if (req.ifal.ifal_family == AF_UNSPEC)
 		req.ifal.ifal_family = AF_INET6;
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		return -2;
 
 	return 0;
 }
 
 
-static int flush_addrlabel(struct nlmsghdr *n, void *arg)
+static int flush_addrlabel(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
 	struct rtnl_handle rth2;
 	struct rtmsg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
-	struct rtattr *tb[IFAL_MAX+1];
+	struct rtattr * tb[IFAL_MAX+1];
 
 	len -= NLMSG_LENGTH(sizeof(*r));
 	if (len < 0)
@@ -217,7 +209,7 @@
 		if (rtnl_open(&rth2, 0) < 0)
 			return -1;
 
-		if (rtnl_talk(&rth2, n, NULL) < 0)
+		if (rtnl_talk(&rth2, n, NULL, 0) < 0)
 			return -2;
 
 		rtnl_close(&rth2);
@@ -238,7 +230,7 @@
 		return -1;
 	}
 
-	if (rtnl_addrlbldump_req(&rth, af) < 0) {
+	if (rtnl_wilddump_request(&rth, af, RTM_GETADDRLABEL) < 0) {
 		perror("Cannot send dump request");
 		return -1;
 	}
diff --git a/ip/ipfou.c b/ip/ipfou.c
index ea126b0..8a86b18 100644
--- a/ip/ipfou.c
+++ b/ip/ipfou.c
@@ -22,22 +22,14 @@
 #include "libgenl.h"
 #include "utils.h"
 #include "ip_common.h"
-#include "json_print.h"
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: ip fou add port PORT { ipproto PROTO  | gue } [ -6 ]\n"
-		"		   [ local IFADDR ] [ peer IFADDR ]\n"
-		"		   [ peer_port PORT ] [ dev IFNAME ]\n"
-		"       ip fou del port PORT [ -6 ] [ local IFADDR ]\n"
-		"		   [ peer IFADDR ] [ peer_port PORT ]\n"
-		"		   [ dev IFNAME ]\n"
-		"       ip fou show\n"
-		"\n"
-		"Where: PROTO { ipproto-name | 1..255 }\n"
-		"       PORT { 1..65535 }\n"
-		"       IFADDR { addr }\n");
+	fprintf(stderr, "Usage: ip fou add port PORT { ipproto PROTO  | gue }\n");
+	fprintf(stderr, "       ip fou del port PORT\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: PROTO { ipproto-name | 1..255 }\n");
+	fprintf(stderr, "       PORT { 1..65535 }\n");
 
 	exit(-1);
 }
@@ -53,21 +45,19 @@
 static int fou_parse_opt(int argc, char **argv, struct nlmsghdr *n,
 			 bool adding)
 {
-	const char *local = NULL, *peer = NULL;
-	__u16 port, peer_port = 0;
-	__u8 family = AF_INET;
+	__u16 port;
+	int port_set = 0;
+	__u8 ipproto, type;
 	bool gue_set = false;
 	int ipproto_set = 0;
-	__u8 ipproto, type;
-	int port_set = 0;
-	int index = 0;
 
 	while (argc > 0) {
 		if (!matches(*argv, "port")) {
 			NEXT_ARG();
 
-			if (get_be16(&port, *argv, 0) || port == 0)
+			if (get_u16(&port, *argv, 0) || port == 0)
 				invarg("invalid port", *argv);
+			port = htons(port);
 			port_set = 1;
 		} else if (!matches(*argv, "ipproto")) {
 			struct protoent *servptr;
@@ -82,42 +72,8 @@
 			ipproto_set = 1;
 		} else if (!matches(*argv, "gue")) {
 			gue_set = true;
-		} else if (!matches(*argv, "-6")) {
-			family = AF_INET6;
-		} else if (!matches(*argv, "local")) {
-			NEXT_ARG();
-
-			local = *argv;
-		} else if (!matches(*argv, "peer")) {
-			NEXT_ARG();
-
-			peer = *argv;
-		} else if (!matches(*argv, "peer_port")) {
-			NEXT_ARG();
-
-			if (get_be16(&peer_port, *argv, 0) || peer_port == 0)
-				invarg("invalid peer port", *argv);
-		} else if (!matches(*argv, "dev")) {
-			const char *ifname;
-
-			NEXT_ARG();
-
-			ifname = *argv;
-
-			if (check_ifname(ifname)) {
-				fprintf(stderr, "fou: invalid device name\n");
-				exit(EXIT_FAILURE);
-			}
-
-			index = ll_name_to_index(ifname);
-
-			if (!index) {
-				fprintf(stderr, "fou: unknown device name\n");
-				exit(EXIT_FAILURE);
-			}
 		} else {
-			fprintf(stderr
-				, "fou: unknown command \"%s\"?\n", *argv);
+			fprintf(stderr, "fou: unknown command \"%s\"?\n", *argv);
 			usage();
 			return -1;
 		}
@@ -139,52 +95,14 @@
 		return -1;
 	}
 
-	if ((peer_port && !peer) || (peer && !peer_port)) {
-		fprintf(stderr, "fou: both peer and peer port must be set\n");
-		return -1;
-	}
-
 	type = gue_set ? FOU_ENCAP_GUE : FOU_ENCAP_DIRECT;
 
 	addattr16(n, 1024, FOU_ATTR_PORT, port);
 	addattr8(n, 1024, FOU_ATTR_TYPE, type);
-	addattr8(n, 1024, FOU_ATTR_AF, family);
 
 	if (ipproto_set)
 		addattr8(n, 1024, FOU_ATTR_IPPROTO, ipproto);
 
-	if (local) {
-		inet_prefix local_addr;
-		__u8 attr_type = family == AF_INET ? FOU_ATTR_LOCAL_V4 :
-						     FOU_ATTR_LOCAL_V6;
-
-		if (get_addr(&local_addr, local, family)) {
-			fprintf(stderr, "fou: parsing local address failed\n");
-			exit(EXIT_FAILURE);
-		}
-		addattr_l(n, 1024, attr_type, &local_addr.data,
-			  local_addr.bytelen);
-	}
-
-	if (peer) {
-		inet_prefix peer_addr;
-		__u8 attr_type = family == AF_INET ? FOU_ATTR_PEER_V4 :
-						     FOU_ATTR_PEER_V6;
-
-		if (get_addr(&peer_addr, peer, family)) {
-			fprintf(stderr, "fou: parsing peer address failed\n");
-			exit(EXIT_FAILURE);
-		}
-		addattr_l(n, 1024, attr_type, &peer_addr.data,
-			  peer_addr.bytelen);
-
-		if (peer_port)
-			addattr16(n, 1024, FOU_ATTR_PEER_PORT, peer_port);
-	}
-
-	if (index)
-		addattr32(n, 1024, FOU_ATTR_IFINDEX, index);
-
 	return 0;
 }
 
@@ -194,7 +112,7 @@
 
 	fou_parse_opt(argc, argv, &req.n, true);
 
-	if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0)
 		return -2;
 
 	return 0;
@@ -206,145 +124,35 @@
 
 	fou_parse_opt(argc, argv, &req.n, false);
 
-	if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0)
 		return -2;
 
 	return 0;
 }
 
-static int print_fou_mapping(struct nlmsghdr *n, void *arg)
-{
-	__u8 family = AF_INET, local_attr_type, peer_attr_type, byte_len;
-	struct rtattr *tb[FOU_ATTR_MAX + 1];
-	__u8 empty_buf[16] = {0};
-	struct genlmsghdr *ghdr;
-	int len = n->nlmsg_len;
-
-	if (n->nlmsg_type != genl_family)
-		return 0;
-
-	len -= NLMSG_LENGTH(GENL_HDRLEN);
-	if (len < 0)
-		return -1;
-
-	ghdr = NLMSG_DATA(n);
-	parse_rtattr(tb, FOU_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len);
-
-	open_json_object(NULL);
-	if (tb[FOU_ATTR_PORT])
-		print_uint(PRINT_ANY, "port", "port %u",
-			   ntohs(rta_getattr_u16(tb[FOU_ATTR_PORT])));
-
-	if (tb[FOU_ATTR_TYPE] &&
-	    rta_getattr_u8(tb[FOU_ATTR_TYPE]) == FOU_ENCAP_GUE)
-		print_null(PRINT_ANY, "gue", " gue", NULL);
-	else if (tb[FOU_ATTR_IPPROTO])
-		print_uint(PRINT_ANY, "ipproto",
-			   " ipproto %u", rta_getattr_u8(tb[FOU_ATTR_IPPROTO]));
-
-	if (tb[FOU_ATTR_AF]) {
-		family = rta_getattr_u8(tb[FOU_ATTR_AF]);
-
-		print_string(PRINT_JSON, "family", NULL,
-			     family_name(family));
-
-		if (family == AF_INET6)
-			print_string(PRINT_FP, NULL,
-				     " -6", NULL);
-	}
-
-	local_attr_type = family == AF_INET ? FOU_ATTR_LOCAL_V4 :
-					      FOU_ATTR_LOCAL_V6;
-	peer_attr_type = family == AF_INET ? FOU_ATTR_PEER_V4 :
-					     FOU_ATTR_PEER_V6;
-	byte_len = af_bit_len(family) / 8;
-
-	if (tb[local_attr_type] && memcmp(RTA_DATA(tb[local_attr_type]),
-					  empty_buf, byte_len)) {
-		print_string(PRINT_ANY, "local", " local %s",
-			     format_host_rta(family, tb[local_attr_type]));
-	}
-
-	if (tb[peer_attr_type] && memcmp(RTA_DATA(tb[peer_attr_type]),
-					 empty_buf, byte_len)) {
-		print_string(PRINT_ANY, "peer", " peer %s",
-			     format_host_rta(family, tb[peer_attr_type]));
-	}
-
-	if (tb[FOU_ATTR_PEER_PORT]) {
-		__u16 p_port = ntohs(rta_getattr_u16(tb[FOU_ATTR_PEER_PORT]));
-
-		if (p_port)
-			print_uint(PRINT_ANY, "peer_port", " peer_port %u",
-				   p_port);
-
-	}
-
-	if (tb[FOU_ATTR_IFINDEX]) {
-		int index = rta_getattr_s32(tb[FOU_ATTR_IFINDEX]);
-
-		if (index) {
-			const char *ifname;
-
-			ifname = ll_index_to_name(index);
-
-			if (ifname)
-				print_string(PRINT_ANY, "dev", " dev %s",
-					     ifname);
-		}
-	}
-
-	print_string(PRINT_FP, NULL, "\n", NULL);
-	close_json_object();
-
-	return 0;
-}
-
-static int do_show(int argc, char **argv)
-{
-	FOU_REQUEST(req, 4096, FOU_CMD_GET, NLM_F_REQUEST | NLM_F_DUMP);
-
-	if (argc > 0) {
-		fprintf(stderr,
-			"\"ip fou show\" does not take any arguments.\n");
-		return -1;
-	}
-
-	if (rtnl_send(&genl_rth, &req.n, req.n.nlmsg_len) < 0) {
-		perror("Cannot send show request");
-		exit(1);
-	}
-
-	new_json_obj(json);
-	if (rtnl_dump_filter(&genl_rth, print_fou_mapping, stdout) < 0) {
-		fprintf(stderr, "Dump terminated\n");
-		return 1;
-	}
-	delete_json_obj();
-	fflush(stdout);
-
-	return 0;
-}
-
 int do_ipfou(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_resolve_family(&genl_rth, FOU_GENL_NAME);
+		if (genl_family < 0)
+			exit(1);
+	}
+
 	if (argc < 1)
 		usage();
 
-	if (matches(*argv, "help") == 0)
-		usage();
-
-	if (genl_init_handle(&genl_rth, FOU_GENL_NAME, &genl_family))
-		exit(1);
-
 	if (matches(*argv, "add") == 0)
 		return do_add(argc-1, argv+1);
 	if (matches(*argv, "delete") == 0)
 		return do_del(argc-1, argv+1);
-	if (matches(*argv, "show") == 0)
-		return do_show(argc-1, argv+1);
+	if (matches(*argv, "help") == 0)
+		usage();
 
-	fprintf(stderr,
-		"Command \"%s\" is unknown, try \"ip fou help\".\n", *argv);
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip fou help\".\n", *argv);
 	exit(-1);
 }
diff --git a/ip/ipila.c b/ip/ipila.c
deleted file mode 100644
index 739ee4e..0000000
--- a/ip/ipila.c
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * ipila.c	ILA (Identifier Locator Addressing) 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.
- *
- * Authors:	Tom Herbert <tom@herbertland.com>
- */
-
-#include <netdb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <net/if.h>
-#include <linux/ila.h>
-#include <linux/genetlink.h>
-#include <linux/ip.h>
-#include <arpa/inet.h>
-
-#include "libgenl.h"
-#include "utils.h"
-#include "ip_common.h"
-#include "ila_common.h"
-#include "json_print.h"
-
-static void usage(void)
-{
-	fprintf(stderr,
-		"Usage: ip ila add loc_match LOCATOR_MATCH loc LOCATOR [ dev DEV ] OPTIONS\n"
-		"       ip ila del loc_match LOCATOR_MATCH [ loc LOCATOR ] [ dev DEV ]\n"
-		"       ip ila list\n"
-		"OPTIONS := [ csum-mode { adj-transport | neutral-map | neutral-map-auto | no-action } ]\n"
-		"           [ ident-type { luid | use-format } ]\n");
-
-	exit(-1);
-}
-
-/* netlink socket */
-static struct rtnl_handle genl_rth = { .fd = -1 };
-static int genl_family = -1;
-
-#define ILA_REQUEST(_req, _bufsiz, _cmd, _flags)	\
-	GENL_REQUEST(_req, _bufsiz, genl_family, 0,	\
-		     ILA_GENL_VERSION, _cmd, _flags)
-
-#define ILA_RTA(g) ((struct rtattr *)(((char *)(g)) +	\
-	NLMSG_ALIGN(sizeof(struct genlmsghdr))))
-
-static void print_addr64(__u64 addr, char *buff, size_t len)
-{
-	__u16 *words = (__u16 *)&addr;
-	__u16 v;
-	int i, ret;
-	size_t written = 0;
-	char *sep = ":";
-
-	for (i = 0; i < 4; i++) {
-		v = ntohs(words[i]);
-
-		if (i == 3)
-			sep = "";
-
-		ret = snprintf(&buff[written], len - written, "%x%s", v, sep);
-		written += ret;
-	}
-}
-
-static void print_ila_locid(const char *tag, int attr, struct rtattr *tb[])
-{
-	char abuf[256];
-
-	if (tb[attr])
-		print_addr64(rta_getattr_u64(tb[attr]),
-			     abuf, sizeof(abuf));
-	else
-		snprintf(abuf, sizeof(abuf), "-");
-
-	/* 20 = sizeof("xxxx:xxxx:xxxx:xxxx") */
-	print_string(PRINT_ANY, tag, "%-20s", abuf);
-}
-
-static int print_ila_mapping(struct nlmsghdr *n, void *arg)
-{
-	struct genlmsghdr *ghdr;
-	struct rtattr *tb[ILA_ATTR_MAX + 1];
-	int len = n->nlmsg_len;
-
-	if (n->nlmsg_type != genl_family)
-		return 0;
-
-	len -= NLMSG_LENGTH(GENL_HDRLEN);
-	if (len < 0)
-		return -1;
-
-	ghdr = NLMSG_DATA(n);
-	parse_rtattr(tb, ILA_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len);
-
-	open_json_object(NULL);
-	print_ila_locid("locator_match", ILA_ATTR_LOCATOR_MATCH, tb);
-	print_ila_locid("locator", ILA_ATTR_LOCATOR, tb);
-
-	if (tb[ILA_ATTR_IFINDEX]) {
-		__u32 ifindex
-			= rta_getattr_u32(tb[ILA_ATTR_IFINDEX]);
-
-		print_color_string(PRINT_ANY, COLOR_IFNAME,
-				   "interface", "%-16s",
-				   ll_index_to_name(ifindex));
-	} else {
-		print_string(PRINT_FP, NULL, "%-10s ", "-");
-	}
-
-	if (tb[ILA_ATTR_CSUM_MODE]) {
-		__u8 csum = rta_getattr_u8(tb[ILA_ATTR_CSUM_MODE]);
-
-		print_string(PRINT_ANY, "csum_mode", "%s",
-			     ila_csum_mode2name(csum));
-	} else
-		print_string(PRINT_FP, NULL, "%-10s ", "-");
-
-	if (tb[ILA_ATTR_IDENT_TYPE])
-		print_string(PRINT_ANY, "ident_type", "%s",
-			ila_ident_type2name(rta_getattr_u8(
-						tb[ILA_ATTR_IDENT_TYPE])));
-	else
-		print_string(PRINT_FP, NULL, "%s", "-");
-
-	print_nl();
-	close_json_object();
-
-	return 0;
-}
-
-#define NLMSG_BUF_SIZE 4096
-
-static int do_list(int argc, char **argv)
-{
-	ILA_REQUEST(req, 1024, ILA_CMD_GET, NLM_F_REQUEST | NLM_F_DUMP);
-
-	if (argc > 0) {
-		fprintf(stderr, "\"ip ila show\" does not take "
-			"any arguments.\n");
-		return -1;
-	}
-
-	if (rtnl_send(&genl_rth, (void *)&req, req.n.nlmsg_len) < 0) {
-		perror("Cannot send dump request");
-		exit(1);
-	}
-
-	new_json_obj(json);
-	if (rtnl_dump_filter(&genl_rth, print_ila_mapping, stdout) < 0) {
-		fprintf(stderr, "Dump terminated\n");
-		return 1;
-	}
-	delete_json_obj();
-	fflush(stdout);
-
-	return 0;
-}
-
-static int ila_parse_opt(int argc, char **argv, struct nlmsghdr *n,
-			 bool adding)
-{
-	__u64 locator = 0;
-	__u64 locator_match = 0;
-	int ifindex = 0;
-	int csum_mode = 0;
-	int ident_type = 0;
-	bool loc_set = false;
-	bool loc_match_set = false;
-	bool ifindex_set = false;
-	bool csum_mode_set = false;
-	bool ident_type_set = false;
-
-	while (argc > 0) {
-		if (!matches(*argv, "loc")) {
-			NEXT_ARG();
-
-			if (get_addr64(&locator, *argv) < 0) {
-				fprintf(stderr, "Bad locator: %s\n", *argv);
-				return -1;
-			}
-			loc_set = true;
-		} else if (!matches(*argv, "loc_match")) {
-			NEXT_ARG();
-
-			if (get_addr64(&locator_match, *argv) < 0) {
-				fprintf(stderr, "Bad locator to match: %s\n",
-					*argv);
-				return -1;
-			}
-			loc_match_set = true;
-		} else if (!matches(*argv, "csum-mode")) {
-			NEXT_ARG();
-
-			csum_mode = ila_csum_name2mode(*argv);
-			if (csum_mode < 0) {
-				fprintf(stderr, "Bad csum-mode: %s\n",
-					*argv);
-				return -1;
-			}
-			csum_mode_set = true;
-		} else if (!matches(*argv, "ident-type")) {
-			NEXT_ARG();
-
-			ident_type = ila_ident_name2type(*argv);
-			if (ident_type < 0) {
-				fprintf(stderr, "Bad ident-type: %s\n",
-					*argv);
-				return -1;
-			}
-			ident_type_set = true;
-		} else if (!matches(*argv, "dev")) {
-			NEXT_ARG();
-
-			ifindex = ll_name_to_index(*argv);
-			if (ifindex == 0) {
-				fprintf(stderr, "No such interface: %s\n",
-					*argv);
-				return -1;
-			}
-			ifindex_set = true;
-		} else {
-			usage();
-			return -1;
-		}
-		argc--, argv++;
-	}
-
-	if (adding) {
-		if (!loc_set) {
-			fprintf(stderr, "ila: missing locator\n");
-			return -1;
-		}
-		if (!loc_match_set) {
-			fprintf(stderr, "ila: missing locator0match\n");
-			return -1;
-		}
-	}
-
-	if (loc_match_set)
-		addattr64(n, 1024, ILA_ATTR_LOCATOR_MATCH, locator_match);
-
-	if (loc_set)
-		addattr64(n, 1024, ILA_ATTR_LOCATOR, locator);
-
-	if (ifindex_set)
-		addattr32(n, 1024, ILA_ATTR_IFINDEX, ifindex);
-
-	if (csum_mode_set)
-		addattr8(n, 1024, ILA_ATTR_CSUM_MODE, csum_mode);
-
-	if (ident_type_set)
-		addattr8(n, 1024, ILA_ATTR_IDENT_TYPE, ident_type);
-
-	return 0;
-}
-
-static int do_add(int argc, char **argv)
-{
-	ILA_REQUEST(req, 1024, ILA_CMD_ADD, NLM_F_REQUEST);
-
-	ila_parse_opt(argc, argv, &req.n, true);
-
-	if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
-		return -2;
-
-	return 0;
-}
-
-static int do_del(int argc, char **argv)
-{
-	ILA_REQUEST(req, 1024, ILA_CMD_DEL, NLM_F_REQUEST);
-
-	ila_parse_opt(argc, argv, &req.n, false);
-
-	if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
-		return -2;
-
-	return 0;
-}
-
-int do_ipila(int argc, char **argv)
-{
-	if (argc < 1)
-		usage();
-
-	if (matches(*argv, "help") == 0)
-		usage();
-
-	if (genl_init_handle(&genl_rth, ILA_GENL_NAME, &genl_family))
-		exit(1);
-
-	if (matches(*argv, "add") == 0)
-		return do_add(argc-1, argv+1);
-	if (matches(*argv, "delete") == 0)
-		return do_del(argc-1, argv+1);
-	if (matches(*argv, "list") == 0)
-		return do_list(argc-1, argv+1);
-
-	fprintf(stderr, "Command \"%s\" is unknown, try \"ip ila help\".\n",
-		*argv);
-	exit(-1);
-}
diff --git a/ip/ipl2tp.c b/ip/ipl2tp.c
index 77bc324..f050880 100644
--- a/ip/ipl2tp.c
+++ b/ip/ipl2tp.c
@@ -42,6 +42,8 @@
 	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;
@@ -53,13 +55,14 @@
 	inet_prefix peer_ip;
 
 	uint16_t pw_type;
-	unsigned int udp6_csum_tx:1;
-	unsigned int udp6_csum_rx:1;
-	unsigned int udp_csum:1;
-	unsigned int recv_seq:1;
-	unsigned int send_seq:1;
-	unsigned int tunnel:1;
-	unsigned int session:1;
+	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;
 	uint8_t l2spec_type;
@@ -105,26 +108,18 @@
 
 	if (p->local_ip.family == AF_INET6)
 		local_attr = L2TP_ATTR_IP6_SADDR;
-	addattr_l(&req.n, 1024, local_attr, &p->local_ip.data,
-		  p->local_ip.bytelen);
+	addattr_l(&req.n, 1024, local_attr, &p->local_ip.data, p->local_ip.bytelen);
 
 	if (p->peer_ip.family == AF_INET6)
 		peer_attr = L2TP_ATTR_IP6_DADDR;
-	addattr_l(&req.n, 1024, peer_attr, &p->peer_ip.data,
-		  p->peer_ip.bytelen);
+	addattr_l(&req.n, 1024, peer_attr, &p->peer_ip.data, p->peer_ip.bytelen);
 
 	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 (p->udp_csum)
-			addattr8(&req.n, 1024, L2TP_ATTR_UDP_CSUM, 1);
-		if (!p->udp6_csum_tx)
-			addattr(&req.n, 1024, L2TP_ATTR_UDP_ZERO_CSUM6_TX);
-		if (!p->udp6_csum_rx)
-			addattr(&req.n, 1024, L2TP_ATTR_UDP_ZERO_CSUM6_RX);
 	}
 
-	if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0)
 		return -2;
 
 	return 0;
@@ -137,7 +132,7 @@
 
 	addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->tunnel_id);
 
-	if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0)
 		return -2;
 
 	return 0;
@@ -156,23 +151,22 @@
 	addattr8(&req.n, 1024, L2TP_ATTR_L2SPEC_TYPE, p->l2spec_type);
 	addattr8(&req.n, 1024, L2TP_ATTR_L2SPEC_LEN, p->l2spec_len);
 
-	if (p->recv_seq)
-		addattr8(&req.n, 1024, L2TP_ATTR_RECV_SEQ, 1);
-	if (p->send_seq)
-		addattr8(&req.n, 1024, L2TP_ATTR_SEND_SEQ, 1);
-	if (p->reorder_timeout)
-		addattr64(&req.n, 1024, L2TP_ATTR_RECV_TIMEOUT,
+	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->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)
+	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, NULL) < 0)
+	if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0)
 		return -2;
 
 	return 0;
@@ -185,28 +179,21 @@
 
 	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, NULL) < 0)
+	if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0)
 		return -2;
 
 	return 0;
 }
 
-static void print_cookie(const char *name, const char *fmt,
-			 const uint8_t *cookie, int len)
+static void print_cookie(char *name, const uint8_t *cookie, int len)
 {
-	char abuf[32];
-	size_t n;
-
-	n = snprintf(abuf, sizeof(abuf),
-		     "%02x%02x%02x%02x",
-		     cookie[0], cookie[1], cookie[2], cookie[3]);
+	printf("  %s %02x%02x%02x%02x", name,
+	       cookie[0], cookie[1],
+	       cookie[2], cookie[3]);
 	if (len == 8)
-		snprintf(abuf + n, sizeof(abuf) - n,
-			 "%02x%02x%02x%02x",
-			 cookie[4], cookie[5],
-			 cookie[6], cookie[7]);
-
-	print_string(PRINT_ANY, name, fmt, abuf);
+		printf("%02x%02x%02x%02x",
+		       cookie[4], cookie[5],
+		       cookie[6], cookie[7]);
 }
 
 static void print_tunnel(const struct l2tp_data *data)
@@ -214,117 +201,43 @@
 	const struct l2tp_parm *p = &data->config;
 	char buf[INET6_ADDRSTRLEN];
 
-	open_json_object(NULL);
-	print_uint(PRINT_ANY, "tunnel_id", "Tunnel %u,", p->tunnel_id);
-	print_string(PRINT_ANY, "encap", " encap %s",
-		     p->encap == L2TP_ENCAPTYPE_UDP ? "UDP" :
-		     p->encap == L2TP_ENCAPTYPE_IP ? "IP" : "??");
-	print_nl();
+	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_ntop(p->local_ip.family, p->local_ip.data, buf, sizeof(buf)));
+	printf("to %s\n", inet_ntop(p->peer_ip.family, p->peer_ip.data, buf, sizeof(buf)));
+	printf("  Peer tunnel %u\n",
+	       p->peer_tunnel_id);
 
-	print_string(PRINT_ANY, "local", "  From %s ",
-		     inet_ntop(p->local_ip.family, p->local_ip.data,
-			       buf, sizeof(buf)));
-	print_string(PRINT_ANY, "peer", "to %s",
-		     inet_ntop(p->peer_ip.family, p->peer_ip.data,
-			       buf, sizeof(buf)));
-	print_nl();
-
-	print_uint(PRINT_ANY, "peer_tunnel", "  Peer tunnel %u",
-		   p->peer_tunnel_id);
-	print_nl();
-
-	if (p->encap == L2TP_ENCAPTYPE_UDP) {
-		print_string(PRINT_FP, NULL,
-			     "  UDP source / dest ports:", NULL);
-
-		print_hu(PRINT_ANY, "local_port", " %hu",
-			   p->local_udp_port);
-		print_hu(PRINT_ANY, "peer_port", "/%hu",
-			   p->peer_udp_port);
-		print_nl();
-
-		switch (p->local_ip.family) {
-		case AF_INET:
-			print_bool(PRINT_JSON, "checksum",
-				   NULL, p->udp_csum);
-			print_string(PRINT_FP, NULL,
-				     "  UDP checksum: %s\n",
-				     p->udp_csum ? "enabled" : "disabled");
-			break;
-		case AF_INET6:
-			if (is_json_context()) {
-				print_bool(PRINT_JSON, "checksum_tx",
-					   NULL, p->udp6_csum_tx);
-
-				print_bool(PRINT_JSON, "checksum_rx",
-					   NULL, p->udp6_csum_tx);
-			} else {
-				printf("  UDP checksum: %s%s%s%s\n",
-				       p->udp6_csum_tx && p->udp6_csum_rx
-				       ? "enabled" : "",
-				       p->udp6_csum_tx && !p->udp6_csum_rx
-				       ? "tx" : "",
-				       !p->udp6_csum_tx && p->udp6_csum_rx
-				       ? "rx" : "",
-				       !p->udp6_csum_tx && !p->udp6_csum_rx
-				       ? "disabled" : "");
-			}
-			break;
-		}
-	}
-	close_json_object();
+	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;
 
-	open_json_object(NULL);
-
-	print_uint(PRINT_ANY, "session_id", "Session %u", p->session_id);
-	print_uint(PRINT_ANY, "tunnel_id",  " in tunnel %u", p->tunnel_id);
-	print_nl();
-
-	print_uint(PRINT_ANY, "peer_session_id",
-		     "  Peer session %u,", p->peer_session_id);
-	print_uint(PRINT_ANY, "peer_tunnel_id",
-		     " tunnel %u",  p->peer_tunnel_id);
-	print_nl();
+	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) {
-		print_color_string(PRINT_ANY, COLOR_IFNAME,
-				   "interface", "  interface name: %s" , p->ifname);
-		print_nl();
+		printf("  interface name: %s\n", p->ifname);
 	}
-
-	/* Show offsets only for plain console output (for legacy scripts) */
-	print_uint(PRINT_FP, "offset", "  offset %u,", 0);
-	print_uint(PRINT_FP, "peer_offset", " peer offset %u\n", 0);
-
+	printf("  offset %u, peer offset %u\n",
+	       p->offset, p->peer_offset);
 	if (p->cookie_len > 0)
-		print_cookie("cookie", "  cookie %s",
-			     p->cookie, p->cookie_len);
-
+		print_cookie("cookie", p->cookie, p->cookie_len);
 	if (p->peer_cookie_len > 0)
-		print_cookie("peer_cookie", "  peer cookie %s",
-			     p->peer_cookie, p->peer_cookie_len);
+		print_cookie("peer cookie", p->peer_cookie, p->peer_cookie_len);
 
 	if (p->reorder_timeout != 0)
-		print_uint(PRINT_ANY, "reorder_timeout",
-			   "  reorder timeout: %u", p->reorder_timeout);
-
-
-	if (p->send_seq || p->recv_seq) {
-		print_string(PRINT_FP, NULL, "%s  sequence numbering:", _SL_);
-
-		if (p->send_seq)
-			print_null(PRINT_ANY, "send_seq", " send", NULL);
-		if (p->recv_seq)
-			print_null(PRINT_ANY, "recv_seq", " recv", NULL);
-
-	}
-	print_string(PRINT_FP, NULL, "\n", NULL);
-	close_json_object();
+		printf("  reorder timeout: %u\n", p->reorder_timeout);
+	else
+		printf("\n");
 }
 
 static int get_response(struct nlmsghdr *n, void *arg)
@@ -333,7 +246,7 @@
 	struct l2tp_data *data = arg;
 	struct l2tp_parm *p = &data->config;
 	struct rtattr *attrs[L2TP_ATTR_MAX + 1];
-	struct rtattr *nla_stats, *rta;
+	struct rtattr *nla_stats;
 	int len;
 
 	/* Validate message and parse attributes */
@@ -351,6 +264,10 @@
 		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])
@@ -364,12 +281,7 @@
 	if (attrs[L2TP_ATTR_L2SPEC_LEN])
 		p->l2spec_len = rta_getattr_u8(attrs[L2TP_ATTR_L2SPEC_LEN]);
 
-	if (attrs[L2TP_ATTR_UDP_CSUM])
-		p->udp_csum = !!rta_getattr_u8(attrs[L2TP_ATTR_UDP_CSUM]);
-
-	p->udp6_csum_tx = !attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX];
-	p->udp6_csum_rx = !attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX];
-
+	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]));
@@ -378,36 +290,41 @@
 		memcpy(p->peer_cookie, RTA_DATA(attrs[L2TP_ATTR_PEER_COOKIE]),
 		       p->peer_cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_PEER_COOKIE]));
 
-	if (attrs[L2TP_ATTR_RECV_SEQ])
-		p->recv_seq = !!rta_getattr_u8(attrs[L2TP_ATTR_RECV_SEQ]);
-	if (attrs[L2TP_ATTR_SEND_SEQ])
-		p->send_seq = !!rta_getattr_u8(attrs[L2TP_ATTR_SEND_SEQ]);
+	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]);
-
-	rta = attrs[L2TP_ATTR_IP_SADDR];
-	p->local_ip.family = AF_INET;
-	if (!rta) {
-		rta = attrs[L2TP_ATTR_IP6_SADDR];
+	if (attrs[L2TP_ATTR_IP_SADDR]) {
+		p->local_ip.family = AF_INET;
+		p->local_ip.data[0] = rta_getattr_u32(attrs[L2TP_ATTR_IP_SADDR]);
+		p->local_ip.bytelen = 4;
+		p->local_ip.bitlen = -1;
+	}
+	if (attrs[L2TP_ATTR_IP_DADDR]) {
+		p->peer_ip.family = AF_INET;
+		p->peer_ip.data[0] = rta_getattr_u32(attrs[L2TP_ATTR_IP_DADDR]);
+		p->peer_ip.bytelen = 4;
+		p->peer_ip.bitlen = -1;
+	}
+	if (attrs[L2TP_ATTR_IP6_SADDR]) {
 		p->local_ip.family = AF_INET6;
+		memcpy(&p->local_ip.data, RTA_DATA(attrs[L2TP_ATTR_IP6_SADDR]),
+			p->local_ip.bytelen = 16);
+		p->local_ip.bitlen = -1;
 	}
-	if (rta && get_addr_rta(&p->local_ip, rta, p->local_ip.family))
-		return -1;
-
-	rta = attrs[L2TP_ATTR_IP_DADDR];
-	p->peer_ip.family = AF_INET;
-	if (!rta) {
-		rta = attrs[L2TP_ATTR_IP6_DADDR];
+	if (attrs[L2TP_ATTR_IP6_DADDR]) {
 		p->peer_ip.family = AF_INET6;
+		memcpy(&p->peer_ip.data, RTA_DATA(attrs[L2TP_ATTR_IP6_DADDR]),
+			p->peer_ip.bytelen = 16);
+		p->peer_ip.bitlen = -1;
 	}
-	if (rta && get_addr_rta(&p->peer_ip, rta, p->peer_ip.family))
-		return -1;
-
 	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]);
 
@@ -438,7 +355,7 @@
 	return 0;
 }
 
-static int session_nlmsg(struct nlmsghdr *n, void *arg)
+static int session_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
 	int ret = get_response(n, arg);
 
@@ -458,25 +375,21 @@
 
 	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);
+		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;
 
-	new_json_obj(json);
 	if (rtnl_dump_filter(&genl_rth, session_nlmsg, p) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
-	delete_json_obj();
-	fflush(stdout);
 
 	return 0;
 }
 
-static int tunnel_nlmsg(struct nlmsghdr *n, void *arg)
+static int tunnel_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
 	int ret = get_response(n, arg);
 
@@ -500,13 +413,10 @@
 	if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0)
 		return -2;
 
-	new_json_obj(json);
 	if (rtnl_dump_filter(&genl_rth, tunnel_nlmsg, p) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
-	delete_json_obj();
-	fflush(stdout);
 
 	return 0;
 }
@@ -515,36 +425,68 @@
  * 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"
-		"          remote ADDR local ADDR\n"
-		"          tunnel_id ID peer_tunnel_id ID\n"
-		"          [ encap { ip | udp } ]\n"
-		"          [ udp_sport PORT ] [ udp_dport PORT ]\n"
-		"          [ udp_csum { on | off } ]\n"
-		"          [ udp6_csum_tx { on | off } ]\n"
-		"          [ udp6_csum_rx { on | off } ]\n"
-		"Usage: ip l2tp add session [ name NAME ]\n"
-		"          tunnel_id ID\n"
-		"          session_id ID peer_session_id ID\n"
-		"          [ cookie HEXSTR ] [ peer_cookie HEXSTR ]\n"
-		"          [ seq { none | send | recv | both } ]\n"
-		"          [ l2spec_type L2SPEC ]\n"
-		"       ip l2tp del tunnel tunnel_id ID\n"
-		"       ip l2tp del session tunnel_id ID session_id ID\n"
-		"       ip l2tp show tunnel [ tunnel_id ID ]\n"
-		"       ip l2tp show session [ tunnel_id ID ] [ session_id ID ]\n"
-		"\n"
-		"Where: NAME   := STRING\n"
-		"       ADDR   := { IP_ADDRESS | any }\n"
-		"       PORT   := { 0..65535 }\n"
-		"       ID     := { 1..4294967295 }\n"
-		"       HEXSTR := { 8 or 16 hex digits (4 / 8 bytes) }\n"
-		"       L2SPEC := { none | default }\n");
-
+	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, "          [ l2spec_type L2SPEC ]\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");
+	fprintf(stderr, "       L2SPEC := { none | default }\n");
 	exit(-1);
 }
 
@@ -558,8 +500,6 @@
 	/* Defaults */
 	p->l2spec_type = L2TP_L2SPECTYPE_DEFAULT;
 	p->l2spec_len = 4;
-	p->udp6_csum_rx = 1;
-	p->udp6_csum_tx = 1;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "encap") == 0) {
@@ -574,8 +514,6 @@
 			}
 		} else if (strcmp(*argv, "name") == 0) {
 			NEXT_ARG();
-			if (check_ifname(*argv))
-				invarg("\"name\" not a valid ifname", *argv);
 			p->ifname = *argv;
 		} else if (strcmp(*argv, "remote") == 0) {
 			NEXT_ARG();
@@ -588,7 +526,6 @@
 		} 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);
@@ -596,7 +533,6 @@
 		} 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);
@@ -604,7 +540,6 @@
 		} 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);
@@ -612,60 +547,36 @@
 		} 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, "udp_csum") == 0) {
-			NEXT_ARG();
-			if (strcmp(*argv, "on") == 0)
-				p->udp_csum = 1;
-			else if (strcmp(*argv, "off") == 0)
-				p->udp_csum = 0;
-			else
-				invarg("invalid option for udp_csum\n", *argv);
-		} else if (strcmp(*argv, "udp6_csum_rx") == 0) {
-			NEXT_ARG();
-			if (strcmp(*argv, "on") == 0)
-				p->udp6_csum_rx = 1;
-			else if (strcmp(*argv, "off") == 0)
-				p->udp6_csum_rx = 0;
-			else
-				invarg("invalid option for udp6_csum_rx\n"
-						, *argv);
-		} else if (strcmp(*argv, "udp6_csum_tx") == 0) {
-			NEXT_ARG();
-			if (strcmp(*argv, "on") == 0)
-				p->udp6_csum_tx = 1;
-			else if (strcmp(*argv, "off") == 0)
-				p->udp6_csum_tx = 0;
-			else
-				invarg("invalid option for udp6_csum_tx\n"
-						, *argv);
 		} else if (strcmp(*argv, "offset") == 0) {
-			fprintf(stderr, "Ignoring option \"offset\"\n");
+			__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) {
-			fprintf(stderr, "Ignoring option \"peer_offset\"\n");
+			__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))
@@ -676,7 +587,6 @@
 				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))
@@ -694,26 +604,7 @@
 				p->l2spec_type = L2TP_L2SPECTYPE_NONE;
 				p->l2spec_len = 0;
 			} else {
-				fprintf(stderr,
-					"Unknown layer2specific header type \"%s\"\n",
-					*argv);
-				exit(-1);
-			}
-		} else if (strcmp(*argv, "seq") == 0) {
-			NEXT_ARG();
-			if (strcasecmp(*argv, "both") == 0) {
-				p->recv_seq = 1;
-				p->send_seq = 1;
-			} else if (strcasecmp(*argv, "recv") == 0) {
-				p->recv_seq = 1;
-			} else if (strcasecmp(*argv, "send") == 0) {
-				p->send_seq = 1;
-			} else if (strcasecmp(*argv, "none") == 0) {
-				p->recv_seq = 0;
-				p->send_seq = 0;
-			} else {
-				fprintf(stderr,
-					"Unknown seq value \"%s\"\n", *argv);
+				fprintf(stderr, "Unknown layer2specific header type \"%s\"\n", *argv);
 				exit(-1);
 			}
 		} else if (strcmp(*argv, "tunnel") == 0) {
@@ -829,11 +720,19 @@
 
 int do_ipl2tp(int argc, char **argv)
 {
-	if (argc < 1 || !matches(*argv, "help"))
-		usage();
+	if (genl_family < 0) {
+		if (rtnl_open_byproto(&genl_rth, 0, NETLINK_GENERIC) < 0) {
+			fprintf(stderr, "Cannot open generic netlink socket\n");
+			exit(1);
+		}
 
-	if (genl_init_handle(&genl_rth, L2TP_GENL_NAME, &genl_family))
-		exit(1);
+		genl_family = genl_resolve_family(&genl_rth, 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);
@@ -843,8 +742,9 @@
 	    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);
+	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 212a088..5ab9d61 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <dlfcn.h>
 #include <errno.h>
@@ -25,8 +26,8 @@
 #include <arpa/inet.h>
 #include <string.h>
 #include <sys/ioctl.h>
+#include <linux/sockios.h>
 #include <stdbool.h>
-#include <linux/mpls.h>
 
 #include "rt_names.h"
 #include "utils.h"
@@ -38,92 +39,62 @@
 #define LIBDIR "/usr/lib"
 #endif
 
-#ifndef GSO_MAX_SIZE
-#define GSO_MAX_SIZE		65536
-#endif
-#ifndef GSO_MAX_SEGS
-#define GSO_MAX_SEGS		65535
-#endif
-
-
 static void usage(void) __attribute__((noreturn));
 static int iplink_have_newlink(void);
 
 void iplink_usage(void)
 {
 	if (iplink_have_newlink()) {
-		fprintf(stderr,
-			"Usage: ip link add [link DEV] [ name ] NAME\n"
-			"		    [ txqueuelen PACKETS ]\n"
-			"		    [ address LLADDR ]\n"
-			"		    [ broadcast LLADDR ]\n"
-			"		    [ mtu MTU ] [index IDX ]\n"
-			"		    [ numtxqueues QUEUE_COUNT ]\n"
-			"		    [ numrxqueues QUEUE_COUNT ]\n"
-			"		    type TYPE [ ARGS ]\n"
-			"\n"
-			"	ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]\n"
-			"\n"
-			"	ip link set { DEVICE | dev DEVICE | group DEVGROUP }\n"
-			"			[ { up | down } ]\n"
-			"			[ type TYPE ARGS ]\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");
+		fprintf(stderr, "                   [ mtu MTU ] [index IDX ]\n");
+		fprintf(stderr, "                   [ numtxqueues QUEUE_COUNT ]\n");
+		fprintf(stderr, "                   [ numrxqueues QUEUE_COUNT ]\n");
+		fprintf(stderr, "                   type TYPE [ ARGS ]\n");
+		fprintf(stderr, "       ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]\n");
+		fprintf(stderr, "\n");
+		fprintf(stderr, "       ip link set { DEVICE | dev DEVICE | group DEVGROUP } [ { up | down } ]\n");
 	} else
-		fprintf(stderr,
-			"Usage: ip link set DEVICE [ { up | down } ]\n");
+		fprintf(stderr, "Usage: ip link set DEVICE [ { up | down } ]\n");
 
-	fprintf(stderr,
-		"		[ arp { on | off } ]\n"
-		"		[ dynamic { on | off } ]\n"
-		"		[ multicast { on | off } ]\n"
-		"		[ allmulticast { on | off } ]\n"
-		"		[ promisc { on | off } ]\n"
-		"		[ trailers { on | off } ]\n"
-		"		[ carrier { on | off } ]\n"
-		"		[ txqueuelen PACKETS ]\n"
-		"		[ name NEWNAME ]\n"
-		"		[ address LLADDR ]\n"
-		"		[ broadcast LLADDR ]\n"
-		"		[ mtu MTU ]\n"
-		"		[ netns { PID | NAME } ]\n"
-		"		[ link-netns NAME | link-netnsid ID ]\n"
-		"			[ alias NAME ]\n"
-		"			[ vf NUM [ mac LLADDR ]\n"
-		"				 [ vlan VLANID [ qos VLAN-QOS ] [ proto VLAN-PROTO ] ]\n"
-		"				 [ rate TXRATE ]\n"
-		"				 [ max_tx_rate TXRATE ]\n"
-		"				 [ min_tx_rate TXRATE ]\n"
-		"				 [ spoofchk { on | off} ]\n"
-		"				 [ query_rss { on | off} ]\n"
-		"				 [ state { auto | enable | disable} ] ]\n"
-		"				 [ trust { on | off} ] ]\n"
-		"				 [ node_guid { eui64 } ]\n"
-		"				 [ port_guid { eui64 } ]\n"
-		"			[ { xdp | xdpgeneric | xdpdrv | xdpoffload } { off |\n"
-		"				  object FILE [ section NAME ] [ verbose ] |\n"
-		"				  pinned FILE } ]\n"
-		"			[ master DEVICE ][ vrf NAME ]\n"
-		"			[ nomaster ]\n"
-		"			[ addrgenmode { eui64 | none | stable_secret | random } ]\n"
-		"			[ protodown { on | off } ]\n"
-		"			[ gso_max_size BYTES ] | [ gso_max_segs PACKETS ]\n"
-		"\n"
-		"	ip link show [ DEVICE | group GROUP ] [up] [master DEV] [vrf NAME] [type TYPE]\n"
-		"\n"
-		"	ip link xstats type TYPE [ ARGS ]\n"
-		"\n"
-		"	ip link afstats [ dev DEVICE ]\n");
+	fprintf(stderr, "	                  [ arp { on | off } ]\n");
+	fprintf(stderr, "	                  [ dynamic { on | off } ]\n");
+	fprintf(stderr, "	                  [ multicast { on | off } ]\n");
+	fprintf(stderr, "	                  [ allmulticast { on | off } ]\n");
+	fprintf(stderr, "	                  [ promisc { on | off } ]\n");
+	fprintf(stderr, "	                  [ trailers { on | off } ]\n");
+	fprintf(stderr, "	                  [ txqueuelen PACKETS ]\n");
+	fprintf(stderr, "	                  [ name NEWNAME ]\n");
+	fprintf(stderr, "	                  [ address LLADDR ]\n");
+	fprintf(stderr, "	                  [ broadcast LLADDR ]\n");
+	fprintf(stderr, "	                  [ mtu MTU ]\n");
+	fprintf(stderr, "	                  [ netns PID ]\n");
+	fprintf(stderr, "	                  [ netns NAME ]\n");
+	fprintf(stderr, "	                  [ link-netnsid ID ]\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, "				   [ spoofchk { on | off} ] ]\n");
+	fprintf(stderr, "				   [ query_rss { on | off} ] ]\n");
+	fprintf(stderr, "				   [ state { auto | enable | disable} ] ]\n");
+	fprintf(stderr, "			  [ master DEVICE ]\n");
+	fprintf(stderr, "			  [ nomaster ]\n");
+	fprintf(stderr, "			  [ addrgenmode { eui64 | none | stable_secret | random } ]\n");
+	fprintf(stderr, "	                  [ protodown { on | off } ]\n");
+	fprintf(stderr, "       ip link show [ DEVICE | group GROUP ] [up] [master DEV] [type TYPE]\n");
 
 	if (iplink_have_newlink()) {
-		fprintf(stderr,
-			"\n"
-			"	ip link help [ TYPE ]\n"
-			"\n"
-			"TYPE := { vlan | veth | vcan | vxcan | dummy | ifb | macvlan | macvtap |\n"
-			"	   bridge | bond | team | ipoib | ip6tnl | ipip | sit | vxlan |\n"
-			"	   gre | gretap | erspan | ip6gre | ip6gretap | ip6erspan |\n"
-			"	   vti | nlmon | team_slave | bond_slave | bridge_slave |\n"
-			"	   ipvlan | ipvtap | geneve | vrf | macsec | netdevsim | rmnet |\n"
-			"	   xfrm }\n");
+		fprintf(stderr, "       ip link help [ TYPE ]\n");
+		fprintf(stderr, "\n");
+		fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n");
+		fprintf(stderr, "          bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |\n");
+		fprintf(stderr, "          gre | gretap | ip6gre | ip6gretap | vti | nlmon |\n");
+		fprintf(stderr, "          bond_slave | ipvlan | geneve | bridge_slave | vrf }\n");
 	}
 	exit(-1);
 }
@@ -144,14 +115,15 @@
 static void *BODY;		/* cached dlopen(NULL) handle */
 static struct link_util *linkutil_list;
 
-struct link_util *get_link_kind(const char *id)
+static struct link_util *__get_link_kind(const char *id, bool slave)
 {
 	void *dlh;
 	char buf[256];
 	struct link_util *l;
 
 	for (l = linkutil_list; l; l = l->next)
-		if (strcmp(l->id, id) == 0)
+		if (strcmp(l->id, id) == 0 &&
+		    l->slave == slave)
 			return l;
 
 	snprintf(buf, sizeof(buf), LIBDIR "/ip/link_%s.so", id);
@@ -166,7 +138,10 @@
 		}
 	}
 
-	snprintf(buf, sizeof(buf), "%s_link_util", id);
+	if (slave)
+		snprintf(buf, sizeof(buf), "%s_slave_link_util", id);
+	else
+		snprintf(buf, sizeof(buf), "%s_link_util", id);
 	l = dlsym(dlh, buf);
 	if (l == NULL)
 		return NULL;
@@ -176,6 +151,16 @@
 	return l;
 }
 
+struct link_util *get_link_kind(const char *id)
+{
+	return __get_link_kind(id, false);
+}
+
+struct link_util *get_link_slave_kind(const char *id)
+{
+	return __get_link_kind(id, true);
+}
+
 static int get_link_mode(const char *mode)
 {
 	if (strcasecmp(mode, "default") == 0)
@@ -201,7 +186,8 @@
 #if IPLINK_IOCTL_COMPAT
 static int have_rtnl_newlink = -1;
 
-static int accept_msg(struct rtnl_ctrl_data *ctrl,
+static int accept_msg(const struct sockaddr_nl *who,
+		      struct rtnl_ctrl_data *ctrl,
 		      struct nlmsghdr *n, void *arg)
 {
 	struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n);
@@ -220,14 +206,16 @@
 		struct nlmsghdr		n;
 		struct ifinfomsg	i;
 		char			buf[1024];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
-		.n.nlmsg_type = RTM_NEWLINK,
-		.i.ifi_family = AF_UNSPEC,
-	};
+	} req;
 
 	if (have_rtnl_newlink < 0) {
+		memset(&req, 0, sizeof(req));
+
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+		req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
+		req.n.nlmsg_type = RTM_NEWLINK;
+		req.i.ifi_family = AF_UNSPEC;
+
 		if (rtnl_send(&rth, &req.n, req.n.nlmsg_len) < 0) {
 			perror("request send failed");
 			exit(1);
@@ -243,106 +231,14 @@
 }
 #endif /* ! IPLINK_IOCTL_COMPAT */
 
-static int nl_get_ll_addr_len(const char *ifname)
-{
-	int len;
-	int dev_index = ll_name_to_index(ifname);
-	struct iplink_req req = {
-		.n = {
-			.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-			.nlmsg_type = RTM_GETLINK,
-			.nlmsg_flags = NLM_F_REQUEST
-		},
-		.i = {
-			.ifi_family = preferred_family,
-			.ifi_index = dev_index,
-		}
-	};
-	struct nlmsghdr *answer;
-	struct rtattr *tb[IFLA_MAX+1];
-
-	if (dev_index == 0)
-		return -1;
-
-	if (rtnl_talk(&rth, &req.n, &answer) < 0)
-		return -1;
-
-	len = answer->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg));
-	if (len < 0) {
-		free(answer);
-		return -1;
-	}
-
-	parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)),
-			   len, NLA_F_NESTED);
-	if (!tb[IFLA_ADDRESS]) {
-		free(answer);
-		return -1;
-	}
-
-	len = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
-	free(answer);
-	return len;
-}
-
-static void iplink_parse_vf_vlan_info(int vf, int *argcp, char ***argvp,
-				      struct ifla_vf_vlan_info *ivvip)
-{
-	int argc = *argcp;
-	char **argv = *argvp;
-	unsigned int vci;
-
-	NEXT_ARG();
-	if (get_unsigned(&vci, *argv, 0) || vci > 4095)
-		invarg("Invalid \"vlan\" value\n", *argv);
-
-	ivvip->vlan = vci;
-	ivvip->vf = vf;
-	ivvip->qos = 0;
-	ivvip->vlan_proto = htons(ETH_P_8021Q);
-	if (NEXT_ARG_OK()) {
-		NEXT_ARG();
-		if (matches(*argv, "qos") == 0) {
-			NEXT_ARG();
-			if (get_unsigned(&ivvip->qos, *argv, 0))
-				invarg("Invalid \"qos\" value\n", *argv);
-		} else {
-			/* rewind arg */
-			PREV_ARG();
-		}
-	}
-	if (NEXT_ARG_OK()) {
-		NEXT_ARG();
-		if (matches(*argv, "proto") == 0) {
-			NEXT_ARG();
-			if (ll_proto_a2n(&ivvip->vlan_proto, *argv))
-				invarg("protocol is invalid\n", *argv);
-			if (ivvip->vlan_proto != htons(ETH_P_8021AD) &&
-			    ivvip->vlan_proto != htons(ETH_P_8021Q)) {
-				SPRINT_BUF(b1);
-				SPRINT_BUF(b2);
-				char msg[64 + sizeof(b1) + sizeof(b2)];
-
-				sprintf(msg,
-					"Invalid \"vlan protocol\" value - supported %s, %s\n",
-					ll_proto_n2a(htons(ETH_P_8021Q),
-					     b1, sizeof(b1)),
-					ll_proto_n2a(htons(ETH_P_8021AD),
-					     b2, sizeof(b2)));
-				invarg(msg, *argv);
-			}
-		} else {
-			/* rewind arg */
-			PREV_ARG();
-		}
-	}
-
-	*argcp = argc;
-	*argvp = argv;
-}
+struct iplink_req {
+	struct nlmsghdr		n;
+	struct ifinfomsg	i;
+	char			buf[1024];
+};
 
 static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
-			   struct iplink_req *req, const char *dev)
+			   struct iplink_req *req, int dev_index)
 {
 	char new_rate_api = 0, count = 0, override_legacy_rate = 0;
 	struct ifla_vf_rate tivt;
@@ -377,58 +273,35 @@
 	while (NEXT_ARG_OK()) {
 		NEXT_ARG();
 		if (matches(*argv, "mac") == 0) {
-			struct ifla_vf_mac ivm = { 0 };
-			int halen = nl_get_ll_addr_len(dev);
+			struct ifla_vf_mac ivm;
 
 			NEXT_ARG();
 			ivm.vf = vf;
 			len = ll_addr_a2n((char *)ivm.mac, 32, *argv);
 			if (len < 0)
 				return -1;
-			if (halen > 0 && len != halen) {
-				fprintf(stderr,
-					"Invalid address length %d - must be %d bytes\n",
-					len, halen);
-				return -1;
-			}
-			addattr_l(&req->n, sizeof(*req), IFLA_VF_MAC,
-				  &ivm, sizeof(ivm));
+			addattr_l(&req->n, sizeof(*req), IFLA_VF_MAC, &ivm, sizeof(ivm));
 		} else if (matches(*argv, "vlan") == 0) {
-			struct ifla_vf_vlan_info ivvi;
+			struct ifla_vf_vlan ivv;
 
-			iplink_parse_vf_vlan_info(vf, &argc, &argv, &ivvi);
-			/* support the old interface in case of older kernel*/
-			if (ivvi.vlan_proto == htons(ETH_P_8021Q)) {
-				struct ifla_vf_vlan ivv;
+			NEXT_ARG();
+			if (get_unsigned(&ivv.vlan, *argv, 0))
+				invarg("Invalid \"vlan\" value\n", *argv);
 
-				ivv.vf = ivvi.vf;
-				ivv.vlan = ivvi.vlan;
-				ivv.qos = ivvi.qos;
-				addattr_l(&req->n, sizeof(*req),
-					  IFLA_VF_VLAN, &ivv, sizeof(ivv));
-			} else {
-				struct rtattr *vfvlanlist;
-
-				vfvlanlist = addattr_nest(&req->n, sizeof(*req),
-							  IFLA_VF_VLAN_LIST);
-				addattr_l(&req->n, sizeof(*req),
-					  IFLA_VF_VLAN_INFO, &ivvi,
-					  sizeof(ivvi));
-
-				while (NEXT_ARG_OK()) {
+			ivv.vf = vf;
+			ivv.qos = 0;
+			if (NEXT_ARG_OK()) {
+				NEXT_ARG();
+				if (matches(*argv, "qos") == 0) {
 					NEXT_ARG();
-					if (matches(*argv, "vlan") != 0) {
-						PREV_ARG();
-						break;
-					}
-					iplink_parse_vf_vlan_info(vf, &argc,
-								  &argv, &ivvi);
-					addattr_l(&req->n, sizeof(*req),
-						  IFLA_VF_VLAN_INFO, &ivvi,
-						  sizeof(ivvi));
+					if (get_unsigned(&ivv.qos, *argv, 0))
+						invarg("Invalid \"qos\" value\n", *argv);
+				} else {
+					/* rewind arg */
+					PREV_ARG();
 				}
-				addattr_nest_end(&req->n, vfvlanlist);
 			}
+			addattr_l(&req->n, sizeof(*req), IFLA_VF_VLAN, &ivv, sizeof(ivv));
 		} else if (matches(*argv, "rate") == 0) {
 			struct ifla_vf_tx_rate ivt;
 
@@ -468,8 +341,7 @@
 			else
 				return on_off("spoofchk", *argv);
 			ivs.vf = vf;
-			addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK,
-				  &ivs, sizeof(ivs));
+			addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK, &ivs, sizeof(ivs));
 
 		} else if (matches(*argv, "query_rss") == 0) {
 			struct ifla_vf_rss_query_en ivs;
@@ -482,22 +354,7 @@
 			else
 				return on_off("query_rss", *argv);
 			ivs.vf = vf;
-			addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN,
-				  &ivs, sizeof(ivs));
-
-		} else if (matches(*argv, "trust") == 0) {
-			struct ifla_vf_trust ivt;
-
-			NEXT_ARG();
-			if (matches(*argv, "on") == 0)
-				ivt.setting = 1;
-			else if (matches(*argv, "off") == 0)
-				ivt.setting = 0;
-			else
-				invarg("Invalid \"trust\" value\n", *argv);
-			ivt.vf = vf;
-			addattr_l(&req->n, sizeof(*req), IFLA_VF_TRUST,
-				  &ivt, sizeof(ivt));
+			addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(ivs));
 
 		} else if (matches(*argv, "state") == 0) {
 			struct ifla_vf_link_state ivl;
@@ -512,30 +369,7 @@
 			else
 				invarg("Invalid \"state\" value\n", *argv);
 			ivl.vf = vf;
-			addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE,
-				  &ivl, sizeof(ivl));
-		} else if (matches(*argv, "node_guid") == 0) {
-			struct ifla_vf_guid ivg;
-
-			NEXT_ARG();
-			ivg.vf = vf;
-			if (get_guid(&ivg.guid, *argv)) {
-				invarg("Invalid GUID format\n", *argv);
-				return -1;
-			}
-			addattr_l(&req->n, sizeof(*req), IFLA_VF_IB_NODE_GUID,
-				  &ivg, sizeof(ivg));
-		} else if (matches(*argv, "port_guid") == 0) {
-			struct ifla_vf_guid ivg;
-
-			NEXT_ARG();
-			ivg.vf = vf;
-			if (get_guid(&ivg.guid, *argv)) {
-				invarg("Invalid GUID format\n", *argv);
-				return -1;
-			}
-			addattr_l(&req->n, sizeof(*req), IFLA_VF_IB_PORT_GUID,
-				  &ivg, sizeof(ivg));
+			addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE, &ivl, sizeof(ivl));
 		} else {
 			/* rewind arg */
 			PREV_ARG();
@@ -547,20 +381,12 @@
 		int tmin, tmax;
 
 		if (tivt.min_tx_rate == -1 || tivt.max_tx_rate == -1) {
-			ipaddr_get_vf_rate(tivt.vf, &tmin, &tmax, dev);
+			ipaddr_get_vf_rate(tivt.vf, &tmin, &tmax, dev_index);
 			if (tivt.min_tx_rate == -1)
 				tivt.min_tx_rate = tmin;
 			if (tivt.max_tx_rate == -1)
 				tivt.max_tx_rate = tmax;
 		}
-
-		if (tivt.max_tx_rate && tivt.min_tx_rate > tivt.max_tx_rate) {
-			fprintf(stderr,
-				"Invalid min_tx_rate %d - must be <= max_tx_rate %d\n",
-				tivt.min_tx_rate, tivt.max_tx_rate);
-			return -1;
-		}
-
 		addattr_l(&req->n, sizeof(*req), IFLA_VF_RATE, &tivt,
 			  sizeof(tivt));
 	}
@@ -575,11 +401,10 @@
 	return 0;
 }
 
-int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
+int iplink_parse(int argc, char **argv, struct iplink_req *req,
+		 char **name, char **type, char **link, char **dev,
+		 int *group, int *index)
 {
-	char *name = NULL;
-	char *dev = NULL;
-	char *link = NULL;
 	int ret, len;
 	char abuf[32];
 	int qlen = -1;
@@ -588,11 +413,10 @@
 	int vf = -1;
 	int numtxqueues = -1;
 	int numrxqueues = -1;
+	int dev_index = 0;
 	int link_netnsid = -1;
-	int index = 0;
-	int group = -1;
-	int addr_len = 0;
 
+	*group = -1;
 	ret = argc;
 
 	while (argc > 0) {
@@ -604,38 +428,28 @@
 			req->i.ifi_flags &= ~IFF_UP;
 		} else if (strcmp(*argv, "name") == 0) {
 			NEXT_ARG();
-			if (name)
-				duparg("name", *argv);
-			if (check_ifname(*argv))
-				invarg("\"name\" not a valid ifname", *argv);
-			name = *argv;
-			if (!dev)
-				dev = name;
+			*name = *argv;
 		} else if (strcmp(*argv, "index") == 0) {
 			NEXT_ARG();
-			if (index)
-				duparg("index", *argv);
-			index = atoi(*argv);
-			if (index <= 0)
+			*index = atoi(*argv);
+			if (*index < 0)
 				invarg("Invalid \"index\" value", *argv);
 		} else if (matches(*argv, "link") == 0) {
 			NEXT_ARG();
-			link = *argv;
+			*link = *argv;
 		} else if (matches(*argv, "address") == 0) {
 			NEXT_ARG();
-			addr_len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
-			if (addr_len < 0)
+			len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
+			if (len < 0)
 				return -1;
-			addattr_l(&req->n, sizeof(*req),
-				  IFLA_ADDRESS, abuf, addr_len);
+			addattr_l(&req->n, sizeof(*req), IFLA_ADDRESS, abuf, len);
 		} else if (matches(*argv, "broadcast") == 0 ||
 			   strcmp(*argv, "brd") == 0) {
 			NEXT_ARG();
 			len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
 			if (len < 0)
 				return -1;
-			addattr_l(&req->n, sizeof(*req),
-				  IFLA_BROADCAST, abuf, len);
+			addattr_l(&req->n, sizeof(*req), IFLA_BROADCAST, abuf, len);
 		} else if (matches(*argv, "txqueuelen") == 0 ||
 			   strcmp(*argv, "qlen") == 0 ||
 			   matches(*argv, "txqlen") == 0) {
@@ -644,8 +458,7 @@
 				duparg("txqueuelen", *argv);
 			if (get_integer(&qlen,  *argv, 0))
 				invarg("Invalid \"txqueuelen\" value\n", *argv);
-			addattr_l(&req->n, sizeof(*req),
-				  IFLA_TXQLEN, &qlen, 4);
+			addattr_l(&req->n, sizeof(*req), IFLA_TXQLEN, &qlen, 4);
 		} else if (strcmp(*argv, "mtu") == 0) {
 			NEXT_ARG();
 			if (mtu != -1)
@@ -653,32 +466,15 @@
 			if (get_integer(&mtu, *argv, 0))
 				invarg("Invalid \"mtu\" value\n", *argv);
 			addattr_l(&req->n, sizeof(*req), IFLA_MTU, &mtu, 4);
-		} else if (strcmp(*argv, "xdpgeneric") == 0 ||
-			   strcmp(*argv, "xdpdrv") == 0 ||
-			   strcmp(*argv, "xdpoffload") == 0 ||
-			   strcmp(*argv, "xdp") == 0) {
-			bool generic = strcmp(*argv, "xdpgeneric") == 0;
-			bool drv = strcmp(*argv, "xdpdrv") == 0;
-			bool offload = strcmp(*argv, "xdpoffload") == 0;
-
-			NEXT_ARG();
-			if (xdp_parse(&argc, &argv, req, dev,
-				      generic, drv, offload))
-				exit(-1);
-
-			if (offload && name == dev)
-				dev = NULL;
 		} else if (strcmp(*argv, "netns") == 0) {
 			NEXT_ARG();
 			if (netns != -1)
 				duparg("netns", *argv);
 			netns = netns_get_fd(*argv);
 			if (netns >= 0)
-				addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD,
-					  &netns, 4);
+				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);
+				addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4);
 			else
 				invarg("Invalid \"netns\" value\n", *argv);
 		} else if (strcmp(*argv, "multicast") == 0) {
@@ -731,18 +527,6 @@
 				req->i.ifi_flags |= IFF_NOARP;
 			else
 				return on_off("arp", *argv);
-		} else if (strcmp(*argv, "carrier") == 0) {
-			int carrier;
-
-			NEXT_ARG();
-			if (strcmp(*argv, "on") == 0)
-				carrier = 1;
-			else if (strcmp(*argv, "off") == 0)
-				carrier = 0;
-			else
-				return on_off("carrier", *argv);
-
-			addattr8(&req->n, sizeof(*req), IFLA_CARRIER, carrier);
 		} else if (strcmp(*argv, "vf") == 0) {
 			struct rtattr *vflist;
 
@@ -752,16 +536,13 @@
 
 			vflist = addattr_nest(&req->n, sizeof(*req),
 					      IFLA_VFINFO_LIST);
-			if (!dev)
+			if (dev_index == 0)
 				missarg("dev");
 
-			len = iplink_parse_vf(vf, &argc, &argv, req, dev);
+			len = iplink_parse_vf(vf, &argc, &argv, req, dev_index);
 			if (len < 0)
 				return -1;
 			addattr_nest_end(&req->n, vflist);
-
-			if (name == dev)
-				dev = NULL;
 		} else if (matches(*argv, "master") == 0) {
 			int ifindex;
 
@@ -771,17 +552,6 @@
 				invarg("Device does not exist\n", *argv);
 			addattr_l(&req->n, sizeof(*req), IFLA_MASTER,
 				  &ifindex, 4);
-		} else if (strcmp(*argv, "vrf") == 0) {
-			int ifindex;
-
-			NEXT_ARG();
-			ifindex = ll_name_to_index(*argv);
-			if (!ifindex)
-				invarg("Not a valid VRF name\n", *argv);
-			if (!name_is_vrf(*argv))
-				invarg("Not a valid VRF name\n", *argv);
-			addattr_l(&req->n, sizeof(*req), IFLA_MASTER,
-				  &ifindex, sizeof(ifindex));
 		} else if (matches(*argv, "nomaster") == 0) {
 			int ifindex = 0;
 
@@ -804,18 +574,16 @@
 			break;
 		} else if (matches(*argv, "alias") == 0) {
 			NEXT_ARG();
-			len = strlen(*argv);
-			if (len >= IFALIASZ)
-				invarg("alias too long\n", *argv);
 			addattr_l(&req->n, sizeof(*req), IFLA_IFALIAS,
-				  *argv, len);
+				  *argv, strlen(*argv));
+			argc--; argv++;
+			break;
 		} else if (strcmp(*argv, "group") == 0) {
 			NEXT_ARG();
-			if (group != -1)
+			if (*group != -1)
 				duparg("group", *argv);
-			if (rtnl_group_a2n(&group, *argv))
+			if (rtnl_group_a2n(group, *argv))
 				invarg("Invalid \"group\" value\n", *argv);
-			addattr32(&req->n, sizeof(*req), IFLA_GROUP, group);
 		} else if (strcmp(*argv, "mode") == 0) {
 			int mode;
 
@@ -838,8 +606,7 @@
 			if (numtxqueues != -1)
 				duparg("numtxqueues", *argv);
 			if (get_integer(&numtxqueues, *argv, 0))
-				invarg("Invalid \"numtxqueues\" value\n",
-				       *argv);
+				invarg("Invalid \"numtxqueues\" value\n", *argv);
 			addattr_l(&req->n, sizeof(*req), IFLA_NUM_TX_QUEUES,
 				  &numtxqueues, 4);
 		} else if (matches(*argv, "numrxqueues") == 0) {
@@ -847,8 +614,7 @@
 			if (numrxqueues != -1)
 				duparg("numrxqueues", *argv);
 			if (get_integer(&numrxqueues, *argv, 0))
-				invarg("Invalid \"numrxqueues\" value\n",
-				       *argv);
+				invarg("Invalid \"numrxqueues\" value\n", *argv);
 			addattr_l(&req->n, sizeof(*req), IFLA_NUM_RX_QUEUES,
 				  &numrxqueues, 4);
 		} else if (matches(*argv, "addrgenmode") == 0) {
@@ -858,35 +624,18 @@
 			NEXT_ARG();
 			mode = get_addr_gen_mode(*argv);
 			if (mode < 0)
-				invarg("Invalid address generation mode\n",
-				       *argv);
+				invarg("Invalid address generation mode\n", *argv);
 			afs = addattr_nest(&req->n, sizeof(*req), IFLA_AF_SPEC);
 			afs6 = addattr_nest(&req->n, sizeof(*req), AF_INET6);
-			addattr8(&req->n, sizeof(*req),
-				 IFLA_INET6_ADDR_GEN_MODE, mode);
+			addattr8(&req->n, sizeof(*req), IFLA_INET6_ADDR_GEN_MODE, mode);
 			addattr_nest_end(&req->n, afs6);
 			addattr_nest_end(&req->n, afs);
-		} else if (matches(*argv, "link-netns") == 0) {
-			NEXT_ARG();
-			if (link_netnsid != -1)
-				duparg("link-netns/link-netnsid", *argv);
-			link_netnsid = get_netnsid_from_name(*argv);
-			/* No nsid? Try to assign one. */
-			if (link_netnsid < 0)
-				set_netnsid_from_name(*argv, -1);
-			link_netnsid = get_netnsid_from_name(*argv);
-			if (link_netnsid < 0)
-				invarg("Invalid \"link-netns\" value\n",
-				       *argv);
-			addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID,
-				  link_netnsid);
 		} else if (matches(*argv, "link-netnsid") == 0) {
 			NEXT_ARG();
 			if (link_netnsid != -1)
-				duparg("link-netns/link-netnsid", *argv);
+				duparg("link-netnsid", *argv);
 			if (get_integer(&link_netnsid, *argv, 0))
-				invarg("Invalid \"link-netnsid\" value\n",
-				       *argv);
+				invarg("Invalid \"link-netnsid\" value\n", *argv);
 			addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID,
 				  link_netnsid);
 		} else if (strcmp(*argv, "protodown") == 0) {
@@ -901,167 +650,148 @@
 				return on_off("protodown", *argv);
 			addattr8(&req->n, sizeof(*req), IFLA_PROTO_DOWN,
 				 proto_down);
-		} else if (strcmp(*argv, "gso_max_size") == 0) {
-			unsigned int max_size;
-
-			NEXT_ARG();
-			if (get_unsigned(&max_size, *argv, 0) ||
-			    max_size > GSO_MAX_SIZE)
-				invarg("Invalid \"gso_max_size\" value\n",
-				       *argv);
-			addattr32(&req->n, sizeof(*req),
-				  IFLA_GSO_MAX_SIZE, max_size);
-		} else if (strcmp(*argv, "gso_max_segs") == 0) {
-			unsigned int max_segs;
-
-			NEXT_ARG();
-			if (get_unsigned(&max_segs, *argv, 0) ||
-			    max_segs > GSO_MAX_SEGS)
-				invarg("Invalid \"gso_max_segs\" value\n",
-				       *argv);
-			addattr32(&req->n, sizeof(*req),
-				  IFLA_GSO_MAX_SEGS, max_segs);
 		} else {
 			if (matches(*argv, "help") == 0)
 				usage();
 
 			if (strcmp(*argv, "dev") == 0)
 				NEXT_ARG();
-			if (dev != name)
+			if (*dev)
 				duparg2("dev", *argv);
-			if (check_ifname(*argv))
-				invarg("\"dev\" not a valid ifname", *argv);
-			dev = *argv;
+			*dev = *argv;
+			dev_index = ll_name_to_index(*dev);
 		}
 		argc--; argv++;
 	}
 
-	ret -= argc;
+	return ret - argc;
+}
 
-	/* Allow "ip link add dev" and "ip link add name" */
-	if (!name)
-		name = dev;
-	else if (!dev)
-		dev = name;
-	else if (!strcmp(name, dev))
-		name = dev;
+static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
+{
+	int len;
+	char *dev = NULL;
+	char *name = NULL;
+	char *link = NULL;
+	char *type = NULL;
+	int index = -1;
+	int group;
+	struct link_util *lu = NULL;
+	struct iplink_req req;
+	int ret;
 
-	if (dev && addr_len &&
-	    !(req->n.nlmsg_flags & NLM_F_CREATE)) {
-		int halen = nl_get_ll_addr_len(dev);
+	memset(&req, 0, sizeof(req));
 
-		if (halen >= 0 && halen != addr_len) {
-			fprintf(stderr,
-				"Invalid address length %d - must be %d bytes\n",
-				addr_len, halen);
-			return -1;
-		}
-	}
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+	req.n.nlmsg_type = cmd;
+	req.i.ifi_family = preferred_family;
 
-	if (!(req->n.nlmsg_flags & NLM_F_CREATE) && index) {
-		fprintf(stderr,
-			"index can be used only when creating devices.\n");
-		exit(-1);
-	}
+	ret = iplink_parse(argc, argv, &req, &name, &type, &link, &dev, &group, &index);
+	if (ret < 0)
+		return ret;
+
+	argc -= ret;
+	argv += ret;
 
 	if (group != -1) {
-		if (!dev) {
+		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);
-				exit(-1);
+				fprintf(stderr, "Garbage instead of arguments "
+						"\"%s ...\". Try \"ip link "
+						"help\".\n", *argv);
+				return -1;
 			}
-			if (req->n.nlmsg_flags & NLM_F_CREATE) {
-				fprintf(stderr,
-					"group cannot be used when creating devices.\n");
-				exit(-1);
+			if (flags & NLM_F_CREATE) {
+				fprintf(stderr, "group cannot be used when "
+						"creating devices.\n");
+				return -1;
 			}
 
-			*type = NULL;
-			return ret;
+			req.i.ifi_index = 0;
+			addattr32(&req.n, sizeof(req), IFLA_GROUP, group);
+			if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
+				return -2;
+			return 0;
 		}
 	}
 
-	if (!(req->n.nlmsg_flags & NLM_F_CREATE)) {
+	if (!(flags & NLM_F_CREATE)) {
 		if (!dev) {
-			fprintf(stderr,
-				"Not enough information: \"dev\" argument is required.\n");
+			fprintf(stderr, "Not enough information: \"dev\" "
+					"argument is required.\n");
+			exit(-1);
+		}
+		if (cmd == RTM_NEWLINK && index != -1) {
+			fprintf(stderr, "index can be used only when "
+					"creating devices.\n");
 			exit(-1);
 		}
 
-		req->i.ifi_index = ll_name_to_index(dev);
-		if (!req->i.ifi_index)
-			return nodev(dev);
-
-		/* Not renaming to the same name */
-		if (name == dev)
-			name = NULL;
+		req.i.ifi_index = ll_name_to_index(dev);
+		if (req.i.ifi_index == 0) {
+			fprintf(stderr, "Cannot find device \"%s\"\n", dev);
+			return -1;
+		}
 	} else {
-		if (name != dev) {
-			fprintf(stderr,
-				"both \"name\" and \"dev\" cannot be used when creating devices.\n");
-			exit(-1);
-		}
+		/* Allow "ip link add dev" and "ip link add name" */
+		if (!name)
+			name = dev;
 
 		if (link) {
 			int ifindex;
 
 			ifindex = ll_name_to_index(link);
-			if (!ifindex)
-				return nodev(link);
-			addattr32(&req->n, sizeof(*req), IFLA_LINK, ifindex);
+			if (ifindex == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n",
+					link);
+				return -1;
+			}
+			addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifindex, 4);
 		}
 
-		req->i.ifi_index = index;
+		if (index == -1)
+			req.i.ifi_index = 0;
+		else
+			req.i.ifi_index = index;
 	}
 
 	if (name) {
-		addattr_l(&req->n, sizeof(*req),
-			  IFLA_IFNAME, name, strlen(name) + 1);
+		len = strlen(name) + 1;
+		if (len == 1)
+			invarg("\"\" is not a valid device identifier\n", "name");
+		if (len > IFNAMSIZ)
+			invarg("\"name\" too long\n", name);
+		addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len);
 	}
 
-	return ret;
-}
-
-static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
-{
-	char *type = NULL;
-	struct iplink_req req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST | flags,
-		.n.nlmsg_type = cmd,
-		.i.ifi_family = preferred_family,
-	};
-	int ret;
-
-	ret = iplink_parse(argc, argv, &req, &type);
-	if (ret < 0)
-		return ret;
-
 	if (type) {
-		struct link_util *lu;
 		struct rtattr *linkinfo;
-		char *ulinep = strchr(type, '_');
+		char slavebuf[128], *ulinep = strchr(type, '_');
 		int iflatype;
 
 		linkinfo = addattr_nest(&req.n, sizeof(req), IFLA_LINKINFO);
 		addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type,
 			 strlen(type));
 
-		lu = get_link_kind(type);
-		if (ulinep && !strcmp(ulinep, "_slave"))
+		if (ulinep && !strcmp(ulinep, "_slave")) {
+			strncpy(slavebuf, type, sizeof(slavebuf));
+			slavebuf[sizeof(slavebuf) - 1] = '\0';
+			ulinep = strchr(slavebuf, '_');
+			/* check in case it was after sizeof(slavebuf) - 1*/
+			if (ulinep)
+				*ulinep = '\0';
+			lu = get_link_slave_kind(slavebuf);
 			iflatype = IFLA_INFO_SLAVE_DATA;
-		else
+		} else {
+			lu = get_link_kind(type);
 			iflatype = IFLA_INFO_DATA;
-
-		argc -= ret;
-		argv += ret;
-
+		}
 		if (lu && argc) {
-			struct rtattr *data;
-
-			data = addattr_nest(&req.n, sizeof(req), iflatype);
+			struct rtattr *data = addattr_nest(&req.n, sizeof(req), iflatype);
 
 			if (lu->parse_opt &&
 			    lu->parse_opt(lu, argc, argv, &req.n))
@@ -1071,51 +801,58 @@
 		} else if (argc) {
 			if (matches(*argv, "help") == 0)
 				usage();
-			fprintf(stderr,
-				"Garbage instead of arguments \"%s ...\". Try \"ip link help\".\n",
-				*argv);
+			fprintf(stderr, "Garbage instead of arguments \"%s ...\". "
+					"Try \"ip link help\".\n", *argv);
 			return -1;
 		}
 		addattr_nest_end(&req.n, linkinfo);
 	} else if (flags & NLM_F_CREATE) {
-		fprintf(stderr,
-			"Not enough information: \"type\" argument is required\n");
+		fprintf(stderr, "Not enough information: \"type\" argument "
+				"is required\n");
 		return -1;
 	}
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		return -2;
 
-	/* remove device from cache; next use can refresh with new data */
-	ll_drop_by_index(req.i.ifi_index);
-
 	return 0;
 }
 
-int iplink_get(char *name, __u32 filt_mask)
+int iplink_get(unsigned int flags, char *name, __u32 filt_mask)
 {
-	struct iplink_req req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_GETLINK,
-		.i.ifi_family = preferred_family,
-	};
-	struct nlmsghdr *answer;
+	int len;
+	struct iplink_req req;
+	struct {
+		struct nlmsghdr n;
+		char buf[16384];
+	} answer;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+	req.n.nlmsg_type = RTM_GETLINK;
+	req.i.ifi_family = preferred_family;
 
 	if (name) {
-		addattr_l(&req.n, sizeof(req),
-			  IFLA_IFNAME, name, strlen(name) + 1);
+		len = strlen(name) + 1;
+		if (len == 1)
+			invarg("\"\" is not a valid device identifier\n",
+				   "name");
+		if (len > IFNAMSIZ)
+			invarg("\"name\" too long\n", name);
+		addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len);
 	}
 	addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask);
 
-	if (rtnl_talk(&rth, &req.n, &answer) < 0)
+	if (rtnl_talk(&rth, &req.n, &answer.n, sizeof(answer)) < 0)
 		return -2;
 
-	open_json_object(NULL);
-	print_linkinfo(answer, stdout);
-	close_json_object();
+	if (brief)
+		print_linkinfo_brief(NULL, &answer.n, stdout);
+	else
+		print_linkinfo(NULL, &answer.n, stdout);
 
-	free(answer);
 	return 0;
 }
 
@@ -1146,7 +883,7 @@
 	int fd;
 	int err;
 
-	strlcpy(ifr.ifr_name, dev, IFNAMSIZ);
+	strncpy(ifr.ifr_name, dev, IFNAMSIZ);
 	fd = get_ctl_fd();
 	if (fd < 0)
 		return -1;
@@ -1173,8 +910,8 @@
 	int fd;
 	int err;
 
-	strlcpy(ifr.ifr_name, dev, IFNAMSIZ);
-	strlcpy(ifr.ifr_newname, newdev, IFNAMSIZ);
+	strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+	strncpy(ifr.ifr_newname, newdev, IFNAMSIZ);
 	fd = get_ctl_fd();
 	if (fd < 0)
 		return -1;
@@ -1190,14 +927,16 @@
 
 static int set_qlen(const char *dev, int qlen)
 {
-	struct ifreq ifr = { .ifr_qlen = qlen };
+	struct ifreq ifr;
 	int s;
 
 	s = get_ctl_fd();
 	if (s < 0)
 		return -1;
 
-	strlcpy(ifr.ifr_name, dev, IFNAMSIZ);
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+	ifr.ifr_qlen = qlen;
 	if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
 		perror("SIOCSIFXQLEN");
 		close(s);
@@ -1210,14 +949,16 @@
 
 static int set_mtu(const char *dev, int mtu)
 {
-	struct ifreq ifr = { .ifr_mtu = mtu };
+	struct ifreq ifr;
 	int s;
 
 	s = get_ctl_fd();
 	if (s < 0)
 		return -1;
 
-	strlcpy(ifr.ifr_name, dev, IFNAMSIZ);
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+	ifr.ifr_mtu = mtu;
 	if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
 		perror("SIOCSIFMTU");
 		close(s);
@@ -1230,11 +971,8 @@
 
 static int get_address(const char *dev, int *htype)
 {
-	struct ifreq ifr = {};
-	struct sockaddr_ll me = {
-		.sll_family = AF_PACKET,
-		.sll_protocol = htons(ETH_P_LOOP),
-	};
+	struct ifreq ifr;
+	struct sockaddr_ll me;
 	socklen_t alen;
 	int s;
 
@@ -1244,14 +982,18 @@
 		return -1;
 	}
 
-	strlcpy(ifr.ifr_name, dev, IFNAMSIZ);
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, dev, IFNAMSIZ);
 	if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
 		perror("SIOCGIFINDEX");
 		close(s);
 		return -1;
 	}
 
+	memset(&me, 0, sizeof(me));
+	me.sll_family = AF_PACKET;
 	me.sll_ifindex = ifr.ifr_ifindex;
+	me.sll_protocol = htons(ETH_P_LOOP);
 	if (bind(s, (struct sockaddr *)&me, sizeof(me)) == -1) {
 		perror("bind");
 		close(s);
@@ -1275,15 +1017,13 @@
 	int alen;
 
 	memset(ifr, 0, sizeof(*ifr));
-	strlcpy(ifr->ifr_name, dev, IFNAMSIZ);
+	strncpy(ifr->ifr_name, dev, IFNAMSIZ);
 	ifr->ifr_hwaddr.sa_family = hatype;
 	alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla);
 	if (alen < 0)
 		return -1;
 	if (alen != halen) {
-		fprintf(stderr,
-			"Wrong address (%s) length: expected %d bytes\n",
-			lla, halen);
+		fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen);
 		return -1;
 	}
 	return 0;
@@ -1327,8 +1067,6 @@
 			flags &= ~IFF_UP;
 		} else if (strcmp(*argv, "name") == 0) {
 			NEXT_ARG();
-			if (check_ifname(*argv))
-				invarg("\"name\" not a valid ifname", *argv);
 			newname = *argv;
 		} else if (matches(*argv, "address") == 0) {
 			NEXT_ARG();
@@ -1419,16 +1157,13 @@
 
 			if (dev)
 				duparg2("dev", *argv);
-			if (check_ifname(*argv))
-				invarg("\"dev\" not a valid ifname", *argv);
 			dev = *argv;
 		}
 		argc--; argv++;
 	}
 
 	if (!dev) {
-		fprintf(stderr,
-			"Not enough of information: \"dev\" argument is required.\n");
+		fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n");
 		exit(-1);
 	}
 
@@ -1437,18 +1172,18 @@
 		if (halen < 0)
 			return -1;
 		if (newaddr) {
-			if (parse_address(dev, htype, halen,
-					  newaddr, &ifr0) < 0)
+			if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
 				return -1;
 		}
 		if (newbrd) {
-			if (parse_address(dev, htype, halen,
-					  newbrd, &ifr1) < 0)
+			if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
 				return -1;
 		}
 	}
 
 	if (newname && strcmp(dev, newname)) {
+		if (strlen(newname) == 0)
+			invarg("\"\" is not a valid device identifier\n", "name");
 		if (do_changename(dev, newname) < 0)
 			return -1;
 		dev = newname;
@@ -1477,146 +1212,6 @@
 }
 #endif /* IPLINK_IOCTL_COMPAT */
 
-static void print_mpls_stats(FILE *fp, struct rtattr *attr)
-{
-	struct rtattr *mrtb[MPLS_STATS_MAX+1];
-	struct mpls_link_stats *stats;
-
-	parse_rtattr(mrtb, MPLS_STATS_MAX, RTA_DATA(attr),
-		     RTA_PAYLOAD(attr));
-	if (!mrtb[MPLS_STATS_LINK])
-		return;
-
-	stats = RTA_DATA(mrtb[MPLS_STATS_LINK]);
-
-	fprintf(fp, "    mpls:\n");
-	fprintf(fp, "        RX: bytes  packets  errors  dropped  noroute\n");
-	fprintf(fp, "        ");
-	print_num(fp, 10, stats->rx_bytes);
-	print_num(fp, 8, stats->rx_packets);
-	print_num(fp, 7, stats->rx_errors);
-	print_num(fp, 8, stats->rx_dropped);
-	print_num(fp, 7, stats->rx_noroute);
-	fprintf(fp, "\n");
-	fprintf(fp, "        TX: bytes  packets  errors  dropped\n");
-	fprintf(fp, "        ");
-	print_num(fp, 10, stats->tx_bytes);
-	print_num(fp, 8, stats->tx_packets);
-	print_num(fp, 7, stats->tx_errors);
-	print_num(fp, 7, stats->tx_dropped);
-	fprintf(fp, "\n");
-}
-
-static void print_af_stats_attr(FILE *fp, int ifindex, struct rtattr *attr)
-{
-	bool if_printed = false;
-	struct rtattr *i;
-	int rem;
-
-	rem = RTA_PAYLOAD(attr);
-	for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
-		if (preferred_family != AF_UNSPEC &&
-		    i->rta_type != preferred_family)
-			continue;
-
-		if (!if_printed) {
-			fprintf(fp, "%u: %s\n", ifindex,
-				ll_index_to_name(ifindex));
-			if_printed = true;
-		}
-
-		switch (i->rta_type) {
-		case AF_MPLS:
-			print_mpls_stats(fp, i);
-			break;
-		default:
-			fprintf(fp, "    unknown af(%d)\n", i->rta_type);
-			break;
-		}
-	}
-}
-
-struct af_stats_ctx {
-	FILE *fp;
-	int ifindex;
-};
-
-static int print_af_stats(struct nlmsghdr *n, void *arg)
-{
-	struct if_stats_msg *ifsm = NLMSG_DATA(n);
-	struct rtattr *tb[IFLA_STATS_MAX+1];
-	int len = n->nlmsg_len;
-	struct af_stats_ctx *ctx = arg;
-	FILE *fp = ctx->fp;
-
-	len -= NLMSG_LENGTH(sizeof(*ifsm));
-	if (len < 0) {
-		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
-		return -1;
-	}
-
-	if (ctx->ifindex && ifsm->ifindex != ctx->ifindex)
-		return 0;
-
-	parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
-
-	if (tb[IFLA_STATS_AF_SPEC])
-		print_af_stats_attr(fp, ifsm->ifindex, tb[IFLA_STATS_AF_SPEC]);
-
-	fflush(fp);
-	return 0;
-}
-
-static int iplink_afstats(int argc, char **argv)
-{
-	__u32 filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_AF_SPEC);
-	const char *filter_dev = NULL;
-	struct af_stats_ctx ctx = {
-		.fp = stdout,
-		.ifindex = 0,
-	};
-
-	while (argc > 0) {
-		if (strcmp(*argv, "dev") == 0) {
-			NEXT_ARG();
-			if (filter_dev)
-				duparg2("dev", *argv);
-			filter_dev = *argv;
-		} else if (matches(*argv, "help") == 0) {
-			usage();
-		} else {
-			fprintf(stderr,
-				"Command \"%s\" is unknown, try \"ip link help\".\n",
-				*argv);
-			exit(-1);
-		}
-
-		argv++; argc--;
-	}
-
-	if (filter_dev) {
-		ctx.ifindex = ll_name_to_index(filter_dev);
-		if (ctx.ifindex <= 0) {
-			fprintf(stderr,
-				"Device \"%s\" does not exist.\n",
-				filter_dev);
-			return -1;
-		}
-	}
-
-	if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
-		perror("Cannont send dump request");
-		return 1;
-	}
-
-	if (rtnl_dump_filter(&rth, print_af_stats, &ctx) < 0) {
-		fprintf(stderr, "Dump terminated\n");
-		return 1;
-	}
-
-	return 0;
-}
-
 static void do_help(int argc, char **argv)
 {
 	struct link_util *lu = NULL;
@@ -1666,14 +1261,6 @@
 	    matches(*argv, "list") == 0)
 		return ipaddr_list_link(argc-1, argv+1);
 
-	if (matches(*argv, "xstats") == 0)
-		return iplink_ifla_xstats(argc-1, argv+1);
-
-	if (matches(*argv, "afstats") == 0) {
-		iplink_afstats(argc-1, argv+1);
-		return 0;
-	}
-
 	if (matches(*argv, "help") == 0) {
 		do_help(argc-1, argv+1);
 		return 0;
diff --git a/ip/iplink_bond.c b/ip/iplink_bond.c
index 585b6be..cb2f045 100644
--- a/ip/iplink_bond.c
+++ b/ip/iplink_bond.c
@@ -13,18 +13,16 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <linux/if_bonding.h>
+#include <linux/if_link.h>
+#include <linux/if_ether.h>
+#include <net/if.h>
 
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
-#include "json_print.h"
 
 #define BOND_MAX_ARP_TARGETS    16
 
-static unsigned int xstats_print_attr;
-static int filter_index;
-
 static const char *mode_tbl[] = {
 	"balance-rr",
 	"active-backup",
@@ -120,7 +118,6 @@
 		"Usage: ... bond [ mode BONDMODE ] [ active_slave SLAVE_DEV ]\n"
 		"                [ clear_active_slave ] [ miimon MIIMON ]\n"
 		"                [ updelay UPDELAY ] [ downdelay DOWNDELAY ]\n"
-		"                [ peer_notify_delay DELAY ]\n"
 		"                [ use_carrier USE_CARRIER ]\n"
 		"                [ arp_interval ARP_INTERVAL ]\n"
 		"                [ arp_validate ARP_VALIDATE ]\n"
@@ -136,7 +133,7 @@
 		"                [ min_links MIN_LINKS ]\n"
 		"                [ lp_interval LP_INTERVAL ]\n"
 		"                [ packets_per_slave PACKETS_PER_SLAVE ]\n"
-		"                [ tlb_dynamic_lb TLB_DYNAMIC_LB ]\n"
+		"		 [ tlb_dynamic_lb TLB_DYNAMIC_LB ]\n"
 		"                [ lacp_rate LACP_RATE ]\n"
 		"                [ ad_select AD_SELECT ]\n"
 		"                [ ad_user_port_key PORTKEY ]\n"
@@ -166,10 +163,10 @@
 	__u8 xmit_hash_policy, num_peer_notif, all_slaves_active;
 	__u8 lacp_rate, ad_select, tlb_dynamic_lb;
 	__u16 ad_user_port_key, ad_actor_sys_prio;
-	__u32 miimon, updelay, downdelay, peer_notify_delay, arp_interval, arp_validate;
+	__u32 miimon, updelay, downdelay, arp_interval, arp_validate;
 	__u32 arp_all_targets, resend_igmp, min_links, lp_interval;
 	__u32 packets_per_slave;
-	unsigned int ifindex;
+	unsigned ifindex;
 
 	while (argc > 0) {
 		if (matches(*argv, "mode") == 0) {
@@ -180,9 +177,9 @@
 			addattr8(n, 1024, IFLA_BOND_MODE, mode);
 		} else if (matches(*argv, "active_slave") == 0) {
 			NEXT_ARG();
-			ifindex = ll_name_to_index(*argv);
+			ifindex = if_nametoindex(*argv);
 			if (!ifindex)
-				return nodev(*argv);
+				return -1;
 			addattr32(n, 1024, IFLA_BOND_ACTIVE_SLAVE, ifindex);
 		} else if (matches(*argv, "clear_active_slave") == 0) {
 			addattr32(n, 1024, IFLA_BOND_ACTIVE_SLAVE, 0);
@@ -201,11 +198,6 @@
 			if (get_u32(&downdelay, *argv, 0))
 				invarg("invalid downdelay", *argv);
 			addattr32(n, 1024, IFLA_BOND_DOWNDELAY, downdelay);
-		} else if (matches(*argv, "peer_notify_delay") == 0) {
-			NEXT_ARG();
-			if (get_u32(&peer_notify_delay, *argv, 0))
-				invarg("invalid peer_notify_delay", *argv);
-			addattr32(n, 1024, IFLA_BOND_PEER_NOTIF_DELAY, peer_notify_delay);
 		} else if (matches(*argv, "use_carrier") == 0) {
 			NEXT_ARG();
 			if (get_u8(&use_carrier, *argv, 0))
@@ -217,7 +209,7 @@
 				invarg("invalid arp_interval", *argv);
 			addattr32(n, 1024, IFLA_BOND_ARP_INTERVAL, arp_interval);
 		} else if (matches(*argv, "arp_ip_target") == 0) {
-			struct rtattr *nest = addattr_nest(n, 1024,
+			struct rtattr * nest = addattr_nest(n, 1024,
 				IFLA_BOND_ARP_IP_TARGET);
 			if (NEXT_ARG_OK()) {
 				NEXT_ARG();
@@ -225,9 +217,8 @@
 				char *target = strtok(targets, ",");
 				int i;
 
-				for (i = 0; target && i < BOND_MAX_ARP_TARGETS; i++) {
+				for(i = 0; target && i < BOND_MAX_ARP_TARGETS; i++) {
 					__u32 addr = get_addr32(target);
-
 					addattr32(n, 1024, i, addr);
 					target = strtok(NULL, ",");
 				}
@@ -248,9 +239,9 @@
 			addattr32(n, 1024, IFLA_BOND_ARP_ALL_TARGETS, arp_all_targets);
 		} else if (matches(*argv, "primary") == 0) {
 			NEXT_ARG();
-			ifindex = ll_name_to_index(*argv);
+			ifindex = if_nametoindex(*argv);
 			if (!ifindex)
-				return nodev(*argv);
+				return -1;
 			addattr32(n, 1024, IFLA_BOND_PRIMARY, ifindex);
 		} else if (matches(*argv, "primary_reselect") == 0) {
 			NEXT_ARG();
@@ -377,455 +368,216 @@
 
 static void bond_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
+	unsigned ifindex;
+
 	if (!tb)
 		return;
 
 	if (tb[IFLA_BOND_MODE]) {
 		const char *mode = get_name(mode_tbl,
-					    rta_getattr_u8(tb[IFLA_BOND_MODE]));
-		print_string(PRINT_ANY, "mode", "mode %s ", mode);
+			rta_getattr_u8(tb[IFLA_BOND_MODE]));
+		fprintf(f, "mode %s ", mode);
 	}
 
-	if (tb[IFLA_BOND_ACTIVE_SLAVE]) {
-		unsigned int ifindex =
-			rta_getattr_u32(tb[IFLA_BOND_ACTIVE_SLAVE]);
+	if (tb[IFLA_BOND_ACTIVE_SLAVE] &&
+	    (ifindex = rta_getattr_u32(tb[IFLA_BOND_ACTIVE_SLAVE]))) {
+		char buf[IFNAMSIZ];
+		const char *n = if_indextoname(ifindex, buf);
 
-		if (ifindex) {
-			print_string(PRINT_ANY,
-				     "active_slave",
-				     "active_slave %s ",
-				     ll_index_to_name(ifindex));
-		}
+		if (n)
+			fprintf(f, "active_slave %s ", n);
+		else
+			fprintf(f, "active_slave %u ", ifindex);
 	}
 
 	if (tb[IFLA_BOND_MIIMON])
-		print_uint(PRINT_ANY,
-			   "miimon",
-			   "miimon %u ",
-			   rta_getattr_u32(tb[IFLA_BOND_MIIMON]));
+		fprintf(f, "miimon %u ", rta_getattr_u32(tb[IFLA_BOND_MIIMON]));
 
 	if (tb[IFLA_BOND_UPDELAY])
-		print_uint(PRINT_ANY,
-			   "updelay",
-			   "updelay %u ",
-			   rta_getattr_u32(tb[IFLA_BOND_UPDELAY]));
+		fprintf(f, "updelay %u ", rta_getattr_u32(tb[IFLA_BOND_UPDELAY]));
 
 	if (tb[IFLA_BOND_DOWNDELAY])
-		print_uint(PRINT_ANY,
-			   "downdelay",
-			   "downdelay %u ",
-			   rta_getattr_u32(tb[IFLA_BOND_DOWNDELAY]));
-
-	if (tb[IFLA_BOND_PEER_NOTIF_DELAY])
-		print_uint(PRINT_ANY,
-			   "peer_notify_delay",
-			   "peer_notify_delay %u ",
-			   rta_getattr_u32(tb[IFLA_BOND_PEER_NOTIF_DELAY]));
+		fprintf(f, "downdelay %u ",
+			rta_getattr_u32(tb[IFLA_BOND_DOWNDELAY]));
 
 	if (tb[IFLA_BOND_USE_CARRIER])
-		print_uint(PRINT_ANY,
-			   "use_carrier",
-			   "use_carrier %u ",
-			   rta_getattr_u8(tb[IFLA_BOND_USE_CARRIER]));
+		fprintf(f, "use_carrier %u ",
+			rta_getattr_u8(tb[IFLA_BOND_USE_CARRIER]));
 
 	if (tb[IFLA_BOND_ARP_INTERVAL])
-		print_uint(PRINT_ANY,
-			   "arp_interval",
-			   "arp_interval %u ",
-			   rta_getattr_u32(tb[IFLA_BOND_ARP_INTERVAL]));
+		fprintf(f, "arp_interval %u ",
+			rta_getattr_u32(tb[IFLA_BOND_ARP_INTERVAL]));
 
 	if (tb[IFLA_BOND_ARP_IP_TARGET]) {
 		struct rtattr *iptb[BOND_MAX_ARP_TARGETS + 1];
+		char buf[INET_ADDRSTRLEN];
 		int i;
 
 		parse_rtattr_nested(iptb, BOND_MAX_ARP_TARGETS,
-				    tb[IFLA_BOND_ARP_IP_TARGET]);
+			tb[IFLA_BOND_ARP_IP_TARGET]);
 
-		if (iptb[0]) {
-			open_json_array(PRINT_JSON, "arp_ip_target");
-			print_string(PRINT_FP, NULL, "arp_ip_target ", NULL);
-		}
+		if (iptb[0])
+			fprintf(f, "arp_ip_target ");
 
 		for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) {
 			if (iptb[i])
-				print_string(PRINT_ANY,
-					     NULL,
-					     "%s",
-					     rt_addr_n2a_rta(AF_INET, iptb[i]));
-			if (!is_json_context()
-			    && i < BOND_MAX_ARP_TARGETS-1
-			    && iptb[i+1])
+				fprintf(f, "%s",
+					rt_addr_n2a(AF_INET,
+						    RTA_PAYLOAD(iptb[i]),
+						    RTA_DATA(iptb[i]),
+						    buf,
+						    INET_ADDRSTRLEN));
+			if (i < BOND_MAX_ARP_TARGETS-1 && iptb[i+1])
 				fprintf(f, ",");
 		}
 
-		if (iptb[0]) {
-			print_string(PRINT_FP, NULL, " ", NULL);
-			close_json_array(PRINT_JSON, NULL);
-		}
+		if (iptb[0])
+			fprintf(f, " ");
 	}
 
 	if (tb[IFLA_BOND_ARP_VALIDATE]) {
-		__u32 arp_v = rta_getattr_u32(tb[IFLA_BOND_ARP_VALIDATE]);
-		const char *arp_validate = get_name(arp_validate_tbl, arp_v);
-
-		if (!arp_v && is_json_context())
-			print_null(PRINT_JSON, "arp_validate", NULL, NULL);
-		else
-			print_string(PRINT_ANY,
-				     "arp_validate",
-				     "arp_validate %s ",
-				     arp_validate);
+		const char *arp_validate = get_name(arp_validate_tbl,
+			rta_getattr_u32(tb[IFLA_BOND_ARP_VALIDATE]));
+		fprintf(f, "arp_validate %s ", arp_validate);
 	}
 
 	if (tb[IFLA_BOND_ARP_ALL_TARGETS]) {
 		const char *arp_all_targets = get_name(arp_all_targets_tbl,
-						       rta_getattr_u32(tb[IFLA_BOND_ARP_ALL_TARGETS]));
-		print_string(PRINT_ANY,
-			     "arp_all_targets",
-			     "arp_all_targets %s ",
-			     arp_all_targets);
+			rta_getattr_u32(tb[IFLA_BOND_ARP_ALL_TARGETS]));
+		fprintf(f, "arp_all_targets %s ", arp_all_targets);
 	}
 
-	if (tb[IFLA_BOND_PRIMARY]) {
-		unsigned int ifindex = rta_getattr_u32(tb[IFLA_BOND_PRIMARY]);
+	if (tb[IFLA_BOND_PRIMARY] &&
+	    (ifindex = rta_getattr_u32(tb[IFLA_BOND_PRIMARY]))) {
+		char buf[IFNAMSIZ];
+		const char *n = if_indextoname(ifindex, buf);
 
-		if (ifindex) {
-			print_string(PRINT_ANY,
-				     "primary",
-				     "primary %s ",
-				     ll_index_to_name(ifindex));
-		}
+		if (n)
+			fprintf(f, "primary %s ", n);
+		else
+			fprintf(f, "primary %u ", ifindex);
 	}
 
 	if (tb[IFLA_BOND_PRIMARY_RESELECT]) {
 		const char *primary_reselect = get_name(primary_reselect_tbl,
-							rta_getattr_u8(tb[IFLA_BOND_PRIMARY_RESELECT]));
-		print_string(PRINT_ANY,
-			     "primary_reselect",
-			     "primary_reselect %s ",
-			     primary_reselect);
+			rta_getattr_u8(tb[IFLA_BOND_PRIMARY_RESELECT]));
+		fprintf(f, "primary_reselect %s ", primary_reselect);
 	}
 
 	if (tb[IFLA_BOND_FAIL_OVER_MAC]) {
 		const char *fail_over_mac = get_name(fail_over_mac_tbl,
-						     rta_getattr_u8(tb[IFLA_BOND_FAIL_OVER_MAC]));
-		print_string(PRINT_ANY,
-			     "fail_over_mac",
-			     "fail_over_mac %s ",
-			     fail_over_mac);
+			rta_getattr_u8(tb[IFLA_BOND_FAIL_OVER_MAC]));
+		fprintf(f, "fail_over_mac %s ", fail_over_mac);
 	}
 
 	if (tb[IFLA_BOND_XMIT_HASH_POLICY]) {
 		const char *xmit_hash_policy = get_name(xmit_hash_policy_tbl,
-							rta_getattr_u8(tb[IFLA_BOND_XMIT_HASH_POLICY]));
-		print_string(PRINT_ANY,
-			     "xmit_hash_policy",
-			     "xmit_hash_policy %s ",
-			     xmit_hash_policy);
+			rta_getattr_u8(tb[IFLA_BOND_XMIT_HASH_POLICY]));
+		fprintf(f, "xmit_hash_policy %s ", xmit_hash_policy);
 	}
 
 	if (tb[IFLA_BOND_RESEND_IGMP])
-		print_uint(PRINT_ANY,
-			   "resend_igmp",
-			   "resend_igmp %u ",
-			   rta_getattr_u32(tb[IFLA_BOND_RESEND_IGMP]));
+		fprintf(f, "resend_igmp %u ",
+			rta_getattr_u32(tb[IFLA_BOND_RESEND_IGMP]));
 
 	if (tb[IFLA_BOND_NUM_PEER_NOTIF])
-		print_uint(PRINT_ANY,
-			   "num_peer_notif",
-			   "num_grat_arp %u ",
-			   rta_getattr_u8(tb[IFLA_BOND_NUM_PEER_NOTIF]));
+		fprintf(f, "num_grat_arp %u ",
+			rta_getattr_u8(tb[IFLA_BOND_NUM_PEER_NOTIF]));
 
 	if (tb[IFLA_BOND_ALL_SLAVES_ACTIVE])
-		print_uint(PRINT_ANY,
-			   "all_slaves_active",
-			   "all_slaves_active %u ",
-			   rta_getattr_u8(tb[IFLA_BOND_ALL_SLAVES_ACTIVE]));
+		fprintf(f, "all_slaves_active %u ",
+			rta_getattr_u8(tb[IFLA_BOND_ALL_SLAVES_ACTIVE]));
 
 	if (tb[IFLA_BOND_MIN_LINKS])
-		print_uint(PRINT_ANY,
-			   "min_links",
-			   "min_links %u ",
-			   rta_getattr_u32(tb[IFLA_BOND_MIN_LINKS]));
+		fprintf(f, "min_links %u ",
+			rta_getattr_u32(tb[IFLA_BOND_MIN_LINKS]));
 
 	if (tb[IFLA_BOND_LP_INTERVAL])
-		print_uint(PRINT_ANY,
-			   "lp_interval",
-			   "lp_interval %u ",
-			   rta_getattr_u32(tb[IFLA_BOND_LP_INTERVAL]));
+		fprintf(f, "lp_interval %u ",
+			rta_getattr_u32(tb[IFLA_BOND_LP_INTERVAL]));
 
 	if (tb[IFLA_BOND_PACKETS_PER_SLAVE])
-		print_uint(PRINT_ANY,
-			   "packets_per_slave",
-			   "packets_per_slave %u ",
-			   rta_getattr_u32(tb[IFLA_BOND_PACKETS_PER_SLAVE]));
+		fprintf(f, "packets_per_slave %u ",
+			rta_getattr_u32(tb[IFLA_BOND_PACKETS_PER_SLAVE]));
 
 	if (tb[IFLA_BOND_AD_LACP_RATE]) {
 		const char *lacp_rate = get_name(lacp_rate_tbl,
-						 rta_getattr_u8(tb[IFLA_BOND_AD_LACP_RATE]));
-		print_string(PRINT_ANY,
-			     "ad_lacp_rate",
-			     "lacp_rate %s ",
-			     lacp_rate);
+			rta_getattr_u8(tb[IFLA_BOND_AD_LACP_RATE]));
+		fprintf(f, "lacp_rate %s ", lacp_rate);
 	}
 
 	if (tb[IFLA_BOND_AD_SELECT]) {
 		const char *ad_select = get_name(ad_select_tbl,
-						 rta_getattr_u8(tb[IFLA_BOND_AD_SELECT]));
-		print_string(PRINT_ANY,
-			     "ad_select",
-			     "ad_select %s ",
-			     ad_select);
+			rta_getattr_u8(tb[IFLA_BOND_AD_SELECT]));
+		fprintf(f, "ad_select %s ", ad_select);
 	}
 
 	if (tb[IFLA_BOND_AD_INFO]) {
 		struct rtattr *adtb[IFLA_BOND_AD_INFO_MAX + 1];
 
 		parse_rtattr_nested(adtb, IFLA_BOND_AD_INFO_MAX,
-				    tb[IFLA_BOND_AD_INFO]);
-
-		open_json_object("ad_info");
+			tb[IFLA_BOND_AD_INFO]);
 
 		if (adtb[IFLA_BOND_AD_INFO_AGGREGATOR])
-			print_int(PRINT_ANY,
-				  "aggregator",
-				  "ad_aggregator %d ",
-				  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_AGGREGATOR]));
+			fprintf(f, "ad_aggregator %d ",
+			  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_AGGREGATOR]));
 
 		if (adtb[IFLA_BOND_AD_INFO_NUM_PORTS])
-			print_int(PRINT_ANY,
-				  "num_ports",
-				  "ad_num_ports %d ",
-				  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_NUM_PORTS]));
+			fprintf(f, "ad_num_ports %d ",
+			  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_NUM_PORTS]));
 
 		if (adtb[IFLA_BOND_AD_INFO_ACTOR_KEY])
-			print_int(PRINT_ANY,
-				  "actor_key",
-				  "ad_actor_key %d ",
-				  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_ACTOR_KEY]));
+			fprintf(f, "ad_actor_key %d ",
+			  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_ACTOR_KEY]));
 
 		if (adtb[IFLA_BOND_AD_INFO_PARTNER_KEY])
-			print_int(PRINT_ANY,
-				  "partner_key",
-				  "ad_partner_key %d ",
-				  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_PARTNER_KEY]));
+			fprintf(f, "ad_partner_key %d ",
+			  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_PARTNER_KEY]));
 
 		if (adtb[IFLA_BOND_AD_INFO_PARTNER_MAC]) {
 			unsigned char *p =
 				RTA_DATA(adtb[IFLA_BOND_AD_INFO_PARTNER_MAC]);
 			SPRINT_BUF(b);
-			print_string(PRINT_ANY,
-				     "partner_mac",
-				     "ad_partner_mac %s ",
-				     ll_addr_n2a(p, ETH_ALEN, 0, b, sizeof(b)));
+			fprintf(f, "ad_partner_mac %s ",
+				ll_addr_n2a(p, ETH_ALEN, 0, b, sizeof(b)));
 		}
-
-		close_json_object();
 	}
 
 	if (tb[IFLA_BOND_AD_ACTOR_SYS_PRIO]) {
-		print_uint(PRINT_ANY,
-			   "ad_actor_sys_prio",
-			   "ad_actor_sys_prio %u ",
-			   rta_getattr_u16(tb[IFLA_BOND_AD_ACTOR_SYS_PRIO]));
+		fprintf(f, "ad_actor_sys_prio %u ",
+			rta_getattr_u16(tb[IFLA_BOND_AD_ACTOR_SYS_PRIO]));
 	}
 
 	if (tb[IFLA_BOND_AD_USER_PORT_KEY]) {
-		print_uint(PRINT_ANY,
-			   "ad_user_port_key",
-			   "ad_user_port_key %u ",
-			   rta_getattr_u16(tb[IFLA_BOND_AD_USER_PORT_KEY]));
+		fprintf(f, "ad_user_port_key %u ",
+			rta_getattr_u16(tb[IFLA_BOND_AD_USER_PORT_KEY]));
 	}
 
 	if (tb[IFLA_BOND_AD_ACTOR_SYSTEM]) {
 		/* We assume the l2 address is an Ethernet MAC address */
 		SPRINT_BUF(b1);
-
-		print_string(PRINT_ANY,
-			     "ad_actor_system",
-			     "ad_actor_system %s ",
-			     ll_addr_n2a(RTA_DATA(tb[IFLA_BOND_AD_ACTOR_SYSTEM]),
-					 RTA_PAYLOAD(tb[IFLA_BOND_AD_ACTOR_SYSTEM]),
-					 1 /*ARPHDR_ETHER*/, b1, sizeof(b1)));
+		fprintf(f, "ad_actor_system %s ",
+			ll_addr_n2a(RTA_DATA(tb[IFLA_BOND_AD_ACTOR_SYSTEM]),
+				    RTA_PAYLOAD(tb[IFLA_BOND_AD_ACTOR_SYSTEM]),
+				    1 /*ARPHDR_ETHER*/, b1, sizeof(b1)));
 	}
 
 	if (tb[IFLA_BOND_TLB_DYNAMIC_LB]) {
-		print_uint(PRINT_ANY,
-			   "tlb_dynamic_lb",
-			   "tlb_dynamic_lb %u ",
-			   rta_getattr_u8(tb[IFLA_BOND_TLB_DYNAMIC_LB]));
+		fprintf(f, "tlb_dynamic_lb %u ",
+			rta_getattr_u8(tb[IFLA_BOND_TLB_DYNAMIC_LB]));
 	}
 }
 
 static void bond_print_help(struct link_util *lu, int argc, char **argv,
-			    FILE *f)
+	FILE *f)
 {
 	print_explain(f);
 }
 
-static void bond_print_xstats_help(struct link_util *lu, FILE *f)
-{
-	fprintf(f, "Usage: ... %s [ 802.3ad ] [ dev DEVICE ]\n", lu->id);
-}
-
-static void bond_print_3ad_stats(struct rtattr *lacpattr)
-{
-	struct rtattr *lacptb[BOND_3AD_STAT_MAX+1];
-	__u64 val;
-
-	parse_rtattr(lacptb, BOND_3AD_STAT_MAX, RTA_DATA(lacpattr),
-		     RTA_PAYLOAD(lacpattr));
-	open_json_object("802.3ad");
-	if (lacptb[BOND_3AD_STAT_LACPDU_RX]) {
-		print_string(PRINT_FP, NULL, "%-16s    ", "");
-		print_u64(PRINT_ANY, "lacpdu_rx", "LACPDU Rx %llu\n",
-			  rta_getattr_u64(lacptb[BOND_3AD_STAT_LACPDU_RX]));
-	}
-	if (lacptb[BOND_3AD_STAT_LACPDU_TX]) {
-		print_string(PRINT_FP, NULL, "%-16s    ", "");
-		print_u64(PRINT_ANY, "lacpdu_tx", "LACPDU Tx %llu\n",
-			  rta_getattr_u64(lacptb[BOND_3AD_STAT_LACPDU_TX]));
-	}
-	if (lacptb[BOND_3AD_STAT_LACPDU_UNKNOWN_RX]) {
-		print_string(PRINT_FP, NULL, "%-16s    ", "");
-		val = rta_getattr_u64(lacptb[BOND_3AD_STAT_LACPDU_UNKNOWN_RX]);
-		print_u64(PRINT_ANY,
-			  "lacpdu_unknown_rx",
-			  "LACPDU Unknown type Rx %llu\n",
-			  val);
-	}
-	if (lacptb[BOND_3AD_STAT_LACPDU_ILLEGAL_RX]) {
-		print_string(PRINT_FP, NULL, "%-16s    ", "");
-		val = rta_getattr_u64(lacptb[BOND_3AD_STAT_LACPDU_ILLEGAL_RX]);
-		print_u64(PRINT_ANY,
-			  "lacpdu_illegal_rx",
-			  "LACPDU Illegal Rx %llu\n",
-			  val);
-	}
-	if (lacptb[BOND_3AD_STAT_MARKER_RX]) {
-		print_string(PRINT_FP, NULL, "%-16s    ", "");
-		print_u64(PRINT_ANY, "marker_rx", "Marker Rx %llu\n",
-			  rta_getattr_u64(lacptb[BOND_3AD_STAT_MARKER_RX]));
-	}
-	if (lacptb[BOND_3AD_STAT_MARKER_TX]) {
-		print_string(PRINT_FP, NULL, "%-16s    ", "");
-		print_u64(PRINT_ANY, "marker_tx", "Marker Tx %llu\n",
-			  rta_getattr_u64(lacptb[BOND_3AD_STAT_MARKER_TX]));
-	}
-	if (lacptb[BOND_3AD_STAT_MARKER_RESP_RX]) {
-		print_string(PRINT_FP, NULL, "%-16s    ", "");
-		val = rta_getattr_u64(lacptb[BOND_3AD_STAT_MARKER_RESP_RX]);
-		print_u64(PRINT_ANY,
-			  "marker_response_rx",
-			  "Marker response Rx %llu\n",
-			  val);
-	}
-	if (lacptb[BOND_3AD_STAT_MARKER_RESP_TX]) {
-		print_string(PRINT_FP, NULL, "%-16s    ", "");
-		val = rta_getattr_u64(lacptb[BOND_3AD_STAT_MARKER_RESP_TX]);
-		print_u64(PRINT_ANY,
-			  "marker_response_tx",
-			  "Marker response Tx %llu\n",
-			  val);
-	}
-	if (lacptb[BOND_3AD_STAT_MARKER_UNKNOWN_RX]) {
-		print_string(PRINT_FP, NULL, "%-16s    ", "");
-		val = rta_getattr_u64(lacptb[BOND_3AD_STAT_MARKER_UNKNOWN_RX]);
-		print_u64(PRINT_ANY,
-			  "marker_unknown_rx",
-			  "Marker unknown type Rx %llu\n",
-			  val);
-	}
-	close_json_object();
-}
-
-static void bond_print_stats_attr(struct rtattr *attr, int ifindex)
-{
-	struct rtattr *bondtb[LINK_XSTATS_TYPE_MAX+1];
-	struct rtattr *i, *list;
-	const char *ifname = "";
-	int rem;
-
-	parse_rtattr(bondtb, LINK_XSTATS_TYPE_MAX+1, RTA_DATA(attr),
-	RTA_PAYLOAD(attr));
-	if (!bondtb[LINK_XSTATS_TYPE_BOND])
-		return;
-
-	list = bondtb[LINK_XSTATS_TYPE_BOND];
-	rem = RTA_PAYLOAD(list);
-	open_json_object(NULL);
-	ifname = ll_index_to_name(ifindex);
-	print_string(PRINT_ANY, "ifname", "%-16s\n", ifname);
-	for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
-		if (xstats_print_attr && i->rta_type != xstats_print_attr)
-			continue;
-
-		switch (i->rta_type) {
-		case BOND_XSTATS_3AD:
-			bond_print_3ad_stats(i);
-			break;
-		}
-		break;
-	}
-	close_json_object();
-}
-
-int bond_print_xstats(struct nlmsghdr *n, void *arg)
-{
-	struct if_stats_msg *ifsm = NLMSG_DATA(n);
-	struct rtattr *tb[IFLA_STATS_MAX+1];
-	int len = n->nlmsg_len;
-
-	len -= NLMSG_LENGTH(sizeof(*ifsm));
-	if (len < 0) {
-		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
-		return -1;
-	}
-	if (filter_index && filter_index != ifsm->ifindex)
-		return 0;
-
-	parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
-	if (tb[IFLA_STATS_LINK_XSTATS])
-		bond_print_stats_attr(tb[IFLA_STATS_LINK_XSTATS],
-				      ifsm->ifindex);
-
-	if (tb[IFLA_STATS_LINK_XSTATS_SLAVE])
-		bond_print_stats_attr(tb[IFLA_STATS_LINK_XSTATS_SLAVE],
-				      ifsm->ifindex);
-
-	return 0;
-}
-
-int bond_parse_xstats(struct link_util *lu, int argc, char **argv)
-{
-	while (argc > 0) {
-		if (strcmp(*argv, "lacp") == 0 ||
-		    strcmp(*argv, "802.3ad") == 0) {
-			xstats_print_attr = BOND_XSTATS_3AD;
-		} else if (strcmp(*argv, "dev") == 0) {
-			NEXT_ARG();
-			filter_index = ll_name_to_index(*argv);
-			if (!filter_index)
-				return nodev(*argv);
-		} else if (strcmp(*argv, "help") == 0) {
-			bond_print_xstats_help(lu, stdout);
-			exit(0);
-		} else {
-			invarg("unknown attribute", *argv);
-		}
-		argc--; argv++;
-	}
-
-	return 0;
-}
-
-
 struct link_util bond_link_util = {
 	.id		= "bond",
 	.maxattr	= IFLA_BOND_MAX,
 	.parse_opt	= bond_parse_opt,
 	.print_opt	= bond_print_opt,
 	.print_help	= bond_print_help,
-	.parse_ifla_xstats = bond_parse_xstats,
-	.print_ifla_xstats = bond_print_xstats,
 };
diff --git a/ip/iplink_bond_slave.c b/ip/iplink_bond_slave.c
index 4eaf72b..9b569b1 100644
--- a/ip/iplink_bond_slave.c
+++ b/ip/iplink_bond_slave.c
@@ -17,16 +17,6 @@
 #include "utils.h"
 #include "ip_common.h"
 
-static void print_explain(FILE *f)
-{
-	fprintf(f, "Usage: ... bond_slave [ queue_id ID ]\n");
-}
-
-static void explain(void)
-{
-	print_explain(stderr);
-}
-
 static const char *slave_states[] = {
 	[BOND_STATE_ACTIVE] = "ACTIVE",
 	[BOND_STATE_BACKUP] = "BACKUP",
@@ -36,13 +26,10 @@
 {
 	unsigned int state = rta_getattr_u8(tb);
 
-	if (state >= ARRAY_SIZE(slave_states))
-		print_int(PRINT_ANY, "state_index", "state %d ", state);
+	if (state >= sizeof(slave_states) / sizeof(slave_states[0]))
+		fprintf(f, "state %d ", state);
 	else
-		print_string(PRINT_ANY,
-			     "state",
-			     "state %s ",
-			     slave_states[state]);
+		fprintf(f, "state %s ", slave_states[state]);
 }
 
 static const char *slave_mii_status[] = {
@@ -56,16 +43,10 @@
 {
 	unsigned int status = rta_getattr_u8(tb);
 
-	if (status >= ARRAY_SIZE(slave_mii_status))
-		print_int(PRINT_ANY,
-			  "mii_status_index",
-			  "mii_status %d ",
-			  status);
+	if (status >= sizeof(slave_mii_status) / sizeof(slave_mii_status[0]))
+		fprintf(f, "mii_status %d ", status);
 	else
-		print_string(PRINT_ANY,
-			     "mii_status",
-			     "mii_status %s ",
-			     slave_mii_status[status]);
+		fprintf(f, "mii_status %s ", slave_mii_status[status]);
 }
 
 static void bond_slave_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
@@ -81,42 +62,30 @@
 		print_slave_mii_status(f, tb[IFLA_BOND_SLAVE_MII_STATUS]);
 
 	if (tb[IFLA_BOND_SLAVE_LINK_FAILURE_COUNT])
-		print_int(PRINT_ANY,
-			  "link_failure_count",
-			  "link_failure_count %d ",
-			  rta_getattr_u32(tb[IFLA_BOND_SLAVE_LINK_FAILURE_COUNT]));
+		fprintf(f, "link_failure_count %d ",
+			rta_getattr_u32(tb[IFLA_BOND_SLAVE_LINK_FAILURE_COUNT]));
 
 	if (tb[IFLA_BOND_SLAVE_PERM_HWADDR])
-		print_string(PRINT_ANY,
-			     "perm_hwaddr",
-			     "perm_hwaddr %s ",
-			     ll_addr_n2a(RTA_DATA(tb[IFLA_BOND_SLAVE_PERM_HWADDR]),
-					 RTA_PAYLOAD(tb[IFLA_BOND_SLAVE_PERM_HWADDR]),
-					 0, b1, sizeof(b1)));
+		fprintf(f, "perm_hwaddr %s ",
+			ll_addr_n2a(RTA_DATA(tb[IFLA_BOND_SLAVE_PERM_HWADDR]),
+				    RTA_PAYLOAD(tb[IFLA_BOND_SLAVE_PERM_HWADDR]),
+				    0, b1, sizeof(b1)));
 
 	if (tb[IFLA_BOND_SLAVE_QUEUE_ID])
-		print_int(PRINT_ANY,
-			  "queue_id",
-			  "queue_id %d ",
-			  rta_getattr_u16(tb[IFLA_BOND_SLAVE_QUEUE_ID]));
+		fprintf(f, "queue_id %d ",
+			rta_getattr_u16(tb[IFLA_BOND_SLAVE_QUEUE_ID]));
 
 	if (tb[IFLA_BOND_SLAVE_AD_AGGREGATOR_ID])
-		print_int(PRINT_ANY,
-			  "ad_aggregator_id",
-			  "ad_aggregator_id %d ",
-			  rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_AGGREGATOR_ID]));
+		fprintf(f, "ad_aggregator_id %d ",
+			rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_AGGREGATOR_ID]));
 
 	if (tb[IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE])
-		print_int(PRINT_ANY,
-			  "ad_actor_oper_port_state",
-			  "ad_actor_oper_port_state %d ",
-			  rta_getattr_u8(tb[IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE]));
+		fprintf(f, "ad_actor_oper_port_state %d\n",
+			rta_getattr_u8(tb[IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE]));
 
 	if (tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE])
-		print_int(PRINT_ANY,
-			  "ad_partner_oper_port_state",
-			  "ad_partner_oper_port_state %d ",
-			  rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE]));
+		fprintf(f, "ad_partner_oper_port_state %d\n",
+			rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE]));
 }
 
 static int bond_slave_parse_opt(struct link_util *lu, int argc, char **argv,
@@ -130,13 +99,6 @@
 			if (get_u16(&queue_id, *argv, 0))
 				invarg("queue_id is invalid", *argv);
 			addattr16(n, 1024, IFLA_BOND_SLAVE_QUEUE_ID, queue_id);
-		} else {
-			if (matches(*argv, "help") != 0)
-				fprintf(stderr,
-					"bond_slave: unknown option \"%s\"?\n",
-					*argv);
-			explain();
-			return -1;
 		}
 		argc--, argv++;
 	}
@@ -144,18 +106,10 @@
 	return 0;
 }
 
-static void bond_slave_print_help(struct link_util *lu, int argc, char **argv,
-				  FILE *f)
-{
-	print_explain(f);
-}
-
 struct link_util bond_slave_link_util = {
-	.id		= "bond_slave",
+	.id		= "bond",
 	.maxattr	= IFLA_BOND_SLAVE_MAX,
 	.print_opt	= bond_slave_print_opt,
 	.parse_opt	= bond_slave_parse_opt,
-	.print_help	= bond_slave_print_help,
-	.parse_ifla_xstats = bond_parse_xstats,
-	.print_ifla_xstats = bond_print_xstats,
+	.slave		= true,
 };
diff --git a/ip/iplink_bridge.c b/ip/iplink_bridge.c
index 06f736d..0080409 100644
--- a/ip/iplink_bridge.c
+++ b/ip/iplink_bridge.c
@@ -12,56 +12,23 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <netinet/in.h>
-#include <netinet/ether.h>
 #include <linux/if_link.h>
-#include <linux/if_bridge.h>
-#include <net/if.h>
 
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
 
-static unsigned int xstats_print_attr;
-static int filter_index;
-
 static void print_explain(FILE *f)
 {
 	fprintf(f,
-		"Usage: ... bridge [ fdb_flush ]\n"
-		"		  [ forward_delay FORWARD_DELAY ]\n"
-		"		  [ hello_time HELLO_TIME ]\n"
-		"		  [ max_age MAX_AGE ]\n"
-		"		  [ ageing_time AGEING_TIME ]\n"
-		"		  [ stp_state STP_STATE ]\n"
-		"		  [ priority PRIORITY ]\n"
-		"		  [ group_fwd_mask MASK ]\n"
-		"		  [ group_address ADDRESS ]\n"
-		"		  [ vlan_filtering VLAN_FILTERING ]\n"
-		"		  [ vlan_protocol VLAN_PROTOCOL ]\n"
-		"		  [ vlan_default_pvid VLAN_DEFAULT_PVID ]\n"
-		"		  [ vlan_stats_enabled VLAN_STATS_ENABLED ]\n"
-		"		  [ vlan_stats_per_port VLAN_STATS_PER_PORT ]\n"
-		"		  [ mcast_snooping MULTICAST_SNOOPING ]\n"
-		"		  [ mcast_router MULTICAST_ROUTER ]\n"
-		"		  [ mcast_query_use_ifaddr MCAST_QUERY_USE_IFADDR ]\n"
-		"		  [ mcast_querier MULTICAST_QUERIER ]\n"
-		"		  [ mcast_hash_elasticity HASH_ELASTICITY ]\n"
-		"		  [ mcast_hash_max HASH_MAX ]\n"
-		"		  [ mcast_last_member_count LAST_MEMBER_COUNT ]\n"
-		"		  [ mcast_startup_query_count STARTUP_QUERY_COUNT ]\n"
-		"		  [ mcast_last_member_interval LAST_MEMBER_INTERVAL ]\n"
-		"		  [ mcast_membership_interval MEMBERSHIP_INTERVAL ]\n"
-		"		  [ mcast_querier_interval QUERIER_INTERVAL ]\n"
-		"		  [ mcast_query_interval QUERY_INTERVAL ]\n"
-		"		  [ mcast_query_response_interval QUERY_RESPONSE_INTERVAL ]\n"
-		"		  [ mcast_startup_query_interval STARTUP_QUERY_INTERVAL ]\n"
-		"		  [ mcast_stats_enabled MCAST_STATS_ENABLED ]\n"
-		"		  [ mcast_igmp_version IGMP_VERSION ]\n"
-		"		  [ mcast_mld_version MLD_VERSION ]\n"
-		"		  [ nf_call_iptables NF_CALL_IPTABLES ]\n"
-		"		  [ nf_call_ip6tables NF_CALL_IP6TABLES ]\n"
-		"		  [ nf_call_arptables NF_CALL_ARPTABLES ]\n"
+		"Usage: ... bridge [ forward_delay FORWARD_DELAY ]\n"
+		"                  [ hello_time HELLO_TIME ]\n"
+		"                  [ max_age MAX_AGE ]\n"
+		"                  [ ageing_time AGEING_TIME ]\n"
+		"                  [ stp_state STP_STATE ]\n"
+		"                  [ priority PRIORITY ]\n"
+		"                  [ vlan_filtering VLAN_FILTERING ]\n"
+		"                  [ vlan_protocol VLAN_PROTOCOL ]\n"
 		"\n"
 		"Where: VLAN_PROTOCOL := { 802.1Q | 802.1ad }\n"
 	);
@@ -72,14 +39,6 @@
 	print_explain(stderr);
 }
 
-void br_dump_bridge_id(const struct ifla_bridge_id *id, char *buf, size_t len)
-{
-	char eaddr[32];
-
-	ether_ntoa_r((const struct ether_addr *)id->addr, eaddr);
-	snprintf(buf, len, "%.2x%.2x.%s", id->prio[0], id->prio[1], eaddr);
-}
-
 static int bridge_parse_opt(struct link_util *lu, int argc, char **argv,
 			    struct nlmsghdr *n)
 {
@@ -128,246 +87,20 @@
 			__u8 vlan_filter;
 
 			NEXT_ARG();
-			if (get_u8(&vlan_filter, *argv, 0))
+			if (get_u8(&vlan_filter, *argv, 0)) {
 				invarg("invalid vlan_filtering", *argv);
-
+				return -1;
+			}
 			addattr8(n, 1024, IFLA_BR_VLAN_FILTERING, vlan_filter);
 		} else if (matches(*argv, "vlan_protocol") == 0) {
 			__u16 vlan_proto;
 
 			NEXT_ARG();
-			if (ll_proto_a2n(&vlan_proto, *argv))
+			if (ll_proto_a2n(&vlan_proto, *argv)) {
 				invarg("invalid vlan_protocol", *argv);
-
-			addattr16(n, 1024, IFLA_BR_VLAN_PROTOCOL, vlan_proto);
-		} else if (matches(*argv, "group_fwd_mask") == 0) {
-			__u16 fwd_mask;
-
-			NEXT_ARG();
-			if (get_u16(&fwd_mask, *argv, 0))
-				invarg("invalid group_fwd_mask", *argv);
-
-			addattr16(n, 1024, IFLA_BR_GROUP_FWD_MASK, fwd_mask);
-		} else if (matches(*argv, "group_address") == 0) {
-			char llabuf[32];
-			int len;
-
-			NEXT_ARG();
-			len = ll_addr_a2n(llabuf, sizeof(llabuf), *argv);
-			if (len < 0)
 				return -1;
-			addattr_l(n, 1024, IFLA_BR_GROUP_ADDR, llabuf, len);
-		} else if (matches(*argv, "fdb_flush") == 0) {
-			addattr(n, 1024, IFLA_BR_FDB_FLUSH);
-		} else if (matches(*argv, "vlan_default_pvid") == 0) {
-			__u16 default_pvid;
-
-			NEXT_ARG();
-			if (get_u16(&default_pvid, *argv, 0))
-				invarg("invalid vlan_default_pvid", *argv);
-
-			addattr16(n, 1024, IFLA_BR_VLAN_DEFAULT_PVID,
-				  default_pvid);
-		} else if (matches(*argv, "vlan_stats_enabled") == 0) {
-			__u8 vlan_stats_enabled;
-
-			NEXT_ARG();
-			if (get_u8(&vlan_stats_enabled, *argv, 0))
-				invarg("invalid vlan_stats_enabled", *argv);
-			addattr8(n, 1024, IFLA_BR_VLAN_STATS_ENABLED,
-				  vlan_stats_enabled);
-		} else if (matches(*argv, "vlan_stats_per_port") == 0) {
-			__u8 vlan_stats_per_port;
-
-			NEXT_ARG();
-			if (get_u8(&vlan_stats_per_port, *argv, 0))
-				invarg("invalid vlan_stats_per_port", *argv);
-			addattr8(n, 1024, IFLA_BR_VLAN_STATS_PER_PORT,
-				 vlan_stats_per_port);
-		} else if (matches(*argv, "mcast_router") == 0) {
-			__u8 mcast_router;
-
-			NEXT_ARG();
-			if (get_u8(&mcast_router, *argv, 0))
-				invarg("invalid mcast_router", *argv);
-
-			addattr8(n, 1024, IFLA_BR_MCAST_ROUTER, mcast_router);
-		} else if (matches(*argv, "mcast_snooping") == 0) {
-			__u8 mcast_snoop;
-
-			NEXT_ARG();
-			if (get_u8(&mcast_snoop, *argv, 0))
-				invarg("invalid mcast_snooping", *argv);
-
-			addattr8(n, 1024, IFLA_BR_MCAST_SNOOPING, mcast_snoop);
-		} else if (matches(*argv, "mcast_query_use_ifaddr") == 0) {
-			__u8 mcast_qui;
-
-			NEXT_ARG();
-			if (get_u8(&mcast_qui, *argv, 0))
-				invarg("invalid mcast_query_use_ifaddr",
-				       *argv);
-
-			addattr8(n, 1024, IFLA_BR_MCAST_QUERY_USE_IFADDR,
-				 mcast_qui);
-		} else if (matches(*argv, "mcast_querier") == 0) {
-			__u8 mcast_querier;
-
-			NEXT_ARG();
-			if (get_u8(&mcast_querier, *argv, 0))
-				invarg("invalid mcast_querier", *argv);
-
-			addattr8(n, 1024, IFLA_BR_MCAST_QUERIER, mcast_querier);
-		} else if (matches(*argv, "mcast_hash_elasticity") == 0) {
-			__u32 mcast_hash_el;
-
-			NEXT_ARG();
-			if (get_u32(&mcast_hash_el, *argv, 0))
-				invarg("invalid mcast_hash_elasticity",
-				       *argv);
-
-			addattr32(n, 1024, IFLA_BR_MCAST_HASH_ELASTICITY,
-				  mcast_hash_el);
-		} else if (matches(*argv, "mcast_hash_max") == 0) {
-			__u32 mcast_hash_max;
-
-			NEXT_ARG();
-			if (get_u32(&mcast_hash_max, *argv, 0))
-				invarg("invalid mcast_hash_max", *argv);
-
-			addattr32(n, 1024, IFLA_BR_MCAST_HASH_MAX,
-				  mcast_hash_max);
-		} else if (matches(*argv, "mcast_last_member_count") == 0) {
-			__u32 mcast_lmc;
-
-			NEXT_ARG();
-			if (get_u32(&mcast_lmc, *argv, 0))
-				invarg("invalid mcast_last_member_count",
-				       *argv);
-
-			addattr32(n, 1024, IFLA_BR_MCAST_LAST_MEMBER_CNT,
-				  mcast_lmc);
-		} else if (matches(*argv, "mcast_startup_query_count") == 0) {
-			__u32 mcast_sqc;
-
-			NEXT_ARG();
-			if (get_u32(&mcast_sqc, *argv, 0))
-				invarg("invalid mcast_startup_query_count",
-				       *argv);
-
-			addattr32(n, 1024, IFLA_BR_MCAST_STARTUP_QUERY_CNT,
-				  mcast_sqc);
-		} else if (matches(*argv, "mcast_last_member_interval") == 0) {
-			__u64 mcast_last_member_intvl;
-
-			NEXT_ARG();
-			if (get_u64(&mcast_last_member_intvl, *argv, 0))
-				invarg("invalid mcast_last_member_interval",
-				       *argv);
-
-			addattr64(n, 1024, IFLA_BR_MCAST_LAST_MEMBER_INTVL,
-				  mcast_last_member_intvl);
-		} else if (matches(*argv, "mcast_membership_interval") == 0) {
-			__u64 mcast_membership_intvl;
-
-			NEXT_ARG();
-			if (get_u64(&mcast_membership_intvl, *argv, 0))
-				invarg("invalid mcast_membership_interval",
-				       *argv);
-
-			addattr64(n, 1024, IFLA_BR_MCAST_MEMBERSHIP_INTVL,
-				  mcast_membership_intvl);
-		} else if (matches(*argv, "mcast_querier_interval") == 0) {
-			__u64 mcast_querier_intvl;
-
-			NEXT_ARG();
-			if (get_u64(&mcast_querier_intvl, *argv, 0))
-				invarg("invalid mcast_querier_interval",
-				       *argv);
-
-			addattr64(n, 1024, IFLA_BR_MCAST_QUERIER_INTVL,
-				  mcast_querier_intvl);
-		} else if (matches(*argv, "mcast_query_interval") == 0) {
-			__u64 mcast_query_intvl;
-
-			NEXT_ARG();
-			if (get_u64(&mcast_query_intvl, *argv, 0))
-				invarg("invalid mcast_query_interval",
-				       *argv);
-
-			addattr64(n, 1024, IFLA_BR_MCAST_QUERY_INTVL,
-				  mcast_query_intvl);
-		} else if (!matches(*argv, "mcast_query_response_interval")) {
-			__u64 mcast_query_resp_intvl;
-
-			NEXT_ARG();
-			if (get_u64(&mcast_query_resp_intvl, *argv, 0))
-				invarg("invalid mcast_query_response_interval",
-				       *argv);
-
-			addattr64(n, 1024, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
-				  mcast_query_resp_intvl);
-		} else if (!matches(*argv, "mcast_startup_query_interval")) {
-			__u64 mcast_startup_query_intvl;
-
-			NEXT_ARG();
-			if (get_u64(&mcast_startup_query_intvl, *argv, 0))
-				invarg("invalid mcast_startup_query_interval",
-				       *argv);
-
-			addattr64(n, 1024, IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
-				  mcast_startup_query_intvl);
-		} else if (matches(*argv, "mcast_stats_enabled") == 0) {
-			__u8 mcast_stats_enabled;
-
-			NEXT_ARG();
-			if (get_u8(&mcast_stats_enabled, *argv, 0))
-				invarg("invalid mcast_stats_enabled", *argv);
-			addattr8(n, 1024, IFLA_BR_MCAST_STATS_ENABLED,
-				  mcast_stats_enabled);
-		} else if (matches(*argv, "mcast_igmp_version") == 0) {
-			__u8 igmp_version;
-
-			NEXT_ARG();
-			if (get_u8(&igmp_version, *argv, 0))
-				invarg("invalid mcast_igmp_version", *argv);
-			addattr8(n, 1024, IFLA_BR_MCAST_IGMP_VERSION,
-				  igmp_version);
-		} else if (matches(*argv, "mcast_mld_version") == 0) {
-			__u8 mld_version;
-
-			NEXT_ARG();
-			if (get_u8(&mld_version, *argv, 0))
-				invarg("invalid mcast_mld_version", *argv);
-			addattr8(n, 1024, IFLA_BR_MCAST_MLD_VERSION,
-				  mld_version);
-		} else if (matches(*argv, "nf_call_iptables") == 0) {
-			__u8 nf_call_ipt;
-
-			NEXT_ARG();
-			if (get_u8(&nf_call_ipt, *argv, 0))
-				invarg("invalid nf_call_iptables", *argv);
-
-			addattr8(n, 1024, IFLA_BR_NF_CALL_IPTABLES,
-				 nf_call_ipt);
-		} else if (matches(*argv, "nf_call_ip6tables") == 0) {
-			__u8 nf_call_ip6t;
-
-			NEXT_ARG();
-			if (get_u8(&nf_call_ip6t, *argv, 0))
-				invarg("invalid nf_call_ip6tables", *argv);
-
-			addattr8(n, 1024, IFLA_BR_NF_CALL_IP6TABLES,
-				 nf_call_ip6t);
-		} else if (matches(*argv, "nf_call_arptables") == 0) {
-			__u8 nf_call_arpt;
-
-			NEXT_ARG();
-			if (get_u8(&nf_call_arpt, *argv, 0))
-				invarg("invalid nf_call_arptables", *argv);
-
-			addattr8(n, 1024, IFLA_BR_NF_CALL_ARPTABLES,
-				 nf_call_arpt);
+			}
+			addattr16(n, 1024, IFLA_BR_VLAN_PROTOCOL, vlan_proto);
 		} else if (matches(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -382,490 +115,58 @@
 	return 0;
 }
 
-static void _bridge_print_timer(FILE *f,
-				const char *attr,
-				struct rtattr *timer)
-{
-	struct timeval tv;
-
-	__jiffies_to_tv(&tv, rta_getattr_u64(timer));
-	if (is_json_context()) {
-		json_writer_t *jw = get_json_writer();
-
-		jsonw_name(jw, attr);
-		jsonw_printf(jw, "%i.%.2i",
-			     (int)tv.tv_sec,
-			     (int)tv.tv_usec / 10000);
-	} else {
-		fprintf(f, "%s %4i.%.2i ", attr, (int)tv.tv_sec,
-			(int)tv.tv_usec / 10000);
-	}
-}
-
 static void bridge_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
 	if (!tb)
 		return;
 
 	if (tb[IFLA_BR_FORWARD_DELAY])
-		print_uint(PRINT_ANY,
-			   "forward_delay",
-			   "forward_delay %u ",
-			   rta_getattr_u32(tb[IFLA_BR_FORWARD_DELAY]));
+		fprintf(f, "forward_delay %u ",
+			rta_getattr_u32(tb[IFLA_BR_FORWARD_DELAY]));
 
 	if (tb[IFLA_BR_HELLO_TIME])
-		print_uint(PRINT_ANY,
-			   "hello_time",
-			   "hello_time %u ",
-			   rta_getattr_u32(tb[IFLA_BR_HELLO_TIME]));
+		fprintf(f, "hello_time %u ",
+			rta_getattr_u32(tb[IFLA_BR_HELLO_TIME]));
 
 	if (tb[IFLA_BR_MAX_AGE])
-		print_uint(PRINT_ANY,
-			   "max_age",
-			   "max_age %u ",
-			   rta_getattr_u32(tb[IFLA_BR_MAX_AGE]));
+		fprintf(f, "max_age %u ",
+			rta_getattr_u32(tb[IFLA_BR_MAX_AGE]));
 
 	if (tb[IFLA_BR_AGEING_TIME])
-		print_uint(PRINT_ANY,
-			   "ageing_time",
-			   "ageing_time %u ",
-			   rta_getattr_u32(tb[IFLA_BR_AGEING_TIME]));
+		fprintf(f, "ageing_time %u ",
+			rta_getattr_u32(tb[IFLA_BR_AGEING_TIME]));
 
 	if (tb[IFLA_BR_STP_STATE])
-		print_uint(PRINT_ANY,
-			   "stp_state",
-			   "stp_state %u ",
-			   rta_getattr_u32(tb[IFLA_BR_STP_STATE]));
+		fprintf(f, "stp_state %u ",
+			rta_getattr_u32(tb[IFLA_BR_STP_STATE]));
 
 	if (tb[IFLA_BR_PRIORITY])
-		print_uint(PRINT_ANY,
-			   "priority",
-			   "priority %u ",
-			   rta_getattr_u16(tb[IFLA_BR_PRIORITY]));
+		fprintf(f, "priority %u ",
+			rta_getattr_u16(tb[IFLA_BR_PRIORITY]));
 
 	if (tb[IFLA_BR_VLAN_FILTERING])
-		print_uint(PRINT_ANY,
-			   "vlan_filtering",
-			   "vlan_filtering %u ",
-			   rta_getattr_u8(tb[IFLA_BR_VLAN_FILTERING]));
+		fprintf(f, "vlan_filtering %u ",
+			rta_getattr_u8(tb[IFLA_BR_VLAN_FILTERING]));
 
 	if (tb[IFLA_BR_VLAN_PROTOCOL]) {
 		SPRINT_BUF(b1);
 
-		print_string(PRINT_ANY,
-			     "vlan_protocol",
-			     "vlan_protocol %s ",
-			     ll_proto_n2a(rta_getattr_u16(tb[IFLA_BR_VLAN_PROTOCOL]),
-					  b1, sizeof(b1)));
+		fprintf(f, "vlan_protocol %s ",
+			ll_proto_n2a(rta_getattr_u16(tb[IFLA_BR_VLAN_PROTOCOL]),
+				     b1, sizeof(b1)));
 	}
-
-	if (tb[IFLA_BR_BRIDGE_ID]) {
-		char bridge_id[32];
-
-		br_dump_bridge_id(RTA_DATA(tb[IFLA_BR_BRIDGE_ID]), bridge_id,
-				  sizeof(bridge_id));
-		print_string(PRINT_ANY,
-			     "bridge_id",
-			     "bridge_id %s ",
-			     bridge_id);
-	}
-
-	if (tb[IFLA_BR_ROOT_ID]) {
-		char root_id[32];
-
-		br_dump_bridge_id(RTA_DATA(tb[IFLA_BR_BRIDGE_ID]), root_id,
-				  sizeof(root_id));
-		print_string(PRINT_ANY,
-			     "root_id",
-			     "designated_root %s ",
-			     root_id);
-	}
-
-	if (tb[IFLA_BR_ROOT_PORT])
-		print_uint(PRINT_ANY,
-			   "root_port",
-			   "root_port %u ",
-			   rta_getattr_u16(tb[IFLA_BR_ROOT_PORT]));
-
-	if (tb[IFLA_BR_ROOT_PATH_COST])
-		print_uint(PRINT_ANY,
-			   "root_path_cost",
-			   "root_path_cost %u ",
-			   rta_getattr_u32(tb[IFLA_BR_ROOT_PATH_COST]));
-
-	if (tb[IFLA_BR_TOPOLOGY_CHANGE])
-		print_uint(PRINT_ANY,
-			   "topology_change",
-			   "topology_change %u ",
-			   rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE]));
-
-	if (tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED])
-		print_uint(PRINT_ANY,
-			   "topology_change_detected",
-			   "topology_change_detected %u ",
-			   rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED]));
-
-	if (tb[IFLA_BR_HELLO_TIMER])
-		_bridge_print_timer(f, "hello_timer", tb[IFLA_BR_HELLO_TIMER]);
-
-	if (tb[IFLA_BR_TCN_TIMER])
-		_bridge_print_timer(f, "tcn_timer", tb[IFLA_BR_TCN_TIMER]);
-
-	if (tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER])
-		_bridge_print_timer(f, "topology_change_timer",
-				    tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]);
-
-	if (tb[IFLA_BR_GC_TIMER])
-		_bridge_print_timer(f, "gc_timer", tb[IFLA_BR_GC_TIMER]);
-
-	if (tb[IFLA_BR_VLAN_DEFAULT_PVID])
-		print_uint(PRINT_ANY,
-			   "vlan_default_pvid",
-			   "vlan_default_pvid %u ",
-			   rta_getattr_u16(tb[IFLA_BR_VLAN_DEFAULT_PVID]));
-
-	if (tb[IFLA_BR_VLAN_STATS_ENABLED])
-		print_uint(PRINT_ANY,
-			   "vlan_stats_enabled",
-			   "vlan_stats_enabled %u ",
-			   rta_getattr_u8(tb[IFLA_BR_VLAN_STATS_ENABLED]));
-
-	if (tb[IFLA_BR_VLAN_STATS_PER_PORT])
-		print_uint(PRINT_ANY,
-			   "vlan_stats_per_port",
-			   "vlan_stats_per_port %u ",
-			   rta_getattr_u8(tb[IFLA_BR_VLAN_STATS_PER_PORT]));
-
-	if (tb[IFLA_BR_GROUP_FWD_MASK])
-		print_0xhex(PRINT_ANY,
-			    "group_fwd_mask",
-			    "group_fwd_mask %#llx ",
-			    rta_getattr_u16(tb[IFLA_BR_GROUP_FWD_MASK]));
-
-	if (tb[IFLA_BR_GROUP_ADDR]) {
-		SPRINT_BUF(mac);
-
-		print_string(PRINT_ANY,
-			     "group_addr",
-			     "group_address %s ",
-			     ll_addr_n2a(RTA_DATA(tb[IFLA_BR_GROUP_ADDR]),
-					 RTA_PAYLOAD(tb[IFLA_BR_GROUP_ADDR]),
-					 1 /*ARPHDR_ETHER*/, mac, sizeof(mac)));
-	}
-
-	if (tb[IFLA_BR_MCAST_SNOOPING])
-		print_uint(PRINT_ANY,
-			   "mcast_snooping",
-			   "mcast_snooping %u ",
-			   rta_getattr_u8(tb[IFLA_BR_MCAST_SNOOPING]));
-
-	if (tb[IFLA_BR_MCAST_ROUTER])
-		print_uint(PRINT_ANY,
-			   "mcast_router",
-			   "mcast_router %u ",
-			   rta_getattr_u8(tb[IFLA_BR_MCAST_ROUTER]));
-
-	if (tb[IFLA_BR_MCAST_QUERY_USE_IFADDR])
-		print_uint(PRINT_ANY,
-			   "mcast_query_use_ifaddr",
-			   "mcast_query_use_ifaddr %u ",
-			   rta_getattr_u8(tb[IFLA_BR_MCAST_QUERY_USE_IFADDR]));
-
-	if (tb[IFLA_BR_MCAST_QUERIER])
-		print_uint(PRINT_ANY,
-			   "mcast_querier",
-			   "mcast_querier %u ",
-			   rta_getattr_u8(tb[IFLA_BR_MCAST_QUERIER]));
-
-	if (tb[IFLA_BR_MCAST_HASH_ELASTICITY])
-		print_uint(PRINT_ANY,
-			   "mcast_hash_elasticity",
-			   "mcast_hash_elasticity %u ",
-			   rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_ELASTICITY]));
-
-	if (tb[IFLA_BR_MCAST_HASH_MAX])
-		print_uint(PRINT_ANY,
-			   "mcast_hash_max",
-			   "mcast_hash_max %u ",
-			   rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_MAX]));
-
-	if (tb[IFLA_BR_MCAST_LAST_MEMBER_CNT])
-		print_uint(PRINT_ANY,
-			   "mcast_last_member_cnt",
-			   "mcast_last_member_count %u ",
-			   rta_getattr_u32(tb[IFLA_BR_MCAST_LAST_MEMBER_CNT]));
-
-	if (tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT])
-		print_uint(PRINT_ANY,
-			   "mcast_startup_query_cnt",
-			   "mcast_startup_query_count %u ",
-			   rta_getattr_u32(tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT]));
-
-	if (tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL])
-		print_lluint(PRINT_ANY,
-			     "mcast_last_member_intvl",
-			     "mcast_last_member_interval %llu ",
-			     rta_getattr_u64(tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL]));
-
-	if (tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL])
-		print_lluint(PRINT_ANY,
-			     "mcast_membership_intvl",
-			     "mcast_membership_interval %llu ",
-			     rta_getattr_u64(tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL]));
-
-	if (tb[IFLA_BR_MCAST_QUERIER_INTVL])
-		print_lluint(PRINT_ANY,
-			     "mcast_querier_intvl",
-			     "mcast_querier_interval %llu ",
-			     rta_getattr_u64(tb[IFLA_BR_MCAST_QUERIER_INTVL]));
-
-	if (tb[IFLA_BR_MCAST_QUERY_INTVL])
-		print_lluint(PRINT_ANY,
-			     "mcast_query_intvl",
-			     "mcast_query_interval %llu ",
-			     rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_INTVL]));
-
-	if (tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL])
-		print_lluint(PRINT_ANY,
-			     "mcast_query_response_intvl",
-			     "mcast_query_response_interval %llu ",
-			     rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]));
-
-	if (tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL])
-		print_lluint(PRINT_ANY,
-			     "mcast_startup_query_intvl",
-			     "mcast_startup_query_interval %llu ",
-			     rta_getattr_u64(tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]));
-
-	if (tb[IFLA_BR_MCAST_STATS_ENABLED])
-		print_uint(PRINT_ANY,
-			   "mcast_stats_enabled",
-			   "mcast_stats_enabled %u ",
-			   rta_getattr_u8(tb[IFLA_BR_MCAST_STATS_ENABLED]));
-
-	if (tb[IFLA_BR_MCAST_IGMP_VERSION])
-		print_uint(PRINT_ANY,
-			   "mcast_igmp_version",
-			   "mcast_igmp_version %u ",
-			   rta_getattr_u8(tb[IFLA_BR_MCAST_IGMP_VERSION]));
-
-	if (tb[IFLA_BR_MCAST_MLD_VERSION])
-		print_uint(PRINT_ANY,
-			   "mcast_mld_version",
-			   "mcast_mld_version %u ",
-			   rta_getattr_u8(tb[IFLA_BR_MCAST_MLD_VERSION]));
-
-	if (tb[IFLA_BR_NF_CALL_IPTABLES])
-		print_uint(PRINT_ANY,
-			   "nf_call_iptables",
-			   "nf_call_iptables %u ",
-			   rta_getattr_u8(tb[IFLA_BR_NF_CALL_IPTABLES]));
-
-	if (tb[IFLA_BR_NF_CALL_IP6TABLES])
-		print_uint(PRINT_ANY,
-			   "nf_call_ip6tables",
-			   "nf_call_ip6tables %u ",
-			   rta_getattr_u8(tb[IFLA_BR_NF_CALL_IP6TABLES]));
-
-	if (tb[IFLA_BR_NF_CALL_ARPTABLES])
-		print_uint(PRINT_ANY,
-			   "nf_call_arptables",
-			   "nf_call_arptables %u ",
-			   rta_getattr_u8(tb[IFLA_BR_NF_CALL_ARPTABLES]));
 }
 
 static void bridge_print_help(struct link_util *lu, int argc, char **argv,
-			      FILE *f)
+		FILE *f)
 {
 	print_explain(f);
 }
 
-static void bridge_print_xstats_help(struct link_util *lu, FILE *f)
-{
-	fprintf(f, "Usage: ... %s [ igmp ] [ dev DEVICE ]\n", lu->id);
-}
-
-static void bridge_print_stats_attr(struct rtattr *attr, int ifindex)
-{
-	struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1];
-	struct br_mcast_stats *mstats;
-	struct rtattr *i, *list;
-	const char *ifname = "";
-	int rem;
-
-	parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr),
-	RTA_PAYLOAD(attr));
-	if (!brtb[LINK_XSTATS_TYPE_BRIDGE])
-		return;
-
-	list = brtb[LINK_XSTATS_TYPE_BRIDGE];
-	rem = RTA_PAYLOAD(list);
-	open_json_object(NULL);
-	ifname = ll_index_to_name(ifindex);
-	print_string(PRINT_ANY, "ifname", "%-16s\n", ifname);
-	for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
-		if (xstats_print_attr && i->rta_type != xstats_print_attr)
-			continue;
-		switch (i->rta_type) {
-		case BRIDGE_XSTATS_MCAST:
-			mstats = RTA_DATA(i);
-			open_json_object("multicast");
-			open_json_object("igmp_queries");
-			print_string(PRINT_FP, NULL,
-				     "%-16s    IGMP queries:\n", "");
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
-				  mstats->igmp_v1queries[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "rx_v2", "v2 %llu ",
-				  mstats->igmp_v2queries[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "rx_v3", "v3 %llu\n",
-				  mstats->igmp_v3queries[BR_MCAST_DIR_RX]);
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
-				  mstats->igmp_v1queries[BR_MCAST_DIR_TX]);
-			print_u64(PRINT_ANY, "tx_v2", "v2 %llu ",
-				  mstats->igmp_v2queries[BR_MCAST_DIR_TX]);
-			print_u64(PRINT_ANY, "tx_v3", "v3 %llu\n",
-				  mstats->igmp_v3queries[BR_MCAST_DIR_TX]);
-			close_json_object();
-
-			open_json_object("igmp_reports");
-			print_string(PRINT_FP, NULL,
-				     "%-16s    IGMP reports:\n", "");
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
-				  mstats->igmp_v1reports[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "rx_v2", "v2 %llu ",
-				  mstats->igmp_v2reports[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "rx_v3", "v3 %llu\n",
-				  mstats->igmp_v3reports[BR_MCAST_DIR_RX]);
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
-				  mstats->igmp_v1reports[BR_MCAST_DIR_TX]);
-			print_u64(PRINT_ANY, "tx_v2", "v2 %llu",
-				  mstats->igmp_v2reports[BR_MCAST_DIR_TX]);
-			print_u64(PRINT_ANY, "tx_v3", "v3 %llu\n",
-				  mstats->igmp_v3reports[BR_MCAST_DIR_TX]);
-			close_json_object();
-
-			open_json_object("igmp_leaves");
-			print_string(PRINT_FP, NULL,
-				     "%-16s    IGMP leaves: ", "");
-			print_u64(PRINT_ANY, "rx", "RX: %llu ",
-				  mstats->igmp_leaves[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "tx", "TX: %llu\n",
-				  mstats->igmp_leaves[BR_MCAST_DIR_TX]);
-			close_json_object();
-
-			print_string(PRINT_FP, NULL,
-				     "%-16s    IGMP parse errors: ", "");
-			print_u64(PRINT_ANY, "igmp_parse_errors", "%llu\n",
-				  mstats->igmp_parse_errors);
-
-			open_json_object("mld_queries");
-			print_string(PRINT_FP, NULL,
-				     "%-16s    MLD queries:\n", "");
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
-				  mstats->mld_v1queries[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "rx_v2", "v2 %llu\n",
-				  mstats->mld_v2queries[BR_MCAST_DIR_RX]);
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
-				  mstats->mld_v1queries[BR_MCAST_DIR_TX]);
-			print_u64(PRINT_ANY, "tx_v2", "v2 %llu\n",
-				  mstats->mld_v2queries[BR_MCAST_DIR_TX]);
-			close_json_object();
-
-			open_json_object("mld_reports");
-			print_string(PRINT_FP, NULL,
-				     "%-16s    MLD reports:\n", "");
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
-				  mstats->mld_v1reports[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "rx_v2", "v2 %llu\n",
-				  mstats->mld_v2reports[BR_MCAST_DIR_RX]);
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
-				  mstats->mld_v1reports[BR_MCAST_DIR_TX]);
-			print_u64(PRINT_ANY, "tx_v2", "v2 %llu\n",
-				  mstats->mld_v2reports[BR_MCAST_DIR_TX]);
-			close_json_object();
-
-			open_json_object("mld_leaves");
-			print_string(PRINT_FP, NULL,
-				     "%-16s    MLD leaves: ", "");
-			print_u64(PRINT_ANY, "rx", "RX: %llu ",
-				  mstats->mld_leaves[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "tx", "TX: %llu\n",
-				  mstats->mld_leaves[BR_MCAST_DIR_TX]);
-			close_json_object();
-
-			print_string(PRINT_FP, NULL,
-				     "%-16s    MLD parse errors: ", "");
-			print_u64(PRINT_ANY, "mld_parse_errors", "%llu\n",
-				  mstats->mld_parse_errors);
-			close_json_object();
-			break;
-		}
-	}
-	close_json_object();
-}
-
-int bridge_print_xstats(struct nlmsghdr *n, void *arg)
-{
-	struct if_stats_msg *ifsm = NLMSG_DATA(n);
-	struct rtattr *tb[IFLA_STATS_MAX+1];
-	int len = n->nlmsg_len;
-
-	len -= NLMSG_LENGTH(sizeof(*ifsm));
-	if (len < 0) {
-		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
-		return -1;
-	}
-	if (filter_index && filter_index != ifsm->ifindex)
-		return 0;
-
-	parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
-	if (tb[IFLA_STATS_LINK_XSTATS])
-		bridge_print_stats_attr(tb[IFLA_STATS_LINK_XSTATS],
-					ifsm->ifindex);
-
-	if (tb[IFLA_STATS_LINK_XSTATS_SLAVE])
-		bridge_print_stats_attr(tb[IFLA_STATS_LINK_XSTATS_SLAVE],
-					ifsm->ifindex);
-
-	return 0;
-}
-
-int bridge_parse_xstats(struct link_util *lu, int argc, char **argv)
-{
-	while (argc > 0) {
-		if (strcmp(*argv, "igmp") == 0 || strcmp(*argv, "mcast") == 0) {
-			xstats_print_attr = BRIDGE_XSTATS_MCAST;
-		} else if (strcmp(*argv, "dev") == 0) {
-			NEXT_ARG();
-			filter_index = ll_name_to_index(*argv);
-			if (!filter_index)
-				return nodev(*argv);
-		} else if (strcmp(*argv, "help") == 0) {
-			bridge_print_xstats_help(lu, stdout);
-			exit(0);
-		} else {
-			invarg("unknown attribute", *argv);
-		}
-		argc--; argv++;
-	}
-
-	return 0;
-}
-
 struct link_util bridge_link_util = {
 	.id		= "bridge",
 	.maxattr	= IFLA_BR_MAX,
 	.parse_opt	= bridge_parse_opt,
 	.print_opt	= bridge_print_opt,
 	.print_help     = bridge_print_help,
-	.parse_ifla_xstats = bridge_parse_xstats,
-	.print_ifla_xstats = bridge_print_xstats,
 };
diff --git a/ip/iplink_bridge_slave.c b/ip/iplink_bridge_slave.c
index 79a1d2f..4593872 100644
--- a/ip/iplink_bridge_slave.c
+++ b/ip/iplink_bridge_slave.c
@@ -22,27 +22,13 @@
 static void print_explain(FILE *f)
 {
 	fprintf(f,
-		"Usage: ... bridge_slave [ fdb_flush ]\n"
-		"			[ state STATE ]\n"
-		"			[ priority PRIO ]\n"
-		"			[ cost COST ]\n"
-		"			[ guard {on | off} ]\n"
-		"			[ hairpin {on | off} ]\n"
-		"			[ fastleave {on | off} ]\n"
-		"			[ root_block {on | off} ]\n"
-		"			[ learning {on | off} ]\n"
-		"			[ flood {on | off} ]\n"
-		"			[ proxy_arp {on | off} ]\n"
-		"			[ proxy_arp_wifi {on | off} ]\n"
-		"			[ mcast_router MULTICAST_ROUTER ]\n"
-		"			[ mcast_fast_leave {on | off} ]\n"
-		"			[ mcast_flood {on | off} ]\n"
-		"			[ mcast_to_unicast {on | off} ]\n"
-		"			[ group_fwd_mask MASK ]\n"
-		"			[ neigh_suppress {on | off} ]\n"
-		"			[ vlan_tunnel {on | off} ]\n"
-		"			[ isolated {on | off} ]\n"
-		"			[ backup_port DEVICE ] [ nobackup_port ]\n"
+		"Usage: ... bridge_slave [ state STATE ] [ priority PRIO ] [cost COST ]\n"
+		"                        [ guard {on | off} ]\n"
+		"                        [ hairpin {on | off} ] \n"
+		"                        [ fastleave {on | off} ]\n"
+		"                        [ root_block {on | off} ]\n"
+		"                        [ learning {on | off} ]\n"
+		"                        [ flood {on | off} ]\n"
 	);
 }
 
@@ -59,68 +45,17 @@
 	[BR_STATE_BLOCKING] = "blocking",
 };
 
-static const char *fwd_mask_tbl[16] = {
-	[0]	= "stp",
-	[2]	= "lacp",
-	[14]	= "lldp"
-};
-
 static void print_portstate(FILE *f, __u8 state)
 {
 	if (state <= BR_STATE_BLOCKING)
-		print_string(PRINT_ANY,
-			     "state",
-			     "state %s ",
-			     port_states[state]);
+		fprintf(f, "state %s ", port_states[state]);
 	else
-		print_int(PRINT_ANY, "state_index", "state (%d) ", state);
+		fprintf(f, "state (%d) ", state);
 }
 
-static void _print_onoff(FILE *f, char *json_flag, char *flag, __u8 val)
+static void print_onoff(FILE *f, char *flag, __u8 val)
 {
-	if (is_json_context())
-		print_bool(PRINT_JSON, flag, NULL, val);
-	else
-		fprintf(f, "%s %s ", flag, val ? "on" : "off");
-}
-
-static void _print_timer(FILE *f, const char *attr, struct rtattr *timer)
-{
-	struct timeval tv;
-
-	__jiffies_to_tv(&tv, rta_getattr_u64(timer));
-	if (is_json_context()) {
-		json_writer_t *jw = get_json_writer();
-
-		jsonw_name(jw, attr);
-		jsonw_printf(jw, "%i.%.2i",
-			     (int)tv.tv_sec, (int)tv.tv_usec / 10000);
-	} else {
-		fprintf(f, "%s %4i.%.2i ", attr, (int)tv.tv_sec,
-			(int)tv.tv_usec / 10000);
-	}
-}
-
-static void _bitmask2str(__u16 bitmask, char *dst, size_t dst_size,
-			 const char **tbl)
-{
-	int len, i;
-
-	for (i = 0, len = 0; bitmask; i++, bitmask >>= 1) {
-		if (bitmask & 0x1) {
-			if (tbl[i])
-				len += snprintf(dst + len, dst_size - len, "%s,",
-						tbl[i]);
-			else
-				len += snprintf(dst + len, dst_size - len, "0x%x,",
-						(1 << i));
-		}
-	}
-
-	if (!len)
-		snprintf(dst, dst_size, "0x0");
-	else
-		dst[len - 1] = 0;
+	fprintf(f, "%s %s ", flag, val ? "on" : "off");
 }
 
 static void bridge_slave_print_opt(struct link_util *lu, FILE *f,
@@ -133,165 +68,36 @@
 		print_portstate(f, rta_getattr_u8(tb[IFLA_BRPORT_STATE]));
 
 	if (tb[IFLA_BRPORT_PRIORITY])
-		print_int(PRINT_ANY,
-			  "priority",
-			  "priority %d ",
-			  rta_getattr_u16(tb[IFLA_BRPORT_PRIORITY]));
+		fprintf(f, "priority %d ",
+			rta_getattr_u16(tb[IFLA_BRPORT_PRIORITY]));
 
 	if (tb[IFLA_BRPORT_COST])
-		print_int(PRINT_ANY,
-			  "cost",
-			  "cost %d ",
-			  rta_getattr_u32(tb[IFLA_BRPORT_COST]));
+		fprintf(f, "cost %d ",
+			rta_getattr_u32(tb[IFLA_BRPORT_COST]));
 
 	if (tb[IFLA_BRPORT_MODE])
-		_print_onoff(f, "mode", "hairpin",
-			     rta_getattr_u8(tb[IFLA_BRPORT_MODE]));
+		print_onoff(f, "hairpin",
+			    rta_getattr_u8(tb[IFLA_BRPORT_MODE]));
 
 	if (tb[IFLA_BRPORT_GUARD])
-		_print_onoff(f, "guard", "guard",
-			     rta_getattr_u8(tb[IFLA_BRPORT_GUARD]));
+		print_onoff(f, "guard",
+			    rta_getattr_u8(tb[IFLA_BRPORT_GUARD]));
 
 	if (tb[IFLA_BRPORT_PROTECT])
-		_print_onoff(f, "protect", "root_block",
-			     rta_getattr_u8(tb[IFLA_BRPORT_PROTECT]));
+		print_onoff(f, "root_block",
+			    rta_getattr_u8(tb[IFLA_BRPORT_PROTECT]));
 
 	if (tb[IFLA_BRPORT_FAST_LEAVE])
-		_print_onoff(f, "fast_leave", "fastleave",
-			     rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]));
+		print_onoff(f, "fastleave",
+			    rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]));
 
 	if (tb[IFLA_BRPORT_LEARNING])
-		_print_onoff(f, "learning", "learning",
-			     rta_getattr_u8(tb[IFLA_BRPORT_LEARNING]));
+		print_onoff(f, "learning",
+			rta_getattr_u8(tb[IFLA_BRPORT_LEARNING]));
 
 	if (tb[IFLA_BRPORT_UNICAST_FLOOD])
-		_print_onoff(f, "unicast_flood", "flood",
-			     rta_getattr_u8(tb[IFLA_BRPORT_UNICAST_FLOOD]));
-
-	if (tb[IFLA_BRPORT_ID])
-		print_0xhex(PRINT_ANY, "id", "port_id %#llx ",
-			    rta_getattr_u16(tb[IFLA_BRPORT_ID]));
-
-	if (tb[IFLA_BRPORT_NO])
-		print_0xhex(PRINT_ANY, "no", "port_no %#llx ",
-			   rta_getattr_u16(tb[IFLA_BRPORT_NO]));
-
-	if (tb[IFLA_BRPORT_DESIGNATED_PORT])
-		print_uint(PRINT_ANY,
-			   "designated_port",
-			   "designated_port %u ",
-			   rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_PORT]));
-
-	if (tb[IFLA_BRPORT_DESIGNATED_COST])
-		print_uint(PRINT_ANY,
-			   "designated_cost",
-			   "designated_cost %u ",
-			   rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_COST]));
-
-	if (tb[IFLA_BRPORT_BRIDGE_ID]) {
-		char bridge_id[32];
-
-		br_dump_bridge_id(RTA_DATA(tb[IFLA_BRPORT_BRIDGE_ID]),
-				  bridge_id, sizeof(bridge_id));
-		print_string(PRINT_ANY,
-			     "bridge_id",
-			     "designated_bridge %s ",
-			     bridge_id);
-	}
-
-	if (tb[IFLA_BRPORT_ROOT_ID]) {
-		char root_id[32];
-
-		br_dump_bridge_id(RTA_DATA(tb[IFLA_BRPORT_ROOT_ID]),
-				  root_id, sizeof(root_id));
-		print_string(PRINT_ANY,
-			     "root_id",
-			     "designated_root %s ", root_id);
-	}
-
-	if (tb[IFLA_BRPORT_HOLD_TIMER])
-		_print_timer(f, "hold_timer", tb[IFLA_BRPORT_HOLD_TIMER]);
-
-	if (tb[IFLA_BRPORT_MESSAGE_AGE_TIMER])
-		_print_timer(f, "message_age_timer",
-			     tb[IFLA_BRPORT_MESSAGE_AGE_TIMER]);
-
-	if (tb[IFLA_BRPORT_FORWARD_DELAY_TIMER])
-		_print_timer(f, "forward_delay_timer",
-			     tb[IFLA_BRPORT_FORWARD_DELAY_TIMER]);
-
-	if (tb[IFLA_BRPORT_TOPOLOGY_CHANGE_ACK])
-		print_uint(PRINT_ANY,
-			   "topology_change_ack",
-			   "topology_change_ack %u ",
-			   rta_getattr_u8(tb[IFLA_BRPORT_TOPOLOGY_CHANGE_ACK]));
-
-	if (tb[IFLA_BRPORT_CONFIG_PENDING])
-		print_uint(PRINT_ANY,
-			   "config_pending",
-			   "config_pending %u ",
-			   rta_getattr_u8(tb[IFLA_BRPORT_CONFIG_PENDING]));
-
-	if (tb[IFLA_BRPORT_PROXYARP])
-		_print_onoff(f, "proxyarp", "proxy_arp",
-			     rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP]));
-
-	if (tb[IFLA_BRPORT_PROXYARP_WIFI])
-		_print_onoff(f, "proxyarp_wifi", "proxy_arp_wifi",
-			     rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP_WIFI]));
-
-	if (tb[IFLA_BRPORT_MULTICAST_ROUTER])
-		print_uint(PRINT_ANY,
-			   "multicast_router",
-			   "mcast_router %u ",
-			   rta_getattr_u8(tb[IFLA_BRPORT_MULTICAST_ROUTER]));
-
-	if (tb[IFLA_BRPORT_FAST_LEAVE])
-		// not printing any json here because
-		// we already printed fast_leave before
-		print_string(PRINT_FP,
-			     NULL,
-			     "mcast_fast_leave %s ",
-			     rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]) ? "on" : "off");
-
-	if (tb[IFLA_BRPORT_MCAST_FLOOD])
-		_print_onoff(f, "mcast_flood", "mcast_flood",
-			     rta_getattr_u8(tb[IFLA_BRPORT_MCAST_FLOOD]));
-
-	if (tb[IFLA_BRPORT_MCAST_TO_UCAST])
-		_print_onoff(f, "mcast_to_unicast", "mcast_to_unicast",
-			     rta_getattr_u8(tb[IFLA_BRPORT_MCAST_TO_UCAST]));
-
-	if (tb[IFLA_BRPORT_NEIGH_SUPPRESS])
-		_print_onoff(f, "neigh_suppress", "neigh_suppress",
-			     rta_getattr_u8(tb[IFLA_BRPORT_NEIGH_SUPPRESS]));
-
-	if (tb[IFLA_BRPORT_GROUP_FWD_MASK]) {
-		char convbuf[256];
-		__u16 fwd_mask;
-
-		fwd_mask = rta_getattr_u16(tb[IFLA_BRPORT_GROUP_FWD_MASK]);
-		print_0xhex(PRINT_ANY, "group_fwd_mask",
-			    "group_fwd_mask %#llx ", fwd_mask);
-		_bitmask2str(fwd_mask, convbuf, sizeof(convbuf), fwd_mask_tbl);
-		print_string(PRINT_ANY, "group_fwd_mask_str",
-			     "group_fwd_mask_str %s ", convbuf);
-	}
-
-	if (tb[IFLA_BRPORT_VLAN_TUNNEL])
-		_print_onoff(f, "vlan_tunnel", "vlan_tunnel",
-			     rta_getattr_u8(tb[IFLA_BRPORT_VLAN_TUNNEL]));
-
-	if (tb[IFLA_BRPORT_ISOLATED])
-		_print_onoff(f, "isolated", "isolated",
-			     rta_getattr_u8(tb[IFLA_BRPORT_ISOLATED]));
-
-	if (tb[IFLA_BRPORT_BACKUP_PORT]) {
-		int backup_p = rta_getattr_u32(tb[IFLA_BRPORT_BACKUP_PORT]);
-
-		print_string(PRINT_ANY, "backup_port", "backup_port %s ",
-			     ll_index_to_name(backup_p));
-	}
+		print_onoff(f, "flood",
+			rta_getattr_u8(tb[IFLA_BRPORT_UNICAST_FLOOD]));
 }
 
 static void bridge_slave_parse_on_off(char *arg_name, char *arg_val,
@@ -317,9 +123,7 @@
 	__u32 cost;
 
 	while (argc > 0) {
-		if (matches(*argv, "fdb_flush") == 0) {
-			addattr(n, 1024, IFLA_BRPORT_FLUSH);
-		} else if (matches(*argv, "state") == 0) {
+		if (matches(*argv, "state") == 0) {
 			NEXT_ARG();
 			if (get_u8(&state, *argv, 0))
 				invarg("state is invalid", *argv);
@@ -358,63 +162,6 @@
 			NEXT_ARG();
 			bridge_slave_parse_on_off("flood", *argv, n,
 						  IFLA_BRPORT_UNICAST_FLOOD);
-		} else if (matches(*argv, "mcast_flood") == 0) {
-			NEXT_ARG();
-			bridge_slave_parse_on_off("mcast_flood", *argv, n,
-						  IFLA_BRPORT_MCAST_FLOOD);
-		} else if (matches(*argv, "mcast_to_unicast") == 0) {
-			NEXT_ARG();
-			bridge_slave_parse_on_off("mcast_to_unicast", *argv, n,
-						  IFLA_BRPORT_MCAST_TO_UCAST);
-		} else if (matches(*argv, "proxy_arp") == 0) {
-			NEXT_ARG();
-			bridge_slave_parse_on_off("proxy_arp", *argv, n,
-						  IFLA_BRPORT_PROXYARP);
-		} else if (matches(*argv, "proxy_arp_wifi") == 0) {
-			NEXT_ARG();
-			bridge_slave_parse_on_off("proxy_arp_wifi", *argv, n,
-						  IFLA_BRPORT_PROXYARP_WIFI);
-		} else if (matches(*argv, "mcast_router") == 0) {
-			__u8 mcast_router;
-
-			NEXT_ARG();
-			if (get_u8(&mcast_router, *argv, 0))
-				invarg("invalid mcast_router", *argv);
-			addattr8(n, 1024, IFLA_BRPORT_MULTICAST_ROUTER,
-				 mcast_router);
-		} else if (matches(*argv, "mcast_fast_leave") == 0) {
-			NEXT_ARG();
-			bridge_slave_parse_on_off("mcast_fast_leave", *argv, n,
-						  IFLA_BRPORT_FAST_LEAVE);
-		} else if (matches(*argv, "neigh_suppress") == 0) {
-			NEXT_ARG();
-			bridge_slave_parse_on_off("neigh_suppress", *argv, n,
-						  IFLA_BRPORT_NEIGH_SUPPRESS);
-		} else if (matches(*argv, "group_fwd_mask") == 0) {
-			__u16 mask;
-
-			NEXT_ARG();
-			if (get_u16(&mask, *argv, 0))
-				invarg("invalid group_fwd_mask", *argv);
-			addattr16(n, 1024, IFLA_BRPORT_GROUP_FWD_MASK, mask);
-		} else if (matches(*argv, "vlan_tunnel") == 0) {
-			NEXT_ARG();
-			bridge_slave_parse_on_off("vlan_tunnel", *argv, n,
-						  IFLA_BRPORT_VLAN_TUNNEL);
-		} else if (matches(*argv, "isolated") == 0) {
-			NEXT_ARG();
-			bridge_slave_parse_on_off("isolated", *argv, n,
-						  IFLA_BRPORT_ISOLATED);
-		} else if (matches(*argv, "backup_port") == 0) {
-			int ifindex;
-
-			NEXT_ARG();
-			ifindex = ll_name_to_index(*argv);
-			if (!ifindex)
-				invarg("Device does not exist\n", *argv);
-			addattr32(n, 1024, IFLA_BRPORT_BACKUP_PORT, ifindex);
-		} else if (matches(*argv, "nobackup_port") == 0) {
-			addattr32(n, 1024, IFLA_BRPORT_BACKUP_PORT, 0);
 		} else if (matches(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -437,11 +184,10 @@
 }
 
 struct link_util bridge_slave_link_util = {
-	.id		= "bridge_slave",
+	.id		= "bridge",
 	.maxattr	= IFLA_BRPORT_MAX,
 	.print_opt	= bridge_slave_print_opt,
 	.parse_opt	= bridge_slave_parse_opt,
 	.print_help     = bridge_slave_print_help,
-	.parse_ifla_xstats = bridge_parse_xstats,
-	.print_ifla_xstats = bridge_print_xstats,
+	.slave		= true,
 };
diff --git a/ip/iplink_can.c b/ip/iplink_can.c
index 735ab94..f1b089d 100644
--- a/ip/iplink_can.c
+++ b/ip/iplink_can.c
@@ -23,11 +23,13 @@
 {
 	fprintf(f,
 		"Usage: ip link set DEVICE type can\n"
-		"\t[ bitrate BITRATE [ sample-point SAMPLE-POINT] ] |\n"
-		"\t[ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1\n \t  phase-seg2 PHASE-SEG2 [ sjw SJW ] ]\n"
+		"\t[ bitrate BITRATE [ sample-point SAMPLE-POINT] ] | \n"
+		"\t[ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1\n "
+		"\t  phase-seg2 PHASE-SEG2 [ sjw SJW ] ]\n"
 		"\n"
-		"\t[ dbitrate BITRATE [ dsample-point SAMPLE-POINT] ] |\n"
-		"\t[ dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1\n \t  dphase-seg2 PHASE-SEG2 [ dsjw SJW ] ]\n"
+		"\t[ dbitrate BITRATE [ dsample-point SAMPLE-POINT] ] | \n"
+		"\t[ dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1\n "
+		"\t  dphase-seg2 PHASE-SEG2 [ dsjw SJW ] ]\n"
 		"\n"
 		"\t[ loopback { on | off } ]\n"
 		"\t[ listen-only { on | off } ]\n"
@@ -41,8 +43,6 @@
 		"\t[ restart-ms TIME-MS ]\n"
 		"\t[ restart ]\n"
 		"\n"
-		"\t[ termination { 0..65535 } ]\n"
-		"\n"
 		"\tWhere: BITRATE	:= { 1..1000000 }\n"
 		"\t	  SAMPLE-POINT	:= { 0.000..0.999 }\n"
 		"\t	  TQ		:= { NUMBER }\n"
@@ -73,7 +73,7 @@
 	return 0;
 }
 
-static void set_ctrlmode(char *name, char *arg,
+static void set_ctrlmode(char* name, char *arg,
 			 struct can_ctrlmode *cm, __u32 flags)
 {
 	if (strcmp(arg, "on") == 0) {
@@ -89,11 +89,11 @@
 
 static void print_ctrlmode(FILE *f, __u32 cm)
 {
-	open_json_array(PRINT_ANY, is_json_context() ? "ctrlmode" : "<");
-#define _PF(cmflag, cmname)						\
-	if (cm & cmflag) {						\
-		cm &= ~cmflag;						\
-		print_string(PRINT_ANY, NULL, cm ? "%s," : "%s", cmname); \
+	fprintf(f, "<");
+#define _PF(cmflag, cmname)					\
+	if (cm & cmflag) {					\
+		cm &= ~cmflag;					\
+		fprintf(f, "%s%s", cmname, cm ? "," : "");	\
 	}
 	_PF(CAN_CTRLMODE_LOOPBACK, "LOOPBACK");
 	_PF(CAN_CTRLMODE_LISTENONLY, "LISTEN-ONLY");
@@ -105,16 +105,18 @@
 	_PF(CAN_CTRLMODE_PRESUME_ACK, "PRESUME-ACK");
 #undef _PF
 	if (cm)
-		print_hex(PRINT_ANY, NULL, "%x", cm);
-	close_json_array(PRINT_ANY, "> ");
+		fprintf(f, "%x", cm);
+	fprintf(f, "> ");
 }
 
 static int can_parse_opt(struct link_util *lu, int argc, char **argv,
 			 struct nlmsghdr *n)
 {
-	struct can_bittiming bt = {}, dbt = {};
+	struct can_bittiming bt, dbt;
 	struct can_ctrlmode cm = {0, 0};
 
+	memset(&bt, 0, sizeof(bt));
+	memset(&dbt, 0, sizeof(dbt));
 	while (argc > 0) {
 		if (matches(*argv, "bitrate") == 0) {
 			NEXT_ARG();
@@ -222,14 +224,6 @@
 			if (get_u32(&val, *argv, 0))
 				invarg("invalid \"restart-ms\" value\n", *argv);
 			addattr32(n, 1024, IFLA_CAN_RESTART_MS, val);
-		} else if (matches(*argv, "termination") == 0) {
-			__u16 val;
-
-			NEXT_ARG();
-			if (get_u16(&val, *argv, 0))
-				invarg("invalid \"termination\" value\n",
-				       *argv);
-			addattr16(n, 1024, IFLA_CAN_TERMINATION, val);
 		} else if (matches(*argv, "help") == 0) {
 			usage();
 			return -1;
@@ -251,7 +245,7 @@
 	return 0;
 }
 
-static const char *can_state_names[CAN_STATE_MAX] = {
+static const char *can_state_names[] = {
 	[CAN_STATE_ERROR_ACTIVE] = "ERROR-ACTIVE",
 	[CAN_STATE_ERROR_WARNING] = "ERROR-WARNING",
 	[CAN_STATE_ERROR_PASSIVE] = "ERROR-PASSIVE",
@@ -260,14 +254,6 @@
 	[CAN_STATE_SLEEPING] = "SLEEPING"
 };
 
-static void can_print_json_timing_min_max(const char *attr, int min, int max)
-{
-	open_json_object(attr);
-	print_int(PRINT_JSON, "min", NULL, min);
-	print_int(PRINT_JSON, "max", NULL, max);
-	close_json_object();
-}
-
 static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
 	if (!tb)
@@ -281,272 +267,79 @@
 	}
 
 	if (tb[IFLA_CAN_STATE]) {
-		uint32_t state = rta_getattr_u32(tb[IFLA_CAN_STATE]);
+		int *state = RTA_DATA(tb[IFLA_CAN_STATE]);
 
-		print_string(PRINT_ANY, "state", "state %s ", state < CAN_STATE_MAX ?
-			can_state_names[state] : "UNKNOWN");
+		fprintf(f, "state %s ", *state <= CAN_STATE_MAX ?
+			can_state_names[*state] : "UNKNOWN");
 	}
 
 	if (tb[IFLA_CAN_BERR_COUNTER]) {
 		struct can_berr_counter *bc =
 			RTA_DATA(tb[IFLA_CAN_BERR_COUNTER]);
 
-		if (is_json_context()) {
-			open_json_object("berr_counter");
-			print_int(PRINT_JSON, "tx", NULL, bc->txerr);
-			print_int(PRINT_JSON, "rx", NULL, bc->rxerr);
-			close_json_object();
-		} else {
-			fprintf(f, "(berr-counter tx %d rx %d) ",
-				bc->txerr, bc->rxerr);
-		}
+		fprintf(f, "(berr-counter tx %d rx %d) ", bc->txerr, bc->rxerr);
 	}
 
 	if (tb[IFLA_CAN_RESTART_MS]) {
 		__u32 *restart_ms = RTA_DATA(tb[IFLA_CAN_RESTART_MS]);
 
-		print_int(PRINT_ANY,
-			  "restart_ms",
-			  "restart-ms %d ",
-			  *restart_ms);
+		fprintf(f, "restart-ms %d ", *restart_ms);
 	}
 
-	/* bittiming is irrelevant if fixed bitrate is defined */
-	if (tb[IFLA_CAN_BITTIMING] && !tb[IFLA_CAN_BITRATE_CONST]) {
+	if (tb[IFLA_CAN_BITTIMING]) {
 		struct can_bittiming *bt = RTA_DATA(tb[IFLA_CAN_BITTIMING]);
 
-		if (is_json_context()) {
-			json_writer_t *jw;
-
-			open_json_object("bittiming");
-			print_int(PRINT_ANY, "bitrate", NULL, bt->bitrate);
-			jw = get_json_writer();
-			jsonw_name(jw, "sample_point");
-			jsonw_printf(jw, "%.3f",
-				     (float) bt->sample_point / 1000);
-			print_int(PRINT_ANY, "tq", NULL, bt->tq);
-			print_int(PRINT_ANY, "prop_seg", NULL, bt->prop_seg);
-			print_int(PRINT_ANY, "phase_seg1",
-				  NULL, bt->phase_seg1);
-			print_int(PRINT_ANY, "phase_seg2",
-				  NULL, bt->phase_seg2);
-			print_int(PRINT_ANY, "sjw", NULL, bt->sjw);
-			close_json_object();
-		} else {
-			fprintf(f, "\n	  bitrate %d sample-point %.3f ",
-				bt->bitrate, (float) bt->sample_point / 1000.);
-			fprintf(f,
-				"\n	  tq %d prop-seg %d phase-seg1 %d phase-seg2 %d sjw %d",
-				bt->tq, bt->prop_seg,
-				bt->phase_seg1, bt->phase_seg2,
-				bt->sjw);
-		}
+		fprintf(f, "\n	  "
+			"bitrate %d sample-point %.3f ",
+			bt->bitrate, (float)bt->sample_point / 1000.);
+		fprintf(f, "\n	  "
+			"tq %d prop-seg %d phase-seg1 %d phase-seg2 %d sjw %d",
+			bt->tq, bt->prop_seg, bt->phase_seg1, bt->phase_seg2,
+			bt->sjw);
 	}
 
-	/* bittiming const is irrelevant if fixed bitrate is defined */
-	if (tb[IFLA_CAN_BITTIMING_CONST] && !tb[IFLA_CAN_BITRATE_CONST]) {
+	if (tb[IFLA_CAN_BITTIMING_CONST]) {
 		struct can_bittiming_const *btc =
 			RTA_DATA(tb[IFLA_CAN_BITTIMING_CONST]);
 
-		if (is_json_context()) {
-			open_json_object("bittiming_const");
-			print_string(PRINT_JSON, "name", NULL, btc->name);
-			can_print_json_timing_min_max("tseg1",
-						      btc->tseg1_min,
-						      btc->tseg1_max);
-			can_print_json_timing_min_max("tseg2",
-						      btc->tseg2_min,
-						      btc->tseg2_max);
-			can_print_json_timing_min_max("sjw", 1, btc->sjw_max);
-			can_print_json_timing_min_max("brp",
-						      btc->brp_min,
-						      btc->brp_max);
-			print_int(PRINT_JSON, "brp_inc", NULL, btc->brp_inc);
-			close_json_object();
-		} else {
-			fprintf(f, "\n	  %s: tseg1 %d..%d tseg2 %d..%d "
-				"sjw 1..%d brp %d..%d brp-inc %d",
-				btc->name, btc->tseg1_min, btc->tseg1_max,
-				btc->tseg2_min, btc->tseg2_max, btc->sjw_max,
-				btc->brp_min, btc->brp_max, btc->brp_inc);
-		}
+		fprintf(f, "\n	  "
+			"%s: tseg1 %d..%d tseg2 %d..%d "
+			"sjw 1..%d brp %d..%d brp-inc %d",
+			btc->name, btc->tseg1_min, btc->tseg1_max,
+			btc->tseg2_min, btc->tseg2_max, btc->sjw_max,
+			btc->brp_min, btc->brp_max, btc->brp_inc);
 	}
 
-	if (tb[IFLA_CAN_BITRATE_CONST]) {
-		__u32 *bitrate_const = RTA_DATA(tb[IFLA_CAN_BITRATE_CONST]);
-		int bitrate_cnt = RTA_PAYLOAD(tb[IFLA_CAN_BITRATE_CONST]) /
-			sizeof(*bitrate_const);
-		int i;
-		__u32 bitrate = 0;
-
-		if (tb[IFLA_CAN_BITTIMING]) {
-			struct can_bittiming *bt =
-				RTA_DATA(tb[IFLA_CAN_BITTIMING]);
-			bitrate = bt->bitrate;
-		}
-
-		if (is_json_context()) {
-			print_uint(PRINT_JSON,
-				   "bittiming_bitrate",
-				   NULL, bitrate);
-			open_json_array(PRINT_JSON, "bitrate_const");
-			for (i = 0; i < bitrate_cnt; ++i)
-				print_uint(PRINT_JSON, NULL, NULL,
-					   bitrate_const[i]);
-			close_json_array(PRINT_JSON, NULL);
-		} else {
-			fprintf(f, "\n	  bitrate %u", bitrate);
-			fprintf(f, "\n	     [");
-
-			for (i = 0; i < bitrate_cnt - 1; ++i) {
-				/* This will keep lines below 80 signs */
-				if (!(i % 6) && i)
-					fprintf(f, "\n	      ");
-
-				fprintf(f, "%8u, ", bitrate_const[i]);
-			}
-
-			if (!(i % 6) && i)
-				fprintf(f, "\n	      ");
-			fprintf(f, "%8u ]", bitrate_const[i]);
-		}
-	}
-
-	/* data bittiming is irrelevant if fixed bitrate is defined */
-	if (tb[IFLA_CAN_DATA_BITTIMING] && !tb[IFLA_CAN_DATA_BITRATE_CONST]) {
+	if (tb[IFLA_CAN_DATA_BITTIMING]) {
 		struct can_bittiming *dbt =
 			RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING]);
 
-		if (is_json_context()) {
-			json_writer_t *jw;
-
-			open_json_object("data_bittiming");
-			print_int(PRINT_JSON, "bitrate", NULL, dbt->bitrate);
-			jw = get_json_writer();
-			jsonw_name(jw, "sample_point");
-			jsonw_printf(jw, "%.3f",
-				     (float) dbt->sample_point / 1000.);
-			print_int(PRINT_JSON, "tq", NULL, dbt->tq);
-			print_int(PRINT_JSON, "prop_seg", NULL, dbt->prop_seg);
-			print_int(PRINT_JSON, "phase_seg1",
-				  NULL, dbt->phase_seg1);
-			print_int(PRINT_JSON, "phase_seg2",
-				  NULL, dbt->phase_seg2);
-			print_int(PRINT_JSON, "sjw", NULL, dbt->sjw);
-			close_json_object();
-		} else {
-			fprintf(f, "\n	  dbitrate %d dsample-point %.3f ",
-				dbt->bitrate,
-				(float) dbt->sample_point / 1000.);
-			fprintf(f, "\n	  dtq %d dprop-seg %d dphase-seg1 %d "
-				"dphase-seg2 %d dsjw %d",
-				dbt->tq, dbt->prop_seg, dbt->phase_seg1,
-				dbt->phase_seg2, dbt->sjw);
-		}
+		fprintf(f, "\n	  "
+			"dbitrate %d dsample-point %.3f ",
+			dbt->bitrate, (float)dbt->sample_point / 1000.);
+		fprintf(f, "\n	  "
+			"dtq %d dprop-seg %d dphase-seg1 %d "
+			"dphase-seg2 %d dsjw %d",
+			dbt->tq, dbt->prop_seg, dbt->phase_seg1,
+			dbt->phase_seg2, dbt->sjw);
 	}
 
-	/* data bittiming const is irrelevant if fixed bitrate is defined */
-	if (tb[IFLA_CAN_DATA_BITTIMING_CONST] &&
-	    !tb[IFLA_CAN_DATA_BITRATE_CONST]) {
+	if (tb[IFLA_CAN_DATA_BITTIMING_CONST]) {
 		struct can_bittiming_const *dbtc =
 			RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING_CONST]);
 
-		if (is_json_context()) {
-			open_json_object("data_bittiming_const");
-			print_string(PRINT_JSON, "name", NULL, dbtc->name);
-			can_print_json_timing_min_max("tseg1",
-						      dbtc->tseg1_min,
-						      dbtc->tseg1_max);
-			can_print_json_timing_min_max("tseg2",
-						      dbtc->tseg2_min,
-						      dbtc->tseg2_max);
-			can_print_json_timing_min_max("sjw", 1, dbtc->sjw_max);
-			can_print_json_timing_min_max("brp",
-						      dbtc->brp_min,
-						      dbtc->brp_max);
-
-			print_int(PRINT_JSON, "brp_inc", NULL, dbtc->brp_inc);
-			close_json_object();
-		} else {
-			fprintf(f, "\n	  %s: dtseg1 %d..%d dtseg2 %d..%d "
-				"dsjw 1..%d dbrp %d..%d dbrp-inc %d",
-				dbtc->name, dbtc->tseg1_min, dbtc->tseg1_max,
-				dbtc->tseg2_min, dbtc->tseg2_max, dbtc->sjw_max,
-				dbtc->brp_min, dbtc->brp_max, dbtc->brp_inc);
-		}
-	}
-
-	if (tb[IFLA_CAN_DATA_BITRATE_CONST]) {
-		__u32 *dbitrate_const =
-			RTA_DATA(tb[IFLA_CAN_DATA_BITRATE_CONST]);
-		int dbitrate_cnt =
-			RTA_PAYLOAD(tb[IFLA_CAN_DATA_BITRATE_CONST]) /
-			sizeof(*dbitrate_const);
-		int i;
-		__u32 dbitrate = 0;
-
-		if (tb[IFLA_CAN_DATA_BITTIMING]) {
-			struct can_bittiming *dbt =
-				RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING]);
-			dbitrate = dbt->bitrate;
-		}
-
-		if (is_json_context()) {
-			print_uint(PRINT_JSON, "data_bittiming_bitrate",
-				   NULL, dbitrate);
-			open_json_array(PRINT_JSON, "data_bitrate_const");
-			for (i = 0; i < dbitrate_cnt; ++i)
-				print_uint(PRINT_JSON, NULL, NULL,
-					   dbitrate_const[i]);
-			close_json_array(PRINT_JSON, NULL);
-		} else {
-			fprintf(f, "\n	  dbitrate %u", dbitrate);
-			fprintf(f, "\n	     [");
-
-			for (i = 0; i < dbitrate_cnt - 1; ++i) {
-				/* This will keep lines below 80 signs */
-				if (!(i % 6) && i)
-					fprintf(f, "\n	      ");
-
-				fprintf(f, "%8u, ", dbitrate_const[i]);
-			}
-
-			if (!(i % 6) && i)
-				fprintf(f, "\n	      ");
-			fprintf(f, "%8u ]", dbitrate_const[i]);
-		}
-	}
-
-	if (tb[IFLA_CAN_TERMINATION_CONST] && tb[IFLA_CAN_TERMINATION]) {
-		__u16 *trm = RTA_DATA(tb[IFLA_CAN_TERMINATION]);
-		__u16 *trm_const = RTA_DATA(tb[IFLA_CAN_TERMINATION_CONST]);
-		int trm_cnt = RTA_PAYLOAD(tb[IFLA_CAN_TERMINATION_CONST]) /
-			sizeof(*trm_const);
-		int i;
-
-		if (is_json_context()) {
-			print_hu(PRINT_JSON, "termination", NULL, *trm);
-			open_json_array(PRINT_JSON, "termination_const");
-			for (i = 0; i < trm_cnt; ++i)
-				print_hu(PRINT_JSON, NULL, NULL, trm_const[i]);
-			close_json_array(PRINT_JSON, NULL);
-		} else {
-			fprintf(f, "\n	  termination %hu [ ", *trm);
-
-			for (i = 0; i < trm_cnt - 1; ++i)
-				fprintf(f, "%hu, ", trm_const[i]);
-
-			fprintf(f, "%hu ]", trm_const[i]);
-		}
+		fprintf(f, "\n	  "
+			"%s: dtseg1 %d..%d dtseg2 %d..%d "
+			"dsjw 1..%d dbrp %d..%d dbrp-inc %d",
+			dbtc->name, dbtc->tseg1_min, dbtc->tseg1_max,
+			dbtc->tseg2_min, dbtc->tseg2_max, dbtc->sjw_max,
+			dbtc->brp_min, dbtc->brp_max, dbtc->brp_inc);
 	}
 
 	if (tb[IFLA_CAN_CLOCK]) {
 		struct can_clock *clock = RTA_DATA(tb[IFLA_CAN_CLOCK]);
 
-		print_int(PRINT_ANY,
-			  "clock",
-			  "\n	  clock %d ",
-			  clock->freq);
+		fprintf(f, "\n	  clock %d", clock->freq);
 	}
 
 }
@@ -558,32 +351,18 @@
 
 	if (xstats && RTA_PAYLOAD(xstats) == sizeof(*stats)) {
 		stats = RTA_DATA(xstats);
-
-		if (is_json_context()) {
-			print_int(PRINT_JSON, "restarts",
-				  NULL, stats->restarts);
-			print_int(PRINT_JSON, "bus_error",
-				  NULL, stats->bus_error);
-			print_int(PRINT_JSON, "arbitration_lost",
-				  NULL, stats->arbitration_lost);
-			print_int(PRINT_JSON, "error_warning",
-				  NULL, stats->error_warning);
-			print_int(PRINT_JSON, "error_passive",
-				  NULL, stats->error_passive);
-			print_int(PRINT_JSON, "bus_off", NULL, stats->bus_off);
-		} else {
-			fprintf(f, "\n	  re-started bus-errors arbit-lost "
-				"error-warn error-pass bus-off");
-			fprintf(f, "\n	  %-10d %-10d %-10d %-10d %-10d %-10d",
-				stats->restarts, stats->bus_error,
-				stats->arbitration_lost, stats->error_warning,
-				stats->error_passive, stats->bus_off);
-		}
+		fprintf(f, "\n	  "
+			"re-started bus-errors arbit-lost "
+			"error-warn error-pass bus-off");
+		fprintf(f, "\n	  %-10d %-10d %-10d %-10d %-10d %-10d",
+			stats->restarts, stats->bus_error,
+			stats->arbitration_lost, stats->error_warning,
+			stats->error_passive, stats->bus_off);
 	}
 }
 
 static void can_print_help(struct link_util *lu, int argc, char **argv,
-			   FILE *f)
+	FILE *f)
 {
 	print_usage(f);
 }
diff --git a/ip/iplink_dummy.c b/ip/iplink_dummy.c
deleted file mode 100644
index cba2295..0000000
--- a/ip/iplink_dummy.c
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "utils.h"
-#include "ip_common.h"
-
-static void dummy_print_help(struct link_util *lu,
-			    int argc, char **argv, FILE *f)
-{
-	fprintf(f, "Usage: ... dummy\n");
-}
-
-struct link_util dummy_link_util = {
-	.id		= "dummy",
-	.print_help	= dummy_print_help,
-};
diff --git a/ip/iplink_geneve.c b/ip/iplink_geneve.c
index 9299236..1345479 100644
--- a/ip/iplink_geneve.c
+++ b/ip/iplink_geneve.c
@@ -15,30 +15,15 @@
 #include "utils.h"
 #include "ip_common.h"
 
-#define GENEVE_ATTRSET(attrs, type) (((attrs) & (1L << (type))) != 0)
-
 static void print_explain(FILE *f)
 {
-	fprintf(f,
-		"Usage: ... geneve id VNI\n"
-		"		remote ADDR\n"
-		"		[ ttl TTL ]\n"
-		"		[ tos TOS ]\n"
-		"		[ df DF ]\n"
-		"		[ flowlabel LABEL ]\n"
-		"		[ dstport PORT ]\n"
-		"		[ [no]external ]\n"
-		"		[ [no]udpcsum ]\n"
-		"		[ [no]udp6zerocsumtx ]\n"
-		"		[ [no]udp6zerocsumrx ]\n"
-		"\n"
-		"Where:	VNI   := 0-16777215\n"
-		"	ADDR  := IP_ADDRESS\n"
-		"	TOS   := { NUMBER | inherit }\n"
-		"	TTL   := { 1..255 | auto | inherit }\n"
-		"	DF    := { unset | set | inherit }\n"
-		"	LABEL := 0-1048575\n"
-	);
+	fprintf(f, "Usage: ... geneve id VNI remote ADDR\n");
+	fprintf(f, "                 [ ttl TTL ] [ tos TOS ]\n");
+	fprintf(f, "\n");
+	fprintf(f, "Where: VNI  := 0-16777215\n");
+	fprintf(f, "       ADDR := IP_ADDRESS\n");
+	fprintf(f, "       TOS  := { NUMBER | inherit }\n");
+	fprintf(f, "       TTL  := { 1..255 | inherit }\n");
 }
 
 static void explain(void)
@@ -46,59 +31,38 @@
 	print_explain(stderr);
 }
 
-static void check_duparg(__u64 *attrs, int type, const char *key,
-			 const char *argv)
-{
-	if (!GENEVE_ATTRSET(*attrs, type)) {
-		*attrs |= (1L << type);
-		return;
-	}
-	duparg2(key, argv);
-}
-
 static int geneve_parse_opt(struct link_util *lu, int argc, char **argv,
 			  struct nlmsghdr *n)
 {
-	inet_prefix daddr;
 	__u32 vni = 0;
-	__u32 label = 0;
+	int vni_set = 0;
+	__u32 daddr = 0;
+	struct in6_addr daddr6 = IN6ADDR_ANY_INIT;
 	__u8 ttl = 0;
 	__u8 tos = 0;
-	__u16 dstport = 0;
-	bool metadata = 0;
-	__u8 udpcsum = 0;
-	__u8 udp6zerocsumtx = 0;
-	__u8 udp6zerocsumrx = 0;
-	__u64 attrs = 0;
-	bool set_op = (n->nlmsg_type == RTM_NEWLINK &&
-		       !(n->nlmsg_flags & NLM_F_CREATE));
-
-	inet_prefix_reset(&daddr);
 
 	while (argc > 0) {
 		if (!matches(*argv, "id") ||
 		    !matches(*argv, "vni")) {
 			NEXT_ARG();
-			check_duparg(&attrs, IFLA_GENEVE_ID, "id", *argv);
 			if (get_u32(&vni, *argv, 0) ||
 			    vni >= 1u << 24)
 				invarg("invalid id", *argv);
+			vni_set = 1;
 		} else if (!matches(*argv, "remote")) {
 			NEXT_ARG();
-			check_duparg(&attrs, IFLA_GENEVE_REMOTE, "remote",
-				     *argv);
-			get_addr(&daddr, *argv, AF_UNSPEC);
-			if (!is_addrtype_inet_not_multi(&daddr))
+			if (!inet_get_addr(*argv, &daddr, &daddr6)) {
+				fprintf(stderr, "Invalid address \"%s\"\n", *argv);
+				return -1;
+			}
+			if (IN6_IS_ADDR_MULTICAST(&daddr6) || IN_MULTICAST(ntohl(daddr)))
 				invarg("invalid remote address", *argv);
 		} else if (!matches(*argv, "ttl") ||
 			   !matches(*argv, "hoplimit")) {
-			unsigned int uval;
+			unsigned uval;
 
 			NEXT_ARG();
-			check_duparg(&attrs, IFLA_GENEVE_TTL, "ttl", *argv);
-			if (strcmp(*argv, "inherit") == 0) {
-				addattr8(n, 1024, IFLA_GENEVE_TTL_INHERIT, 1);
-			} else if (strcmp(*argv, "auto") != 0) {
+			if (strcmp(*argv, "inherit") != 0) {
 				if (get_unsigned(&uval, *argv, 0))
 					invarg("invalid TTL", *argv);
 				if (uval > 255)
@@ -110,78 +74,12 @@
 			__u32 uval;
 
 			NEXT_ARG();
-			check_duparg(&attrs, IFLA_GENEVE_TOS, "tos", *argv);
 			if (strcmp(*argv, "inherit") != 0) {
 				if (rtnl_dsfield_a2n(&uval, *argv))
 					invarg("bad TOS value", *argv);
 				tos = uval;
 			} else
 				tos = 1;
-		} else if (!matches(*argv, "df")) {
-			enum ifla_geneve_df df;
-
-			NEXT_ARG();
-			check_duparg(&attrs, IFLA_GENEVE_DF, "df", *argv);
-			if (strcmp(*argv, "unset") == 0)
-				df = GENEVE_DF_UNSET;
-			else if (strcmp(*argv, "set") == 0)
-				df = GENEVE_DF_SET;
-			else if (strcmp(*argv, "inherit") == 0)
-				df = GENEVE_DF_INHERIT;
-			else
-				invarg("DF must be 'unset', 'set' or 'inherit'",
-				       *argv);
-
-			addattr8(n, 1024, IFLA_GENEVE_DF, df);
-		} else if (!matches(*argv, "label") ||
-			   !matches(*argv, "flowlabel")) {
-			__u32 uval;
-
-			NEXT_ARG();
-			check_duparg(&attrs, IFLA_GENEVE_LABEL, "flowlabel",
-				     *argv);
-			if (get_u32(&uval, *argv, 0) ||
-			    (uval & ~LABEL_MAX_MASK))
-				invarg("invalid flowlabel", *argv);
-			label = htonl(uval);
-		} else if (!matches(*argv, "dstport")) {
-			NEXT_ARG();
-			check_duparg(&attrs, IFLA_GENEVE_PORT, "dstport",
-				     *argv);
-			if (get_u16(&dstport, *argv, 0))
-				invarg("dstport", *argv);
-		} else if (!matches(*argv, "external")) {
-			check_duparg(&attrs, IFLA_GENEVE_COLLECT_METADATA,
-				     *argv, *argv);
-			metadata = true;
-		} else if (!matches(*argv, "noexternal")) {
-			check_duparg(&attrs, IFLA_GENEVE_COLLECT_METADATA,
-				     *argv, *argv);
-			metadata = false;
-		} else if (!matches(*argv, "udpcsum")) {
-			check_duparg(&attrs, IFLA_GENEVE_UDP_CSUM, *argv,
-				     *argv);
-			udpcsum = 1;
-		} else if (!matches(*argv, "noudpcsum")) {
-			check_duparg(&attrs, IFLA_GENEVE_UDP_CSUM, *argv,
-				     *argv);
-			udpcsum = 0;
-		} else if (!matches(*argv, "udp6zerocsumtx")) {
-			check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
-				     *argv, *argv);
-			udp6zerocsumtx = 1;
-		} else if (!matches(*argv, "noudp6zerocsumtx")) {
-			check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
-				     *argv, *argv);
-			udp6zerocsumtx = 0;
-		} else if (!matches(*argv, "udp6zerocsumrx")) {
-			check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
-				     *argv, *argv);
-			udp6zerocsumrx = 1;
-		} else if (!matches(*argv, "noudp6zerocsumrx")) {
-			check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
-				     *argv, *argv);
-			udp6zerocsumrx = 0;
 		} else if (matches(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -193,50 +91,23 @@
 		argc--, argv++;
 	}
 
-	if (metadata && GENEVE_ATTRSET(attrs, IFLA_GENEVE_ID)) {
-		fprintf(stderr, "geneve: both 'external' and vni cannot be specified\n");
+	if (!vni_set) {
+		fprintf(stderr, "geneve: missing virtual network identifier\n");
 		return -1;
 	}
 
-	if (!metadata) {
-		/* parameter checking make sense only for full geneve tunnels */
-		if (!GENEVE_ATTRSET(attrs, IFLA_GENEVE_ID)) {
-			fprintf(stderr, "geneve: missing virtual network identifier\n");
-			return -1;
-		}
-
-		/* If we are modifying the geneve device, then we only need the
-		 * ID (VNI) to identify the geneve device, and we do not need
-		 * the remote IP.
-		 */
-		if (!set_op && !is_addrtype_inet(&daddr)) {
-			fprintf(stderr, "geneve: remote link partner not specified\n");
-			return -1;
-		}
+	if (!daddr && memcmp(&daddr6, &in6addr_any, sizeof(daddr6)) == 0) {
+		fprintf(stderr, "geneve: remote link partner not specified\n");
+		return -1;
 	}
 
 	addattr32(n, 1024, IFLA_GENEVE_ID, vni);
-	if (is_addrtype_inet(&daddr)) {
-		int type = (daddr.family == AF_INET) ? IFLA_GENEVE_REMOTE :
-						       IFLA_GENEVE_REMOTE6;
-		addattr_l(n, 1024, type, daddr.data, daddr.bytelen);
-	}
-	if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_LABEL))
-		addattr32(n, 1024, IFLA_GENEVE_LABEL, label);
-	if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_TTL))
-		addattr8(n, 1024, IFLA_GENEVE_TTL, ttl);
-	if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_TOS))
-		addattr8(n, 1024, IFLA_GENEVE_TOS, tos);
-	if (dstport)
-		addattr16(n, 1024, IFLA_GENEVE_PORT, htons(dstport));
-	if (metadata)
-		addattr(n, 1024, IFLA_GENEVE_COLLECT_METADATA);
-	if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_CSUM))
-		addattr8(n, 1024, IFLA_GENEVE_UDP_CSUM, udpcsum);
-	if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX))
-		addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, udp6zerocsumtx);
-	if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX))
-		addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, udp6zerocsumrx);
+	if (daddr)
+		addattr_l(n, 1024, IFLA_GENEVE_REMOTE, &daddr, 4);
+	if (memcmp(&daddr6, &in6addr_any, sizeof(daddr6)) != 0)
+		addattr_l(n, 1024, IFLA_GENEVE_REMOTE6, &daddr6, sizeof(struct in6_addr));
+	addattr8(n, 1024, IFLA_GENEVE_TTL, ttl);
+	addattr8(n, 1024, IFLA_GENEVE_TOS, tos);
 
 	return 0;
 }
@@ -244,130 +115,46 @@
 static void geneve_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
 	__u32 vni;
-	__u8 ttl = 0;
-	__u8 tos = 0;
+	char s1[1024];
+	__u8 tos;
 
 	if (!tb)
 		return;
 
-	if (tb[IFLA_GENEVE_COLLECT_METADATA]) {
-		print_bool(PRINT_ANY, "external", "external ", true);
-		return;
-	}
-
 	if (!tb[IFLA_GENEVE_ID] ||
 	    RTA_PAYLOAD(tb[IFLA_GENEVE_ID]) < sizeof(__u32))
 		return;
 
 	vni = rta_getattr_u32(tb[IFLA_GENEVE_ID]);
-	print_uint(PRINT_ANY, "id", "id %u ", vni);
+	fprintf(f, "id %u ", vni);
 
 	if (tb[IFLA_GENEVE_REMOTE]) {
 		__be32 addr = rta_getattr_u32(tb[IFLA_GENEVE_REMOTE]);
-
 		if (addr)
-			print_string(PRINT_ANY,
-				     "remote",
-				     "remote %s ",
-				     format_host(AF_INET, 4, &addr));
+			fprintf(f, "remote %s ",
+				format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
 	} else if (tb[IFLA_GENEVE_REMOTE6]) {
 		struct in6_addr addr;
-
 		memcpy(&addr, RTA_DATA(tb[IFLA_GENEVE_REMOTE6]), sizeof(struct in6_addr));
-		if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) {
-			if (!IN6_IS_ADDR_MULTICAST(&addr))
-				print_string(PRINT_ANY,
-					     "remote6",
-					     "remote %s ",
-					     format_host(AF_INET6,
-							 sizeof(struct in6_addr),
-							 &addr));
+		if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0) {
+			if (IN6_IS_ADDR_MULTICAST(&addr))
+				fprintf(f, "remote %s ",
+					format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1)));
 		}
 	}
 
-	if (tb[IFLA_GENEVE_TTL_INHERIT] &&
-	    rta_getattr_u8(tb[IFLA_GENEVE_TTL_INHERIT])) {
-		print_string(PRINT_FP, NULL, "ttl %s ", "inherit");
-	} else if (tb[IFLA_GENEVE_TTL]) {
-		ttl = rta_getattr_u8(tb[IFLA_GENEVE_TTL]);
-		if (is_json_context() || ttl)
-			print_uint(PRINT_ANY, "ttl", "ttl %u ", ttl);
+	if (tb[IFLA_GENEVE_TTL]) {
+		__u8 ttl = rta_getattr_u8(tb[IFLA_GENEVE_TTL]);
+		if (ttl)
+			fprintf(f, "ttl %d ", ttl);
+	}
+
+	if (tb[IFLA_GENEVE_TOS] &&
+	    (tos = rta_getattr_u8(tb[IFLA_GENEVE_TOS]))) {
+		if (tos == 1)
+			fprintf(f, "tos inherit ");
 		else
-			print_string(PRINT_FP, NULL, "ttl %s ", "auto");
-	}
-
-	if (tb[IFLA_GENEVE_TOS])
-		tos = rta_getattr_u8(tb[IFLA_GENEVE_TOS]);
-	if (tos) {
-		if (is_json_context() || tos != 1)
-			print_0xhex(PRINT_ANY, "tos", "tos %#llx ", tos);
-		else
-			print_string(PRINT_FP, NULL, "tos %s ", "inherit");
-	}
-
-	if (tb[IFLA_GENEVE_DF]) {
-		enum ifla_geneve_df df = rta_getattr_u8(tb[IFLA_GENEVE_DF]);
-
-		if (df == GENEVE_DF_UNSET)
-			print_string(PRINT_JSON, "df", "df %s ", "unset");
-		else if (df == GENEVE_DF_SET)
-			print_string(PRINT_ANY, "df", "df %s ", "set");
-		else if (df == GENEVE_DF_INHERIT)
-			print_string(PRINT_ANY, "df", "df %s ", "inherit");
-	}
-
-	if (tb[IFLA_GENEVE_LABEL]) {
-		__u32 label = rta_getattr_u32(tb[IFLA_GENEVE_LABEL]);
-
-		if (label)
-			print_0xhex(PRINT_ANY,
-				    "label", "flowlabel %#llx ",
-				    ntohl(label));
-	}
-
-	if (tb[IFLA_GENEVE_PORT])
-		print_uint(PRINT_ANY,
-			   "port",
-			   "dstport %u ",
-			   rta_getattr_be16(tb[IFLA_GENEVE_PORT]));
-
-	if (tb[IFLA_GENEVE_UDP_CSUM]) {
-		if (is_json_context()) {
-			print_bool(PRINT_JSON,
-				   "udp_csum",
-				   NULL,
-				   rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM]));
-		} else {
-			if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM]))
-				fputs("no", f);
-			fputs("udpcsum ", f);
-		}
-	}
-
-	if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) {
-		if (is_json_context()) {
-			print_bool(PRINT_JSON,
-				   "udp_zero_csum6_tx",
-				   NULL,
-				   rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]));
-		} else {
-			if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]))
-				fputs("no", f);
-			fputs("udp6zerocsumtx ", f);
-		}
-	}
-
-	if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) {
-		if (is_json_context()) {
-			print_bool(PRINT_JSON,
-				   "udp_zero_csum6_rx",
-				   NULL,
-				   rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]));
-		} else {
-			if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]))
-				fputs("no", f);
-			fputs("udp6zerocsumrx ", f);
-		}
+			fprintf(f, "tos %#x ", tos);
 	}
 }
 
diff --git a/ip/iplink_hsr.c b/ip/iplink_hsr.c
index 7d9167d..65fbec8 100644
--- a/ip/iplink_hsr.c
+++ b/ip/iplink_hsr.c
@@ -24,18 +24,16 @@
 static void print_usage(FILE *f)
 {
 	fprintf(f,
-		"Usage:\tip link add name NAME type hsr slave1 SLAVE1-IF slave2 SLAVE2-IF\n"
-		"\t[ supervision ADDR-BYTE ] [version VERSION]\n"
-		"\n"
-		"NAME\n"
-		"	name of new hsr device (e.g. hsr0)\n"
-		"SLAVE1-IF, SLAVE2-IF\n"
-		"	the two slave devices bound to the HSR device\n"
-		"ADDR-BYTE\n"
-		"	0-255; the last byte of the multicast address used for HSR supervision\n"
-		"	frames (default = 0)\n"
-		"VERSION\n"
-		"	0,1; the protocol version to be used. (default = 0)\n");
+"Usage:\tip link add name NAME type hsr slave1 SLAVE1-IF slave2 SLAVE2-IF\n"
+"\t[ supervision ADDR-BYTE ]\n"
+"\n"
+"NAME\n"
+"	name of new hsr device (e.g. hsr0)\n"
+"SLAVE1-IF, SLAVE2-IF\n"
+"	the two slave devices bound to the HSR device\n"
+"ADDR-BYTE\n"
+"	0-255; the last byte of the multicast address used for HSR supervision\n"
+"	frames (default = 0)\n");
 }
 
 static void usage(void)
@@ -48,7 +46,6 @@
 {
 	int ifindex;
 	unsigned char multicast_spec;
-	unsigned char protocol_version;
 
 	while (argc > 0) {
 		if (matches(*argv, "supervision") == 0) {
@@ -57,13 +54,6 @@
 				invarg("ADDR-BYTE is invalid", *argv);
 			addattr_l(n, 1024, IFLA_HSR_MULTICAST_SPEC,
 				  &multicast_spec, 1);
-		} else if (matches(*argv, "version") == 0) {
-			NEXT_ARG();
-			if (!(get_u8(&protocol_version, *argv, 0) == 0 ||
-			      get_u8(&protocol_version, *argv, 0) == 1))
-				invarg("version is invalid", *argv);
-			addattr_l(n, 1024, IFLA_HSR_VERSION,
-				  &protocol_version, 1);
 		} else if (matches(*argv, "slave1") == 0) {
 			NEXT_ARG();
 			ifindex = ll_name_to_index(*argv);
@@ -110,36 +100,30 @@
 	    RTA_PAYLOAD(tb[IFLA_HSR_SUPERVISION_ADDR]) < ETH_ALEN)
 		return;
 
+	fprintf(f, "slave1 ");
 	if (tb[IFLA_HSR_SLAVE1])
-		print_string(PRINT_ANY,
-			     "slave1",
-			     "slave1 %s ",
-			     ll_index_to_name(rta_getattr_u32(tb[IFLA_HSR_SLAVE1])));
+		fprintf(f, "%s ",
+			ll_index_to_name(rta_getattr_u32(tb[IFLA_HSR_SLAVE1])));
 	else
-		print_null(PRINT_ANY, "slave1", "slave1 %s ", "<none>");
+		fprintf(f, "<none> ");
 
+	fprintf(f, "slave2 ");
 	if (tb[IFLA_HSR_SLAVE2])
-		print_string(PRINT_ANY,
-			     "slave2",
-			     "slave2 %s ",
-			     ll_index_to_name(rta_getattr_u32(tb[IFLA_HSR_SLAVE2])));
+		fprintf(f, "%s ",
+			ll_index_to_name(rta_getattr_u32(tb[IFLA_HSR_SLAVE2])));
 	else
-		print_null(PRINT_ANY, "slave2", "slave2 %s ", "<none>");
+		fprintf(f, "<none> ");
 
 	if (tb[IFLA_HSR_SEQ_NR])
-		print_int(PRINT_ANY,
-			  "seq_nr",
-			  "sequence %d ",
-			  rta_getattr_u16(tb[IFLA_HSR_SEQ_NR]));
+		fprintf(f, "sequence %d ",
+			rta_getattr_u16(tb[IFLA_HSR_SEQ_NR]));
 
 	if (tb[IFLA_HSR_SUPERVISION_ADDR])
-		print_string(PRINT_ANY,
-			     "supervision_addr",
-			     "supervision %s ",
-			     ll_addr_n2a(RTA_DATA(tb[IFLA_HSR_SUPERVISION_ADDR]),
-					 RTA_PAYLOAD(tb[IFLA_HSR_SUPERVISION_ADDR]),
-					 ARPHRD_VOID,
-					 b1, sizeof(b1)));
+		fprintf(f, "supervision %s ",
+			ll_addr_n2a(RTA_DATA(tb[IFLA_HSR_SUPERVISION_ADDR]),
+				    RTA_PAYLOAD(tb[IFLA_HSR_SUPERVISION_ADDR]),
+				    ARPHRD_VOID,
+				    b1, sizeof(b1)));
 }
 
 static void hsr_print_help(struct link_util *lu, int argc, char **argv,
@@ -150,7 +134,7 @@
 
 struct link_util hsr_link_util = {
 	.id		= "hsr",
-	.maxattr	= IFLA_HSR_MAX,
+	.maxattr	= IFLA_VLAN_MAX,
 	.parse_opt	= hsr_parse_opt,
 	.print_opt	= hsr_print_opt,
 	.print_help	= hsr_print_help,
diff --git a/ip/iplink_ifb.c b/ip/iplink_ifb.c
deleted file mode 100644
index a2a7301..0000000
--- a/ip/iplink_ifb.c
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "utils.h"
-#include "ip_common.h"
-
-static void ifb_print_help(struct link_util *lu,
-			    int argc, char **argv, FILE *f)
-{
-	fprintf(f, "Usage: ... ifb\n");
-}
-
-struct link_util ifb_link_util = {
-	.id		= "ifb",
-	.print_help	= ifb_print_help,
-};
diff --git a/ip/iplink_ipoib.c b/ip/iplink_ipoib.c
index 05dba35..6087cbe 100644
--- a/ip/iplink_ipoib.c
+++ b/ip/iplink_ipoib.c
@@ -22,9 +22,8 @@
 static void print_explain(FILE *f)
 {
 	fprintf(f,
-		"Usage: ... ipoib [ pkey PKEY ]\n"
-		"		 [ mode {datagram | connected} ]\n"
-		"		 [ umcast {0|1} ]\n"
+		"Usage: ... ipoib [pkey PKEY] [mode {datagram | connected}]"
+		"[umcast {0|1}]\n"
 		"\n"
 		"PKEY  := 0x8001-0xffff\n"
 	);
@@ -37,7 +36,8 @@
 
 static int mode_arg(void)
 {
-	fprintf(stderr, "Error: argument of \"mode\" must be \"datagram\"or \"connected\"\n");
+	fprintf(stderr, "Error: argument of \"mode\" must be \"datagram\""
+		"or \"connected\"\n");
 	return -1;
 }
 
@@ -91,43 +91,23 @@
 	    RTA_PAYLOAD(tb[IFLA_IPOIB_PKEY]) < sizeof(__u16))
 		return;
 
-	__u16 pkey = rta_getattr_u16(tb[IFLA_IPOIB_PKEY]);
-
-	if (is_json_context()) {
-		SPRINT_BUF(b1);
-
-		snprintf(b1, sizeof(b1), "%#.4x", pkey);
-		print_string(PRINT_JSON, "key", NULL, b1);
-	} else {
-		fprintf(f, "pkey  %#.4x ", pkey);
-	}
+	fprintf(f, "pkey  %#.4x ", rta_getattr_u16(tb[IFLA_IPOIB_PKEY]));
 
 	if (!tb[IFLA_IPOIB_MODE] ||
 	    RTA_PAYLOAD(tb[IFLA_IPOIB_MODE]) < sizeof(__u16))
 		return;
 
 	mode = rta_getattr_u16(tb[IFLA_IPOIB_MODE]);
-
-	const char *mode_str =
+	fprintf(f, "mode  %s ",
 		mode == IPOIB_MODE_DATAGRAM ? "datagram" :
-		mode == IPOIB_MODE_CONNECTED ? "connected" : "unknown";
-
-	print_string(PRINT_ANY, "mode", "mode  %s ", mode_str);
+		mode == IPOIB_MODE_CONNECTED ? "connected" :
+		"unknown");
 
 	if (!tb[IFLA_IPOIB_UMCAST] ||
 	    RTA_PAYLOAD(tb[IFLA_IPOIB_UMCAST]) < sizeof(__u16))
 		return;
 
-	__u16 umcast = rta_getattr_u16(tb[IFLA_IPOIB_UMCAST]);
-
-	if (is_json_context()) {
-		SPRINT_BUF(b1);
-
-		snprintf(b1, sizeof(b1), "%.4x", umcast);
-		print_string(PRINT_JSON, "umcast", NULL, b1);
-	} else {
-		fprintf(f, "umcast  %.4x ", umcast);
-	}
+	fprintf(f, "umcast  %.4x ", rta_getattr_u16(tb[IFLA_IPOIB_UMCAST]));
 }
 
 static void ipoib_print_help(struct link_util *lu, int argc, char **argv,
diff --git a/ip/iplink_ipvlan.c b/ip/iplink_ipvlan.c
index baae767..e08fc39 100644
--- a/ip/iplink_ipvlan.c
+++ b/ip/iplink_ipvlan.c
@@ -1,4 +1,4 @@
-/* iplink_ipvlan.c	IPVLAN/IPVTAP device support
+/* iplink_ipvlan.c	IPVLAN device support
  *
  *              This program is free software; you can redistribute it and/or
  *              modify it under the terms of the GNU General Public License
@@ -18,61 +18,50 @@
 #include "utils.h"
 #include "ip_common.h"
 
-static void print_explain(struct link_util *lu, FILE *f)
+static void ipvlan_explain(FILE *f)
 {
-	fprintf(f,
-		"Usage: ... %s [ mode MODE ] [ FLAGS ]\n"
-		"\n"
-		"MODE: l3 | l3s | l2\n"
-		"FLAGS: bridge | private | vepa\n"
-		"(first values are the defaults if nothing is specified).\n",
-		lu->id);
+	fprintf(f, "Usage: ... ipvlan [ mode { l2 | l3 } ]\n");
+}
+
+static void explain(void)
+{
+	ipvlan_explain(stderr);
+}
+
+static int mode_arg(void)
+{
+	fprintf(stderr, "Error: argument of \"mode\" must be either \"l2\", "
+		"or \"l3\"\n");
+	return -1;
 }
 
 static int ipvlan_parse_opt(struct link_util *lu, int argc, char **argv,
 			    struct nlmsghdr *n)
 {
-	__u16 flags = 0;
-	bool mflag_given = false;
-
 	while (argc > 0) {
 		if (matches(*argv, "mode") == 0) {
 			__u16 mode = 0;
-
 			NEXT_ARG();
 
 			if (strcmp(*argv, "l2") == 0)
 				mode = IPVLAN_MODE_L2;
 			else if (strcmp(*argv, "l3") == 0)
 				mode = IPVLAN_MODE_L3;
-			else if (strcmp(*argv, "l3s") == 0)
-				mode = IPVLAN_MODE_L3S;
-			else {
-				fprintf(stderr, "Error: argument of \"mode\" must be either \"l2\", \"l3\" or \"l3s\"\n");
-				return -1;
-			}
+			else
+				return mode_arg();
+
 			addattr16(n, 1024, IFLA_IPVLAN_MODE, mode);
-		} else if (matches(*argv, "private") == 0 && !mflag_given) {
-			flags |= IPVLAN_F_PRIVATE;
-			mflag_given = true;
-		} else if (matches(*argv, "vepa") == 0 && !mflag_given) {
-			flags |= IPVLAN_F_VEPA;
-			mflag_given = true;
-		} else if (matches(*argv, "bridge") == 0 && !mflag_given) {
-			mflag_given = true;
 		} else if (matches(*argv, "help") == 0) {
-			print_explain(lu, stderr);
+			explain();
 			return -1;
 		} else {
-			fprintf(stderr, "%s: unknown option \"%s\"?\n",
-				lu->id, *argv);
-			print_explain(lu, stderr);
+			fprintf(stderr, "ipvlan: unknown option \"%s\"?\n",
+				*argv);
+			explain();
 			return -1;
 		}
-		argc--;
-		argv++;
+		argc--, argv++;
 	}
-	addattr16(n, 1024, IFLA_IPVLAN_FLAGS, flags);
 
 	return 0;
 }
@@ -86,26 +75,10 @@
 	if (tb[IFLA_IPVLAN_MODE]) {
 		if (RTA_PAYLOAD(tb[IFLA_IPVLAN_MODE]) == sizeof(__u16)) {
 			__u16 mode = rta_getattr_u16(tb[IFLA_IPVLAN_MODE]);
-			const char *mode_str = mode == IPVLAN_MODE_L2 ? "l2" :
-				mode == IPVLAN_MODE_L3 ? "l3" :
-				mode == IPVLAN_MODE_L3S ? "l3s" : "unknown";
 
-			print_string(PRINT_ANY, "mode", " mode %s ", mode_str);
-		}
-	}
-	if (tb[IFLA_IPVLAN_FLAGS]) {
-		if (RTA_PAYLOAD(tb[IFLA_IPVLAN_FLAGS]) == sizeof(__u16)) {
-			__u16 flags = rta_getattr_u16(tb[IFLA_IPVLAN_FLAGS]);
-
-			if (flags & IPVLAN_F_PRIVATE)
-				print_bool(PRINT_ANY, "private", "private ",
-					   true);
-			else if (flags & IPVLAN_F_VEPA)
-				print_bool(PRINT_ANY, "vepa", "vepa ",
-					   true);
-			else
-				print_bool(PRINT_ANY, "bridge", "bridge ",
-					   true);
+			fprintf(f, " mode %s ",
+				mode == IPVLAN_MODE_L2 ? "l2" :
+				mode == IPVLAN_MODE_L3 ? "l3" : "unknown");
 		}
 	}
 }
@@ -113,7 +86,7 @@
 static void ipvlan_print_help(struct link_util *lu, int argc, char **argv,
 			      FILE *f)
 {
-	print_explain(lu, f);
+	ipvlan_explain(f);
 }
 
 struct link_util ipvlan_link_util = {
@@ -123,11 +96,3 @@
 	.print_opt	= ipvlan_print_opt,
 	.print_help	= ipvlan_print_help,
 };
-
-struct link_util ipvtap_link_util = {
-	.id		= "ipvtap",
-	.maxattr	= IFLA_IPVLAN_MAX,
-	.parse_opt	= ipvlan_parse_opt,
-	.print_opt	= ipvlan_print_opt,
-	.print_help	= ipvlan_print_help,
-};
diff --git a/ip/iplink_macvlan.c b/ip/iplink_macvlan.c
index b966a61..f195e81 100644
--- a/ip/iplink_macvlan.c
+++ b/ip/iplink_macvlan.c
@@ -15,7 +15,6 @@
 #include <string.h>
 #include <sys/socket.h>
 #include <linux/if_link.h>
-#include <linux/if_ether.h>
 
 #include "rt_names.h"
 #include "utils.h"
@@ -30,12 +29,7 @@
 static void print_explain(struct link_util *lu, FILE *f)
 {
 	fprintf(f,
-		"Usage: ... %s mode MODE [flag MODE_FLAG] MODE_OPTS\n"
-		"\n"
-		"MODE: private | vepa | bridge | passthru | source\n"
-		"MODE_FLAG: null | nopromisc\n"
-		"MODE_OPTS: for mode \"source\":\n"
-		"\tmacaddr { { add | del } <macaddr> | set [ <macaddr> [ <macaddr>  ... ] ] | flush }\n",
+		"Usage: ... %s mode { private | vepa | bridge | passthru [nopromisc] }\n",
 		lu->id
 	);
 }
@@ -45,21 +39,11 @@
 	print_explain(lu, stderr);
 }
 
-
 static int mode_arg(const char *arg)
 {
-	fprintf(stderr,
-		"Error: argument of \"mode\" must be \"private\", \"vepa\", \"bridge\", \"passthru\" or \"source\", not \"%s\"\n",
-		arg);
-	return -1;
-}
-
-static int flag_arg(const char *arg)
-{
-	fprintf(stderr,
-		"Error: argument of \"flag\" must be \"nopromisc\" or \"null\", not \"%s\"\n",
-		arg);
-	return -1;
+        fprintf(stderr, "Error: argument of \"mode\" must be \"private\", "
+		"\"vepa\", \"bridge\" or \"passthru\", not \"%s\"\n", arg);
+        return -1;
 }
 
 static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv,
@@ -67,10 +51,6 @@
 {
 	__u32 mode = 0;
 	__u16 flags = 0;
-	__u32 mac_mode = 0;
-	int has_flags = 0;
-	char mac[ETH_ALEN];
-	struct rtattr *nmac;
 
 	while (argc > 0) {
 		if (matches(*argv, "mode") == 0) {
@@ -84,72 +64,10 @@
 				mode = MACVLAN_MODE_BRIDGE;
 			else if (strcmp(*argv, "passthru") == 0)
 				mode = MACVLAN_MODE_PASSTHRU;
-			else if (strcmp(*argv, "source") == 0)
-				mode = MACVLAN_MODE_SOURCE;
 			else
 				return mode_arg(*argv);
-		} else if (matches(*argv, "flag") == 0) {
-			NEXT_ARG();
-
-			if (strcmp(*argv, "nopromisc") == 0)
-				flags |= MACVLAN_FLAG_NOPROMISC;
-			else if (strcmp(*argv, "null") == 0)
-				flags |= 0;
-			else
-				return flag_arg(*argv);
-
-			has_flags = 1;
-
-		} else if (matches(*argv, "macaddr") == 0) {
-			NEXT_ARG();
-
-			if (strcmp(*argv, "add") == 0) {
-				mac_mode = MACVLAN_MACADDR_ADD;
-			} else if (strcmp(*argv, "del") == 0) {
-				mac_mode = MACVLAN_MACADDR_DEL;
-			} else if (strcmp(*argv, "set") == 0) {
-				mac_mode = MACVLAN_MACADDR_SET;
-			} else if (strcmp(*argv, "flush") == 0) {
-				mac_mode = MACVLAN_MACADDR_FLUSH;
-			} else {
-				explain(lu);
-				return -1;
-			}
-
-			addattr32(n, 1024, IFLA_MACVLAN_MACADDR_MODE, mac_mode);
-
-			if (mac_mode == MACVLAN_MACADDR_ADD ||
-			    mac_mode == MACVLAN_MACADDR_DEL) {
-				NEXT_ARG();
-
-				if (ll_addr_a2n(mac, sizeof(mac),
-						*argv) != ETH_ALEN)
-					return -1;
-
-				addattr_l(n, 1024, IFLA_MACVLAN_MACADDR, &mac,
-					  ETH_ALEN);
-			}
-
-			if (mac_mode == MACVLAN_MACADDR_SET) {
-				nmac = addattr_nest(n, 1024,
-						    IFLA_MACVLAN_MACADDR_DATA);
-				while (NEXT_ARG_OK()) {
-					NEXT_ARG_FWD();
-
-					if (ll_addr_a2n(mac, sizeof(mac),
-							*argv) != ETH_ALEN) {
-						PREV_ARG();
-						break;
-					}
-
-					addattr_l(n, 1024, IFLA_MACVLAN_MACADDR,
-						  &mac, ETH_ALEN);
-				}
-				addattr_nest_end(n, nmac);
-			}
 		} else if (matches(*argv, "nopromisc") == 0) {
 			flags |= MACVLAN_FLAG_NOPROMISC;
-			has_flags = 1;
 		} else if (matches(*argv, "help") == 0) {
 			explain(lu);
 			return -1;
@@ -164,7 +82,7 @@
 	if (mode)
 		addattr32(n, 1024, IFLA_MACVLAN_MODE, mode);
 
-	if (has_flags) {
+	if (flags) {
 		if (flags & MACVLAN_FLAG_NOPROMISC &&
 		    mode != MACVLAN_MODE_PASSTHRU) {
 			pfx_err(lu, "nopromisc flag only valid in passthru mode");
@@ -180,10 +98,6 @@
 {
 	__u32 mode;
 	__u16 flags;
-	__u32 count;
-	unsigned char *addr;
-	int len;
-	struct rtattr *rta;
 
 	if (!tb)
 		return;
@@ -193,66 +107,24 @@
 		return;
 
 	mode = rta_getattr_u32(tb[IFLA_MACVLAN_MODE]);
-	print_string(PRINT_ANY,
-		     "mode",
-		     "mode %s ",
-		     mode == MACVLAN_MODE_PRIVATE ? "private"
-		     : mode == MACVLAN_MODE_VEPA    ? "vepa"
-		     : mode == MACVLAN_MODE_BRIDGE  ? "bridge"
-		     : mode == MACVLAN_MODE_PASSTHRU  ? "passthru"
-		     : mode == MACVLAN_MODE_SOURCE  ? "source"
-		     :				 "unknown");
+	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");
 
 	if (!tb[IFLA_MACVLAN_FLAGS] ||
 	    RTA_PAYLOAD(tb[IFLA_MACVLAN_FLAGS]) < sizeof(__u16))
-		flags = 0;
-	else
-		flags = rta_getattr_u16(tb[IFLA_MACVLAN_FLAGS]);
+		return;
 
+	flags = rta_getattr_u16(tb[IFLA_MACVLAN_FLAGS]);
 	if (flags & MACVLAN_FLAG_NOPROMISC)
-		print_bool(PRINT_ANY, "nopromisc", "nopromisc ", true);
-
-	/* in source mode, there are more options to print */
-
-	if (mode != MACVLAN_MODE_SOURCE)
-		return;
-
-	if (!tb[IFLA_MACVLAN_MACADDR_COUNT] ||
-	    RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_COUNT]) < sizeof(__u32))
-		return;
-
-	count = rta_getattr_u32(tb[IFLA_MACVLAN_MACADDR_COUNT]);
-	print_int(PRINT_ANY, "macaddr_count", "remotes (%d) ", count);
-
-	if (!tb[IFLA_MACVLAN_MACADDR_DATA])
-		return;
-
-	rta = RTA_DATA(tb[IFLA_MACVLAN_MACADDR_DATA]);
-	len = RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_DATA]);
-
-	open_json_array(PRINT_JSON, "macaddr_data");
-	for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
-		if (rta->rta_type != IFLA_MACVLAN_MACADDR ||
-		    RTA_PAYLOAD(rta) < 6)
-			continue;
-		addr = RTA_DATA(rta);
-		if (is_json_context()) {
-			SPRINT_BUF(b1);
-
-			snprintf(b1, sizeof(b1),
-				 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", addr[0],
-				 addr[1], addr[2], addr[3], addr[4], addr[5]);
-			print_string(PRINT_JSON, NULL, NULL, b1);
-		} else {
-			fprintf(f, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ", addr[0],
-				addr[1], addr[2], addr[3], addr[4], addr[5]);
-		}
-	}
-	close_json_array(PRINT_JSON, NULL);
+		fprintf(f, "nopromisc ");
 }
 
 static void macvlan_print_help(struct link_util *lu, int argc, char **argv,
-			       FILE *f)
+	FILE *f)
 {
 	print_explain(lu, f);
 }
diff --git a/ip/iplink_netdevsim.c b/ip/iplink_netdevsim.c
deleted file mode 100644
index 3448608..0000000
--- a/ip/iplink_netdevsim.c
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "utils.h"
-#include "ip_common.h"
-
-static void netdevsim_print_help(struct link_util *lu,
-				 int argc, char **argv, FILE *f)
-{
-	fprintf(f, "Usage: ... netdevsim\n");
-}
-
-struct link_util netdevsim_link_util = {
-	.id		= "netdevsim",
-	.print_help	= netdevsim_print_help,
-};
diff --git a/ip/iplink_nlmon.c b/ip/iplink_nlmon.c
deleted file mode 100644
index 6ffb910..0000000
--- a/ip/iplink_nlmon.c
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "utils.h"
-#include "ip_common.h"
-
-static void nlmon_print_help(struct link_util *lu,
-			    int argc, char **argv, FILE *f)
-{
-	fprintf(f, "Usage: ... nlmon\n");
-}
-
-struct link_util nlmon_link_util = {
-	.id		= "nlmon",
-	.print_help	= nlmon_print_help,
-};
diff --git a/ip/iplink_rmnet.c b/ip/iplink_rmnet.c
deleted file mode 100644
index 1d16440..0000000
--- a/ip/iplink_rmnet.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * iplink_rmnet.c	RMNET device support
- *
- * Authors:     Daniele Palmas <dnlplm@gmail.com>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "utils.h"
-#include "ip_common.h"
-
-static void print_explain(FILE *f)
-{
-	fprintf(f,
-		"Usage: ... rmnet mux_id MUXID\n"
-		"\n"
-		"MUXID := 1-254\n"
-	);
-}
-
-static void explain(void)
-{
-	print_explain(stderr);
-}
-
-static int rmnet_parse_opt(struct link_util *lu, int argc, char **argv,
-			   struct nlmsghdr *n)
-{
-	__u16 mux_id;
-
-	while (argc > 0) {
-		if (matches(*argv, "mux_id") == 0) {
-			NEXT_ARG();
-			if (get_u16(&mux_id, *argv, 0))
-				invarg("mux_id is invalid", *argv);
-			addattr16(n, 1024, IFLA_RMNET_MUX_ID, mux_id);
-		} else if (matches(*argv, "help") == 0) {
-			explain();
-			return -1;
-		} else {
-			fprintf(stderr, "rmnet: unknown command \"%s\"?\n", *argv);
-			explain();
-			return -1;
-		}
-		argc--, argv++;
-	}
-
-	return 0;
-}
-
-static void rmnet_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
-{
-	if (!tb)
-		return;
-
-	if (!tb[IFLA_RMNET_MUX_ID] ||
-	    RTA_PAYLOAD(tb[IFLA_RMNET_MUX_ID]) < sizeof(__u16))
-		return;
-
-	print_uint(PRINT_ANY,
-		   "mux_id",
-		   "mux_id %u ",
-		   rta_getattr_u16(tb[IFLA_RMNET_MUX_ID]));
-}
-
-static void rmnet_print_help(struct link_util *lu, int argc, char **argv,
-			     FILE *f)
-{
-	print_explain(f);
-}
-
-struct link_util rmnet_link_util = {
-	.id		= "rmnet",
-	.maxattr	= IFLA_RMNET_MAX,
-	.parse_opt	= rmnet_parse_opt,
-	.print_opt	= rmnet_print_opt,
-	.print_help	= rmnet_print_help,
-};
diff --git a/ip/iplink_team.c b/ip/iplink_team.c
deleted file mode 100644
index 58f955a..0000000
--- a/ip/iplink_team.c
+++ /dev/null
@@ -1,26 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "utils.h"
-#include "ip_common.h"
-
-static void team_print_help(struct link_util *lu,
-			    int argc, char **argv, FILE *f)
-{
-	fprintf(f, "Usage: ... team\n");
-}
-
-static void team_slave_print_help(struct link_util *lu,
-				  int argc, char **argv, FILE *f)
-{
-	fprintf(f, "Usage: ... team_slave\n");
-}
-
-struct link_util team_link_util = {
-	.id		= "team",
-	.print_help	= team_print_help,
-}, team_slave_link_util = {
-	.id		= "team_slave",
-	.print_help	= team_slave_print_help,
-};
diff --git a/ip/iplink_vcan.c b/ip/iplink_vcan.c
deleted file mode 100644
index 74a1505..0000000
--- a/ip/iplink_vcan.c
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "utils.h"
-#include "ip_common.h"
-
-static void vcan_print_help(struct link_util *lu,
-			    int argc, char **argv, FILE *f)
-{
-	fprintf(f, "Usage: ... vcan\n");
-}
-
-struct link_util vcan_link_util = {
-	.id		= "vcan",
-	.print_help	= vcan_print_help,
-};
diff --git a/ip/iplink_vlan.c b/ip/iplink_vlan.c
index 0dfb4a8..5bd766f 100644
--- a/ip/iplink_vlan.c
+++ b/ip/iplink_vlan.c
@@ -21,18 +21,15 @@
 static void print_explain(FILE *f)
 {
 	fprintf(f,
-		"Usage: ... vlan id VLANID\n"
-		"		[ protocol VLANPROTO ]\n"
-		"		[ reorder_hdr { on | off } ]\n"
-		"		[ gvrp { on | off } ]\n"
-		"		[ mvrp { on | off } ]\n"
-		"		[ loose_binding { on | off } ]\n"
-		"		[ bridge_binding { on | off } ]\n"
-		"		[ ingress-qos-map QOS-MAP ]\n"
-		"		[ egress-qos-map QOS-MAP ]\n"
+		"Usage: ... vlan [ protocol VLANPROTO ] id VLANID"
+		"                [ FLAG-LIST ]\n"
+		"                [ ingress-qos-map QOS-MAP ] [ egress-qos-map QOS-MAP ]\n"
 		"\n"
+		"VLANPROTO: [ 802.1Q / 802.1ad ]\n"
 		"VLANID := 0-4095\n"
-		"VLANPROTO: [ 802.1Q | 802.1ad ]\n"
+		"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
+		"FLAG := [ reorder_hdr { on | off } ] [ gvrp { on | off } ] [ mvrp { on | off } ]\n"
+		"        [ loose_binding { on | off } ]\n"
 		"QOS-MAP := [ QOS-MAP ] QOS-MAPPING\n"
 		"QOS-MAPPING := FROM:TO\n"
 	);
@@ -57,7 +54,8 @@
 	struct ifla_vlan_qos_mapping m;
 	struct rtattr *tail;
 
-	tail = addattr_nest(n, 1024, attrtype);
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, 1024, attrtype, NULL, 0);
 
 	while (argc > 0) {
 		char *colon = strchr(*argv, ':');
@@ -75,7 +73,7 @@
 		addattr_l(n, 1024, IFLA_VLAN_QOS_MAPPING, &m, sizeof(m));
 	}
 
-	addattr_nest_end(n, tail);
+	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *)tail;
 
 	*argcp = argc;
 	*argvp = argv;
@@ -135,15 +133,6 @@
 				flags.flags &= ~VLAN_FLAG_LOOSE_BINDING;
 			else
 				return on_off("loose_binding", *argv);
-		} else if (matches(*argv, "bridge_binding") == 0) {
-			NEXT_ARG();
-			flags.mask |= VLAN_FLAG_BRIDGE_BINDING;
-			if (strcmp(*argv, "on") == 0)
-				flags.flags |= VLAN_FLAG_BRIDGE_BINDING;
-			else if (strcmp(*argv, "off") == 0)
-				flags.flags &= ~VLAN_FLAG_BRIDGE_BINDING;
-			else
-				return on_off("bridge_binding", *argv);
 		} else if (matches(*argv, "ingress-qos-map") == 0) {
 			NEXT_ARG();
 			if (vlan_parse_qos_map(&argc, &argv, n,
@@ -173,58 +162,42 @@
 	return 0;
 }
 
-static void vlan_print_map(FILE *f,
-			   const char *name_json,
-			   const char *name_fp,
-			   struct rtattr *attr)
+static void vlan_print_map(FILE *f, char *name, struct rtattr *attr)
 {
 	struct ifla_vlan_qos_mapping *m;
 	struct rtattr *i;
 	int rem;
 
-	open_json_array(PRINT_JSON, name_json);
-	print_string(PRINT_FP, NULL, "\n      %s { ", name_fp);
+	fprintf(f, "\n      %s { ", name);
 
 	rem = RTA_PAYLOAD(attr);
 	for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 		m = RTA_DATA(i);
-
-		if (is_json_context()) {
-			open_json_object(NULL);
-			print_uint(PRINT_JSON, "from", NULL, m->from);
-			print_uint(PRINT_JSON, "to", NULL, m->to);
-			close_json_object();
-		} else {
-			fprintf(f, "%u:%u ", m->from, m->to);
-		}
+		fprintf(f, "%u:%u ", m->from, m->to);
 	}
-
-	close_json_array(PRINT_JSON, NULL);
-	print_string(PRINT_FP, NULL, "%s ", "}");
+	fprintf(f, "} ");
 }
 
 static void vlan_print_flags(FILE *fp, __u32 flags)
 {
-	open_json_array(PRINT_ANY, is_json_context() ? "flags" : "<");
-#define _PF(f)	if (flags & VLAN_FLAG_##f) {				\
-		flags &= ~VLAN_FLAG_##f;				\
-		print_string(PRINT_ANY, NULL, flags ? "%s," : "%s", #f); \
-	}
+	fprintf(fp, "<");
+#define _PF(f)	if (flags & VLAN_FLAG_##f) { \
+			flags &= ~ VLAN_FLAG_##f; \
+			fprintf(fp, #f "%s", flags ? "," : ""); \
+		}
 	_PF(REORDER_HDR);
 	_PF(GVRP);
 	_PF(MVRP);
 	_PF(LOOSE_BINDING);
-	_PF(BRIDGE_BINDING);
 #undef _PF
 	if (flags)
-		print_hex(PRINT_ANY, NULL, "%x", flags);
-	close_json_array(PRINT_ANY, "> ");
+		fprintf(fp, "%x", flags);
+	fprintf(fp, "> ");
 }
 
 static void vlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
 	struct ifla_vlan_flags *flags;
-
 	SPRINT_BUF(b1);
 
 	if (!tb)
@@ -238,19 +211,13 @@
 		return;
 
 	if (tb[IFLA_VLAN_PROTOCOL])
-		print_string(PRINT_ANY,
-			     "protocol",
-			     "protocol %s ",
-			     ll_proto_n2a(
-				     rta_getattr_u16(tb[IFLA_VLAN_PROTOCOL]),
+		fprintf(f, "protocol %s ",
+			ll_proto_n2a(rta_getattr_u16(tb[IFLA_VLAN_PROTOCOL]),
 				     b1, sizeof(b1)));
 	else
-		print_string(PRINT_ANY, "protocol", "protocol %s ", "802.1q");
+		fprintf(f, "protocol 802.1q ");
 
-	print_uint(PRINT_ANY,
-		   "id",
-		   "id %u ",
-		   rta_getattr_u16(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))
@@ -259,19 +226,13 @@
 		vlan_print_flags(f, flags->flags);
 	}
 	if (tb[IFLA_VLAN_INGRESS_QOS])
-		vlan_print_map(f,
-			       "ingress_qos",
-			       "ingress-qos-map",
-			       tb[IFLA_VLAN_INGRESS_QOS]);
+		vlan_print_map(f, "ingress-qos-map", tb[IFLA_VLAN_INGRESS_QOS]);
 	if (tb[IFLA_VLAN_EGRESS_QOS])
-		vlan_print_map(f,
-			       "egress_qos",
-			       "egress-qos-map",
-			       tb[IFLA_VLAN_EGRESS_QOS]);
+		vlan_print_map(f, "egress-qos-map", tb[IFLA_VLAN_EGRESS_QOS]);
 }
 
 static void vlan_print_help(struct link_util *lu, int argc, char **argv,
-			    FILE *f)
+	FILE *f)
 {
 	print_explain(f);
 }
diff --git a/ip/iplink_vrf.c b/ip/iplink_vrf.c
index 5d20f29..9b4b772 100644
--- a/ip/iplink_vrf.c
+++ b/ip/iplink_vrf.c
@@ -13,7 +13,6 @@
 #include <string.h>
 #include <sys/socket.h>
 #include <linux/if_link.h>
-#include <errno.h>
 
 #include "rt_names.h"
 #include "utils.h"
@@ -21,7 +20,7 @@
 
 static void vrf_explain(FILE *f)
 {
-	fprintf(f, "Usage: ... vrf table TABLEID\n");
+	fprintf(f, "Usage: ... vrf table TABLEID \n");
 }
 
 static void explain(void)
@@ -62,28 +61,11 @@
 		return;
 
 	if (tb[IFLA_VRF_TABLE])
-		print_uint(PRINT_ANY,
-			   "table",
-			   "table %u ",
-			   rta_getattr_u32(tb[IFLA_VRF_TABLE]));
-}
-
-static void vrf_slave_print_opt(struct link_util *lu, FILE *f,
-				struct rtattr *tb[])
-{
-	if (!tb)
-		return;
-
-	if (tb[IFLA_VRF_PORT_TABLE]) {
-		print_uint(PRINT_ANY,
-			   "table",
-			   "table %u ",
-			   rta_getattr_u32(tb[IFLA_VRF_PORT_TABLE]));
-	}
+		fprintf(f, "table %u ", rta_getattr_u32(tb[IFLA_VRF_TABLE]));
 }
 
 static void vrf_print_help(struct link_util *lu, int argc, char **argv,
-			   FILE *f)
+			      FILE *f)
 {
 	vrf_explain(f);
 }
@@ -95,132 +77,3 @@
 	.print_opt	= vrf_print_opt,
 	.print_help	= vrf_print_help,
 };
-
-struct link_util vrf_slave_link_util = {
-	.id             = "vrf_slave",
-	.maxattr        = IFLA_VRF_PORT_MAX,
-	.print_opt	= vrf_slave_print_opt,
-};
-
-/* returns table id if name is a VRF device */
-__u32 ipvrf_get_table(const char *name)
-{
-	struct {
-		struct nlmsghdr		n;
-		struct ifinfomsg	i;
-		char			buf[1024];
-	} req = {
-		.n = {
-			.nlmsg_len   = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-			.nlmsg_flags = NLM_F_REQUEST,
-			.nlmsg_type  = RTM_GETLINK,
-		},
-		.i = {
-			.ifi_family  = preferred_family,
-		},
-	};
-	struct nlmsghdr *answer;
-	struct rtattr *tb[IFLA_MAX+1];
-	struct rtattr *li[IFLA_INFO_MAX+1];
-	struct rtattr *vrf_attr[IFLA_VRF_MAX + 1];
-	struct ifinfomsg *ifi;
-	__u32 tb_id = 0;
-	int len;
-
-	addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, strlen(name) + 1);
-
-	if (rtnl_talk_suppress_rtnl_errmsg(&rth, &req.n, &answer) < 0) {
-		/* special case "default" vrf to be the main table */
-		if (errno == ENODEV && !strcmp(name, "default"))
-			if (rtnl_rttable_a2n(&tb_id, "main"))
-				fprintf(stderr,
-					"BUG: RTTable \"main\" not found.\n");
-
-		return tb_id;
-	}
-
-	ifi = NLMSG_DATA(answer);
-	len = answer->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
-	if (len < 0) {
-		fprintf(stderr, "BUG: Invalid response to link query.\n");
-		goto out;
-	}
-
-	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
-
-	if (!tb[IFLA_LINKINFO])
-		goto out;
-
-	parse_rtattr_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
-
-	if (!li[IFLA_INFO_KIND] || !li[IFLA_INFO_DATA])
-		goto out;
-
-	if (strcmp(RTA_DATA(li[IFLA_INFO_KIND]), "vrf"))
-		goto out;
-
-	parse_rtattr_nested(vrf_attr, IFLA_VRF_MAX, li[IFLA_INFO_DATA]);
-	if (vrf_attr[IFLA_VRF_TABLE])
-		tb_id = rta_getattr_u32(vrf_attr[IFLA_VRF_TABLE]);
-
-	if (!tb_id)
-		fprintf(stderr, "BUG: VRF %s is missing table id\n", name);
-
-out:
-	free(answer);
-	return tb_id;
-}
-
-int name_is_vrf(const char *name)
-{
-	struct {
-		struct nlmsghdr		n;
-		struct ifinfomsg	i;
-		char			buf[1024];
-	} req = {
-		.n = {
-			.nlmsg_len   = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-			.nlmsg_flags = NLM_F_REQUEST,
-			.nlmsg_type  = RTM_GETLINK,
-		},
-		.i = {
-			.ifi_family  = preferred_family,
-		},
-	};
-	struct nlmsghdr *answer;
-	struct rtattr *tb[IFLA_MAX+1];
-	struct rtattr *li[IFLA_INFO_MAX+1];
-	struct ifinfomsg *ifi;
-	int ifindex = 0;
-	int len;
-
-	addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, strlen(name) + 1);
-
-	if (rtnl_talk_suppress_rtnl_errmsg(&rth, &req.n, &answer) < 0)
-		return 0;
-
-	ifi = NLMSG_DATA(answer);
-	len = answer->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
-	if (len < 0) {
-		fprintf(stderr, "BUG: Invalid response to link query.\n");
-		goto out;
-	}
-
-	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
-
-	if (!tb[IFLA_LINKINFO])
-		goto out;
-
-	parse_rtattr_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
-
-	if (!li[IFLA_INFO_KIND])
-		goto out;
-
-	if (strcmp(RTA_DATA(li[IFLA_INFO_KIND]), "vrf"))
-		goto out;
-
-	ifindex = ifi->ifi_index;
-out:
-	free(answer);
-	return ifindex;
-}
diff --git a/ip/iplink_vxcan.c b/ip/iplink_vxcan.c
deleted file mode 100644
index 8b08c9a..0000000
--- a/ip/iplink_vxcan.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * iplink_vxcan.c	vxcan device support (Virtual CAN Tunnel)
- *
- *		This program is free software; you can redistribute it and/or
- *		modify it under the terms of the GNU General Public License
- *		as published by the Free Software Foundation; either version
- *		2 of the License, or (at your option) any later version.
- *
- * Author:	Oliver Hartkopp <socketcan@hartkopp.net>
- * Based on:	link_veth.c from Pavel Emelianov <xemul@openvz.org>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <net/if.h>
-#include <linux/can/vxcan.h>
-
-#include "utils.h"
-#include "ip_common.h"
-
-static void print_usage(FILE *f)
-{
-	printf("Usage: ip link <options> type vxcan [peer <options>]\n"
-	       "To get <options> type 'ip link add help'\n");
-}
-
-static void usage(void)
-{
-	print_usage(stderr);
-}
-
-static int vxcan_parse_opt(struct link_util *lu, int argc, char **argv,
-			  struct nlmsghdr *n)
-{
-	char *type = NULL;
-	int err;
-	struct rtattr *data;
-	struct ifinfomsg *ifm, *peer_ifm;
-	unsigned int ifi_flags, ifi_change, ifi_index;
-
-	if (strcmp(argv[0], "peer") != 0) {
-		usage();
-		return -1;
-	}
-
-	ifm = NLMSG_DATA(n);
-	ifi_flags = ifm->ifi_flags;
-	ifi_change = ifm->ifi_change;
-	ifi_index = ifm->ifi_index;
-	ifm->ifi_flags = 0;
-	ifm->ifi_change = 0;
-	ifm->ifi_index = 0;
-
-	data = addattr_nest(n, 1024, VXCAN_INFO_PEER);
-
-	n->nlmsg_len += sizeof(struct ifinfomsg);
-
-	err = iplink_parse(argc - 1, argv + 1, (struct iplink_req *)n, &type);
-	if (err < 0)
-		return err;
-
-	if (type)
-		duparg("type", argv[err]);
-
-	peer_ifm = RTA_DATA(data);
-	peer_ifm->ifi_index = ifm->ifi_index;
-	peer_ifm->ifi_flags = ifm->ifi_flags;
-	peer_ifm->ifi_change = ifm->ifi_change;
-	ifm->ifi_flags = ifi_flags;
-	ifm->ifi_change = ifi_change;
-	ifm->ifi_index = ifi_index;
-
-	addattr_nest_end(n, data);
-	return argc - 1 - err;
-}
-
-static void vxcan_print_help(struct link_util *lu, int argc, char **argv,
-	FILE *f)
-{
-	print_usage(f);
-}
-
-struct link_util vxcan_link_util = {
-	.id = "vxcan",
-	.parse_opt = vxcan_parse_opt,
-	.print_help = vxcan_print_help,
-};
diff --git a/ip/iplink_vxlan.c b/ip/iplink_vxlan.c
index bae9d99..ede8482 100644
--- a/ip/iplink_vxlan.c
+++ b/ip/iplink_vxlan.c
@@ -21,41 +21,22 @@
 #include "utils.h"
 #include "ip_common.h"
 
-#define VXLAN_ATTRSET(attrs, type) (((attrs) & (1L << (type))) != 0)
-
 static void print_explain(FILE *f)
 {
-	fprintf(f,
-		"Usage: ... vxlan id VNI\n"
-		"		[ { group | remote } IP_ADDRESS ]\n"
-		"		[ local ADDR ]\n"
-		"		[ ttl TTL ]\n"
-		"		[ tos TOS ]\n"
-		"		[ df DF ]\n"
-		"		[ flowlabel LABEL ]\n"
-		"		[ dev PHYS_DEV ]\n"
-		"		[ dstport PORT ]\n"
-		"		[ srcport MIN MAX ]\n"
-		"		[ [no]learning ]\n"
-		"		[ [no]proxy ]\n"
-		"		[ [no]rsc ]\n"
-		"		[ [no]l2miss ]\n"
-		"		[ [no]l3miss ]\n"
-		"		[ ageing SECONDS ]\n"
-		"		[ maxaddress NUMBER ]\n"
-		"		[ [no]udpcsum ]\n"
-		"		[ [no]udp6zerocsumtx ]\n"
-		"		[ [no]udp6zerocsumrx ]\n"
-		"		[ [no]remcsumtx ] [ [no]remcsumrx ]\n"
-		"		[ [no]external ] [ gbp ] [ gpe ]\n"
-		"\n"
-		"Where:	VNI	:= 0-16777215\n"
-		"	ADDR	:= { IP_ADDRESS | any }\n"
-		"	TOS	:= { NUMBER | inherit }\n"
-		"	TTL	:= { 1..255 | auto | inherit }\n"
-		"	DF	:= { unset | set | inherit }\n"
-		"	LABEL := 0-1048575\n"
-	);
+	fprintf(f, "Usage: ... vxlan id VNI [ { group | remote } IP_ADDRESS ] [ local ADDR ]\n");
+	fprintf(f, "                 [ ttl TTL ] [ tos TOS ] [ dev PHYS_DEV ]\n");
+	fprintf(f, "                 [ dstport PORT ] [ srcport MIN MAX ]\n");
+	fprintf(f, "                 [ [no]learning ] [ [no]proxy ] [ [no]rsc ]\n");
+	fprintf(f, "                 [ [no]l2miss ] [ [no]l3miss ]\n");
+	fprintf(f, "                 [ ageing SECONDS ] [ maxaddress NUMBER ]\n");
+	fprintf(f, "                 [ [no]udpcsum ] [ [no]udp6zerocsumtx ] [ [no]udp6zerocsumrx ]\n");
+	fprintf(f, "                 [ [no]remcsumtx ] [ [no]remcsumrx ]\n");
+	fprintf(f, "                 [ [no]external ] [ gbp ]\n");
+	fprintf(f, "\n");
+	fprintf(f, "Where: VNI := 0-16777215\n");
+	fprintf(f, "       ADDR := { IP_ADDRESS | any }\n");
+	fprintf(f, "       TOS  := { NUMBER | inherit }\n");
+	fprintf(f, "       TTL  := { 1..255 | inherit }\n");
 }
 
 static void explain(void)
@@ -63,273 +44,179 @@
 	print_explain(stderr);
 }
 
-static void check_duparg(__u64 *attrs, int type, const char *key,
-			 const char *argv)
-{
-	if (!VXLAN_ATTRSET(*attrs, type)) {
-		*attrs |= (1L << type);
-		return;
-	}
-	duparg2(key, argv);
-}
-
 static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
 			  struct nlmsghdr *n)
 {
-	inet_prefix saddr, daddr;
 	__u32 vni = 0;
+	int vni_set = 0;
+	__u32 saddr = 0;
+	__u32 gaddr = 0;
+	__u32 daddr = 0;
+	struct in6_addr saddr6 = IN6ADDR_ANY_INIT;
+	struct in6_addr gaddr6 = IN6ADDR_ANY_INIT;
+	struct in6_addr daddr6 = IN6ADDR_ANY_INIT;
+	unsigned link = 0;
+	__u8 tos = 0;
+	__u8 ttl = 0;
 	__u8 learning = 1;
+	__u8 proxy = 0;
+	__u8 rsc = 0;
+	__u8 l2miss = 0;
+	__u8 l3miss = 0;
+	__u8 noage = 0;
+	__u32 age = 0;
+	__u32 maxaddr = 0;
 	__u16 dstport = 0;
+	__u8 udpcsum = 0;
+	__u8 udp6zerocsumtx = 0;
+	__u8 udp6zerocsumrx = 0;
+	__u8 remcsumtx = 0;
+	__u8 remcsumrx = 0;
 	__u8 metadata = 0;
-	__u64 attrs = 0;
-	bool set_op = (n->nlmsg_type == RTM_NEWLINK &&
-		       !(n->nlmsg_flags & NLM_F_CREATE));
-	bool selected_family = false;
-
-	saddr.family = daddr.family = AF_UNSPEC;
-
-	inet_prefix_reset(&saddr);
-	inet_prefix_reset(&daddr);
+	__u8 gbp = 0;
+	int dst_port_set = 0;
+	struct ifla_vxlan_port_range range = { 0, 0 };
 
 	while (argc > 0) {
 		if (!matches(*argv, "id") ||
 		    !matches(*argv, "vni")) {
-			/* We will add ID attribute outside of the loop since we
-			 * need to consider metadata information as well.
-			 */
 			NEXT_ARG();
-			check_duparg(&attrs, IFLA_VXLAN_ID, "id", *argv);
 			if (get_u32(&vni, *argv, 0) ||
 			    vni >= 1u << 24)
 				invarg("invalid id", *argv);
+			vni_set = 1;
 		} else if (!matches(*argv, "group")) {
-			if (is_addrtype_inet_not_multi(&daddr)) {
-				fprintf(stderr, "vxlan: both group and remote");
-				fprintf(stderr, " cannot be specified\n");
+			NEXT_ARG();
+			if (!inet_get_addr(*argv, &gaddr, &gaddr6)) {
+				fprintf(stderr, "Invalid address \"%s\"\n", *argv);
 				return -1;
 			}
-			NEXT_ARG();
-			check_duparg(&attrs, IFLA_VXLAN_GROUP, "group", *argv);
-			get_addr(&daddr, *argv, saddr.family);
-			if (!is_addrtype_inet_multi(&daddr))
+			if (!IN6_IS_ADDR_MULTICAST(&gaddr6) && !IN_MULTICAST(ntohl(gaddr)))
 				invarg("invalid group address", *argv);
 		} else if (!matches(*argv, "remote")) {
-			if (is_addrtype_inet_multi(&daddr)) {
-				fprintf(stderr, "vxlan: both group and remote");
-				fprintf(stderr, " cannot be specified\n");
+			NEXT_ARG();
+			if (!inet_get_addr(*argv, &daddr, &daddr6)) {
+				fprintf(stderr, "Invalid address \"%s\"\n", *argv);
 				return -1;
 			}
-			NEXT_ARG();
-			check_duparg(&attrs, IFLA_VXLAN_GROUP, "remote", *argv);
-			get_addr(&daddr, *argv, saddr.family);
-			if (!is_addrtype_inet_not_multi(&daddr))
+			if (IN6_IS_ADDR_MULTICAST(&daddr6) || IN_MULTICAST(ntohl(daddr)))
 				invarg("invalid remote address", *argv);
 		} else if (!matches(*argv, "local")) {
 			NEXT_ARG();
-			check_duparg(&attrs, IFLA_VXLAN_LOCAL, "local", *argv);
-			get_addr(&saddr, *argv, daddr.family);
-			if (!is_addrtype_inet_not_multi(&saddr))
+			if (strcmp(*argv, "any")) {
+				if (!inet_get_addr(*argv, &saddr, &saddr6)) {
+					fprintf(stderr, "Invalid address \"%s\"\n", *argv);
+					return -1;
+				}
+			}
+
+			if (IN_MULTICAST(ntohl(saddr)) || IN6_IS_ADDR_MULTICAST(&saddr6))
 				invarg("invalid local address", *argv);
 		} else if (!matches(*argv, "dev")) {
-			unsigned int link;
-
 			NEXT_ARG();
-			check_duparg(&attrs, IFLA_VXLAN_LINK, "dev", *argv);
-			link = ll_name_to_index(*argv);
-			if (!link)
-				exit(nodev(*argv));
-			addattr32(n, 1024, IFLA_VXLAN_LINK, link);
+			link = if_nametoindex(*argv);
+			if (link == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n",
+					*argv);
+				exit(-1);
+			}
 		} else if (!matches(*argv, "ttl") ||
 			   !matches(*argv, "hoplimit")) {
-			unsigned int uval;
-			__u8 ttl = 0;
+			unsigned uval;
 
 			NEXT_ARG();
-			check_duparg(&attrs, IFLA_VXLAN_TTL, "ttl", *argv);
-			if (strcmp(*argv, "inherit") == 0) {
-				addattr(n, 1024, IFLA_VXLAN_TTL_INHERIT);
-			} else if (strcmp(*argv, "auto") == 0) {
-				addattr8(n, 1024, IFLA_VXLAN_TTL, ttl);
-			} else {
+			if (strcmp(*argv, "inherit") != 0) {
 				if (get_unsigned(&uval, *argv, 0))
 					invarg("invalid TTL", *argv);
 				if (uval > 255)
 					invarg("TTL must be <= 255", *argv);
 				ttl = uval;
-				addattr8(n, 1024, IFLA_VXLAN_TTL, ttl);
 			}
 		} else if (!matches(*argv, "tos") ||
 			   !matches(*argv, "dsfield")) {
 			__u32 uval;
-			__u8 tos;
 
 			NEXT_ARG();
-			check_duparg(&attrs, IFLA_VXLAN_TOS, "tos", *argv);
 			if (strcmp(*argv, "inherit") != 0) {
 				if (rtnl_dsfield_a2n(&uval, *argv))
 					invarg("bad TOS value", *argv);
 				tos = uval;
 			} else
 				tos = 1;
-			addattr8(n, 1024, IFLA_VXLAN_TOS, tos);
-		} else if (!matches(*argv, "df")) {
-			enum ifla_vxlan_df df;
-
-			NEXT_ARG();
-			check_duparg(&attrs, IFLA_VXLAN_DF, "df", *argv);
-			if (strcmp(*argv, "unset") == 0)
-				df = VXLAN_DF_UNSET;
-			else if (strcmp(*argv, "set") == 0)
-				df = VXLAN_DF_SET;
-			else if (strcmp(*argv, "inherit") == 0)
-				df = VXLAN_DF_INHERIT;
-			else
-				invarg("DF must be 'unset', 'set' or 'inherit'",
-				       *argv);
-
-			addattr8(n, 1024, IFLA_VXLAN_DF, df);
-		} else if (!matches(*argv, "label") ||
-			   !matches(*argv, "flowlabel")) {
-			__u32 uval;
-
-			NEXT_ARG();
-			check_duparg(&attrs, IFLA_VXLAN_LABEL, "flowlabel",
-				     *argv);
-			if (get_u32(&uval, *argv, 0) ||
-			    (uval & ~LABEL_MAX_MASK))
-				invarg("invalid flowlabel", *argv);
-			addattr32(n, 1024, IFLA_VXLAN_LABEL, htonl(uval));
 		} else if (!matches(*argv, "ageing")) {
-			__u32 age;
-
 			NEXT_ARG();
-			check_duparg(&attrs, IFLA_VXLAN_AGEING, "ageing",
-				     *argv);
 			if (strcmp(*argv, "none") == 0)
-				age = 0;
+				noage = 1;
 			else if (get_u32(&age, *argv, 0))
 				invarg("ageing timer", *argv);
-			addattr32(n, 1024, IFLA_VXLAN_AGEING, age);
 		} else if (!matches(*argv, "maxaddress")) {
-			__u32 maxaddr;
-
 			NEXT_ARG();
-			check_duparg(&attrs, IFLA_VXLAN_LIMIT,
-				     "maxaddress", *argv);
 			if (strcmp(*argv, "unlimited") == 0)
 				maxaddr = 0;
 			else if (get_u32(&maxaddr, *argv, 0))
 				invarg("max addresses", *argv);
-			addattr32(n, 1024, IFLA_VXLAN_LIMIT, maxaddr);
 		} else if (!matches(*argv, "port") ||
 			   !matches(*argv, "srcport")) {
-			struct ifla_vxlan_port_range range = { 0, 0 };
-
+			__u16 minport, maxport;
 			NEXT_ARG();
-			check_duparg(&attrs, IFLA_VXLAN_PORT_RANGE, "srcport",
-				     *argv);
-			if (get_be16(&range.low, *argv, 0))
+			if (get_u16(&minport, *argv, 0))
 				invarg("min port", *argv);
 			NEXT_ARG();
-			if (get_be16(&range.high, *argv, 0))
+			if (get_u16(&maxport, *argv, 0))
 				invarg("max port", *argv);
-			if (range.low || range.high) {
-				addattr_l(n, 1024, IFLA_VXLAN_PORT_RANGE,
-					  &range, sizeof(range));
-			}
-		} else if (!matches(*argv, "dstport")) {
+			range.low = htons(minport);
+			range.high = htons(maxport);
+		} else if (!matches(*argv, "dstport")){
 			NEXT_ARG();
-			check_duparg(&attrs, IFLA_VXLAN_PORT, "dstport", *argv);
 			if (get_u16(&dstport, *argv, 0))
 				invarg("dst port", *argv);
+			dst_port_set = 1;
 		} else if (!matches(*argv, "nolearning")) {
-			check_duparg(&attrs, IFLA_VXLAN_LEARNING, *argv, *argv);
 			learning = 0;
 		} else if (!matches(*argv, "learning")) {
-			check_duparg(&attrs, IFLA_VXLAN_LEARNING, *argv, *argv);
 			learning = 1;
 		} else if (!matches(*argv, "noproxy")) {
-			check_duparg(&attrs, IFLA_VXLAN_PROXY, *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_PROXY, 0);
+			proxy = 0;
 		} else if (!matches(*argv, "proxy")) {
-			check_duparg(&attrs, IFLA_VXLAN_PROXY, *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_PROXY, 1);
+			proxy = 1;
 		} else if (!matches(*argv, "norsc")) {
-			check_duparg(&attrs, IFLA_VXLAN_RSC, *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_RSC, 0);
+			rsc = 0;
 		} else if (!matches(*argv, "rsc")) {
-			check_duparg(&attrs, IFLA_VXLAN_RSC, *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_RSC, 1);
+			rsc = 1;
 		} else if (!matches(*argv, "nol2miss")) {
-			check_duparg(&attrs, IFLA_VXLAN_L2MISS, *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_L2MISS, 0);
+			l2miss = 0;
 		} else if (!matches(*argv, "l2miss")) {
-			check_duparg(&attrs, IFLA_VXLAN_L2MISS, *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_L2MISS, 1);
+			l2miss = 1;
 		} else if (!matches(*argv, "nol3miss")) {
-			check_duparg(&attrs, IFLA_VXLAN_L3MISS, *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_L3MISS, 0);
+			l3miss = 0;
 		} else if (!matches(*argv, "l3miss")) {
-			check_duparg(&attrs, IFLA_VXLAN_L3MISS, *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_L3MISS, 1);
+			l3miss = 1;
 		} else if (!matches(*argv, "udpcsum")) {
-			check_duparg(&attrs, IFLA_VXLAN_UDP_CSUM, *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_UDP_CSUM, 1);
+			udpcsum = 1;
 		} else if (!matches(*argv, "noudpcsum")) {
-			check_duparg(&attrs, IFLA_VXLAN_UDP_CSUM, *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_UDP_CSUM, 0);
+			udpcsum = 0;
 		} else if (!matches(*argv, "udp6zerocsumtx")) {
-			check_duparg(&attrs, IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
-				     *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, 1);
+			udp6zerocsumtx = 1;
 		} else if (!matches(*argv, "noudp6zerocsumtx")) {
-			check_duparg(&attrs, IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
-				     *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, 0);
+			udp6zerocsumtx = 0;
 		} else if (!matches(*argv, "udp6zerocsumrx")) {
-			check_duparg(&attrs, IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
-				     *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, 1);
+			udp6zerocsumrx = 1;
 		} else if (!matches(*argv, "noudp6zerocsumrx")) {
-			check_duparg(&attrs, IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
-				     *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, 0);
+			udp6zerocsumrx = 0;
 		} else if (!matches(*argv, "remcsumtx")) {
-			check_duparg(&attrs, IFLA_VXLAN_REMCSUM_TX,
-				     *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_REMCSUM_TX, 1);
+			remcsumtx = 1;
 		} else if (!matches(*argv, "noremcsumtx")) {
-			check_duparg(&attrs, IFLA_VXLAN_REMCSUM_TX,
-				     *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_REMCSUM_TX, 0);
+			remcsumtx = 0;
 		} else if (!matches(*argv, "remcsumrx")) {
-			check_duparg(&attrs, IFLA_VXLAN_REMCSUM_RX,
-				     *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_REMCSUM_RX, 1);
+			remcsumrx = 1;
 		} else if (!matches(*argv, "noremcsumrx")) {
-			check_duparg(&attrs, IFLA_VXLAN_REMCSUM_RX,
-				     *argv, *argv);
-			addattr8(n, 1024, IFLA_VXLAN_REMCSUM_RX, 0);
+			remcsumrx = 0;
 		} else if (!matches(*argv, "external")) {
-			check_duparg(&attrs, IFLA_VXLAN_COLLECT_METADATA,
-				     *argv, *argv);
 			metadata = 1;
-			learning = 0;
-			/* we will add LEARNING attribute outside of the loop */
-			addattr8(n, 1024, IFLA_VXLAN_COLLECT_METADATA,
-				 metadata);
 		} else if (!matches(*argv, "noexternal")) {
-			check_duparg(&attrs, IFLA_VXLAN_COLLECT_METADATA,
-				     *argv, *argv);
 			metadata = 0;
-			addattr8(n, 1024, IFLA_VXLAN_COLLECT_METADATA,
-				 metadata);
 		} else if (!matches(*argv, "gbp")) {
-			check_duparg(&attrs, IFLA_VXLAN_GBP, *argv, *argv);
-			addattr_l(n, 1024, IFLA_VXLAN_GBP, NULL, 0);
-		} else if (!matches(*argv, "gpe")) {
-			check_duparg(&attrs, IFLA_VXLAN_GPE, *argv, *argv);
-			addattr_l(n, 1024, IFLA_VXLAN_GPE, NULL, 0);
+			gbp = 1;
 		} else if (matches(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -341,26 +228,24 @@
 		argc--, argv++;
 	}
 
-	if (metadata && VXLAN_ATTRSET(attrs, IFLA_VXLAN_ID)) {
+	if (metadata && vni_set) {
 		fprintf(stderr, "vxlan: both 'external' and vni cannot be specified\n");
 		return -1;
 	}
 
-	if (!metadata && !VXLAN_ATTRSET(attrs, IFLA_VXLAN_ID) && !set_op) {
+	if (!metadata && !vni_set) {
 		fprintf(stderr, "vxlan: missing virtual network identifier\n");
 		return -1;
 	}
 
-	if (is_addrtype_inet_multi(&daddr) &&
-	    !VXLAN_ATTRSET(attrs, IFLA_VXLAN_LINK)) {
-		fprintf(stderr, "vxlan: 'group' requires 'dev' to be specified\n");
+	if ((gaddr && daddr) ||
+		(memcmp(&gaddr6, &in6addr_any, sizeof(gaddr6)) &&
+		 memcmp(&daddr6, &in6addr_any, sizeof(daddr6)))) {
+		fprintf(stderr, "vxlan: both group and remote cannot be specified\n");
 		return -1;
 	}
 
-	if (!VXLAN_ATTRSET(attrs, IFLA_VXLAN_PORT) &&
-	    VXLAN_ATTRSET(attrs, IFLA_VXLAN_GPE)) {
-		dstport = 4790;
-	} else if (!VXLAN_ATTRSET(attrs, IFLA_VXLAN_PORT) && !set_op) {
+	if (!dst_port_set) {
 		fprintf(stderr, "vxlan: destination port not specified\n"
 			"Will use Linux kernel default (non-standard value)\n");
 		fprintf(stderr,
@@ -368,282 +253,202 @@
 			"Use 'dstport 0' to get default and quiet this message\n");
 	}
 
-	if (VXLAN_ATTRSET(attrs, IFLA_VXLAN_ID))
-		addattr32(n, 1024, IFLA_VXLAN_ID, vni);
+	addattr32(n, 1024, IFLA_VXLAN_ID, vni);
+	if (gaddr)
+		addattr_l(n, 1024, IFLA_VXLAN_GROUP, &gaddr, 4);
+	else if (daddr)
+		addattr_l(n, 1024, IFLA_VXLAN_GROUP, &daddr, 4);
+	if (memcmp(&gaddr6, &in6addr_any, sizeof(gaddr6)) != 0)
+		addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &gaddr6, sizeof(struct in6_addr));
+	else if (memcmp(&daddr6, &in6addr_any, sizeof(daddr6)) != 0)
+		addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &daddr6, sizeof(struct in6_addr));
 
-	if (is_addrtype_inet(&saddr)) {
-		int type = (saddr.family == AF_INET) ? IFLA_VXLAN_LOCAL
-						     : IFLA_VXLAN_LOCAL6;
-		addattr_l(n, 1024, type, saddr.data, saddr.bytelen);
-		selected_family = true;
-	}
+	if (saddr)
+		addattr_l(n, 1024, IFLA_VXLAN_LOCAL, &saddr, 4);
+	else if (memcmp(&saddr6, &in6addr_any, sizeof(saddr6)) != 0)
+		addattr_l(n, 1024, IFLA_VXLAN_LOCAL6, &saddr6, sizeof(struct in6_addr));
 
-	if (is_addrtype_inet(&daddr)) {
-		int type = (daddr.family == AF_INET) ? IFLA_VXLAN_GROUP
-						     : IFLA_VXLAN_GROUP6;
-		addattr_l(n, 1024, type, daddr.data, daddr.bytelen);
-		selected_family = true;
-	}
+	if (link)
+		addattr32(n, 1024, IFLA_VXLAN_LINK, link);
+	addattr8(n, 1024, IFLA_VXLAN_TTL, ttl);
+	addattr8(n, 1024, IFLA_VXLAN_TOS, tos);
+	addattr8(n, 1024, IFLA_VXLAN_LEARNING, learning);
+	addattr8(n, 1024, IFLA_VXLAN_PROXY, proxy);
+	addattr8(n, 1024, IFLA_VXLAN_RSC, rsc);
+	addattr8(n, 1024, IFLA_VXLAN_L2MISS, l2miss);
+	addattr8(n, 1024, IFLA_VXLAN_L3MISS, l3miss);
+	addattr8(n, 1024, IFLA_VXLAN_UDP_CSUM, udpcsum);
+	addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, udp6zerocsumtx);
+	addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, udp6zerocsumrx);
+	addattr8(n, 1024, IFLA_VXLAN_REMCSUM_TX, remcsumtx);
+	addattr8(n, 1024, IFLA_VXLAN_REMCSUM_RX, remcsumrx);
+	addattr8(n, 1024, IFLA_VXLAN_COLLECT_METADATA, metadata);
 
-	if (!selected_family) {
-		if (preferred_family == AF_INET) {
-			get_addr(&daddr, "default", AF_INET);
-			addattr_l(n, 1024, IFLA_VXLAN_GROUP,
-				  daddr.data, daddr.bytelen);
-		} else if (preferred_family == AF_INET6) {
-			get_addr(&daddr, "default", AF_INET6);
-			addattr_l(n, 1024, IFLA_VXLAN_GROUP6,
-				  daddr.data, daddr.bytelen);
-		}
-	}
-
-	if (!set_op || VXLAN_ATTRSET(attrs, IFLA_VXLAN_LEARNING))
-		addattr8(n, 1024, IFLA_VXLAN_LEARNING, learning);
-
+	if (noage)
+		addattr32(n, 1024, IFLA_VXLAN_AGEING, 0);
+	else if (age)
+		addattr32(n, 1024, IFLA_VXLAN_AGEING, age);
+	if (maxaddr)
+		addattr32(n, 1024, IFLA_VXLAN_LIMIT, maxaddr);
+	if (range.low || range.high)
+		addattr_l(n, 1024, IFLA_VXLAN_PORT_RANGE,
+			  &range, sizeof(range));
 	if (dstport)
 		addattr16(n, 1024, IFLA_VXLAN_PORT, htons(dstport));
 
+	if (gbp)
+		addattr_l(n, 1024, IFLA_VXLAN_GBP, NULL, 0);
+
+
 	return 0;
 }
 
 static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
 	__u32 vni;
-	__u8 ttl = 0;
-	__u8 tos = 0;
+	unsigned link;
+	__u8 tos;
 	__u32 maxaddr;
+	char s1[1024];
+	char s2[64];
 
 	if (!tb)
 		return;
 
-	if (tb[IFLA_VXLAN_COLLECT_METADATA] &&
-	    rta_getattr_u8(tb[IFLA_VXLAN_COLLECT_METADATA])) {
-		print_bool(PRINT_ANY, "external", "external ", true);
-		return;
-	}
-
 	if (!tb[IFLA_VXLAN_ID] ||
 	    RTA_PAYLOAD(tb[IFLA_VXLAN_ID]) < sizeof(__u32))
 		return;
 
 	vni = rta_getattr_u32(tb[IFLA_VXLAN_ID]);
-	print_uint(PRINT_ANY, "id", "id %u ", vni);
+	fprintf(f, "id %u ", vni);
 
 	if (tb[IFLA_VXLAN_GROUP]) {
 		__be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_GROUP]);
-
 		if (addr) {
 			if (IN_MULTICAST(ntohl(addr)))
-				print_string(PRINT_ANY,
-					     "group",
-					     "group %s ",
-					     format_host(AF_INET, 4, &addr));
+				fprintf(f, "group %s ",
+					format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
 			else
-				print_string(PRINT_ANY,
-					     "remote",
-					     "remote %s ",
-					     format_host(AF_INET, 4, &addr));
+				fprintf(f, "remote %s ",
+					format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
 		}
 	} else if (tb[IFLA_VXLAN_GROUP6]) {
 		struct in6_addr addr;
-
 		memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_GROUP6]), sizeof(struct in6_addr));
-		if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) {
+		if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0) {
 			if (IN6_IS_ADDR_MULTICAST(&addr))
-				print_string(PRINT_ANY,
-					     "group6",
-					     "group %s ",
-					     format_host(AF_INET6,
-							 sizeof(struct in6_addr),
-							 &addr));
+				fprintf(f, "group %s ",
+					format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1)));
 			else
-				print_string(PRINT_ANY,
-					     "remote6",
-					     "remote %s ",
-					     format_host(AF_INET6,
-							 sizeof(struct in6_addr),
-							 &addr));
+				fprintf(f, "remote %s ",
+					format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1)));
 		}
 	}
 
 	if (tb[IFLA_VXLAN_LOCAL]) {
 		__be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_LOCAL]);
-
 		if (addr)
-			print_string(PRINT_ANY,
-				     "local",
-				     "local %s ",
-				     format_host(AF_INET, 4, &addr));
+			fprintf(f, "local %s ",
+				format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
 	} else if (tb[IFLA_VXLAN_LOCAL6]) {
 		struct in6_addr addr;
-
 		memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_LOCAL6]), sizeof(struct in6_addr));
-		if (!IN6_IS_ADDR_UNSPECIFIED(&addr))
-			print_string(PRINT_ANY,
-				     "local6",
-				     "local %s ",
-				     format_host(AF_INET6,
-						 sizeof(struct in6_addr),
-						 &addr));
+		if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0)
+			fprintf(f, "local %s ",
+				format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1)));
 	}
 
-	if (tb[IFLA_VXLAN_LINK]) {
-		unsigned int link = rta_getattr_u32(tb[IFLA_VXLAN_LINK]);
+	if (tb[IFLA_VXLAN_LINK] &&
+	    (link = rta_getattr_u32(tb[IFLA_VXLAN_LINK]))) {
+		const char *n = if_indextoname(link, s2);
 
-		if (link) {
-			print_string(PRINT_ANY, "link", "dev %s ",
-				     ll_index_to_name(link));
-		}
+		if (n)
+			fprintf(f, "dev %s ", n);
+		else
+			fprintf(f, "dev %u ", link);
 	}
 
 	if (tb[IFLA_VXLAN_PORT_RANGE]) {
 		const struct ifla_vxlan_port_range *r
 			= RTA_DATA(tb[IFLA_VXLAN_PORT_RANGE]);
-		if (is_json_context()) {
-			open_json_object("port_range");
-			print_uint(PRINT_JSON, "low", NULL, ntohs(r->low));
-			print_uint(PRINT_JSON, "high", NULL, ntohs(r->high));
-			close_json_object();
-		} else {
-			fprintf(f, "srcport %u %u ",
-				ntohs(r->low), ntohs(r->high));
-		}
+		fprintf(f, "srcport %u %u ", ntohs(r->low), ntohs(r->high));
 	}
 
 	if (tb[IFLA_VXLAN_PORT])
-		print_uint(PRINT_ANY,
-			   "port",
-			   "dstport %u ",
-			   rta_getattr_be16(tb[IFLA_VXLAN_PORT]));
+		fprintf(f, "dstport %u ",
+			ntohs(rta_getattr_u16(tb[IFLA_VXLAN_PORT])));
 
-	if (tb[IFLA_VXLAN_LEARNING]) {
-		__u8 learning = rta_getattr_u8(tb[IFLA_VXLAN_LEARNING]);
-
-		print_bool(PRINT_JSON, "learning", NULL, learning);
-		if (!learning)
-			print_bool(PRINT_FP, NULL, "nolearning ", true);
-	}
+	if (tb[IFLA_VXLAN_LEARNING] &&
+	    !rta_getattr_u8(tb[IFLA_VXLAN_LEARNING]))
+		fputs("nolearning ", f);
 
 	if (tb[IFLA_VXLAN_PROXY] && rta_getattr_u8(tb[IFLA_VXLAN_PROXY]))
-		print_bool(PRINT_ANY, "proxy", "proxy ", true);
+		fputs("proxy ", f);
 
 	if (tb[IFLA_VXLAN_RSC] && rta_getattr_u8(tb[IFLA_VXLAN_RSC]))
-		print_bool(PRINT_ANY, "rsc", "rsc ", true);
+		fputs("rsc ", f);
 
 	if (tb[IFLA_VXLAN_L2MISS] && rta_getattr_u8(tb[IFLA_VXLAN_L2MISS]))
-		print_bool(PRINT_ANY, "l2miss", "l2miss ", true);
+		fputs("l2miss ", f);
 
 	if (tb[IFLA_VXLAN_L3MISS] && rta_getattr_u8(tb[IFLA_VXLAN_L3MISS]))
-		print_bool(PRINT_ANY, "l3miss", "l3miss ", true);
+		fputs("l3miss ", f);
 
-	if (tb[IFLA_VXLAN_TOS])
-		tos = rta_getattr_u8(tb[IFLA_VXLAN_TOS]);
-	if (tos) {
-		if (is_json_context() || tos != 1)
-			print_0xhex(PRINT_ANY, "tos", "tos %#llx ", tos);
+	if (tb[IFLA_VXLAN_TOS] &&
+	    (tos = rta_getattr_u8(tb[IFLA_VXLAN_TOS]))) {
+		if (tos == 1)
+			fprintf(f, "tos inherit ");
 		else
-			print_string(PRINT_FP, NULL, "tos %s ", "inherit");
+			fprintf(f, "tos %#x ", tos);
 	}
 
-	if (tb[IFLA_VXLAN_TTL_INHERIT] &&
-	    rta_getattr_u8(tb[IFLA_VXLAN_TTL_INHERIT])) {
-		print_string(PRINT_FP, NULL, "ttl %s ", "inherit");
-	} else if (tb[IFLA_VXLAN_TTL]) {
-		ttl = rta_getattr_u8(tb[IFLA_VXLAN_TTL]);
-		if (is_json_context() || ttl)
-			print_uint(PRINT_ANY, "ttl", "ttl %u ", ttl);
-		else
-			print_string(PRINT_FP, NULL, "ttl %s ", "auto");
-	}
-
-	if (tb[IFLA_VXLAN_DF]) {
-		enum ifla_vxlan_df df = rta_getattr_u8(tb[IFLA_VXLAN_DF]);
-
-		if (df == VXLAN_DF_UNSET)
-			print_string(PRINT_JSON, "df", "df %s ", "unset");
-		else if (df == VXLAN_DF_SET)
-			print_string(PRINT_ANY, "df", "df %s ", "set");
-		else if (df == VXLAN_DF_INHERIT)
-			print_string(PRINT_ANY, "df", "df %s ", "inherit");
-	}
-
-	if (tb[IFLA_VXLAN_LABEL]) {
-		__u32 label = rta_getattr_u32(tb[IFLA_VXLAN_LABEL]);
-
-		if (label)
-			print_0xhex(PRINT_ANY, "label",
-				    "flowlabel %#llx ", ntohl(label));
+	if (tb[IFLA_VXLAN_TTL]) {
+		__u8 ttl = rta_getattr_u8(tb[IFLA_VXLAN_TTL]);
+		if (ttl)
+			fprintf(f, "ttl %d ", ttl);
 	}
 
 	if (tb[IFLA_VXLAN_AGEING]) {
 		__u32 age = rta_getattr_u32(tb[IFLA_VXLAN_AGEING]);
-
 		if (age == 0)
-			print_uint(PRINT_ANY, "ageing", "ageing none ", 0);
+			fprintf(f, "ageing none ");
 		else
-			print_uint(PRINT_ANY, "ageing", "ageing %u ", age);
+			fprintf(f, "ageing %u ", age);
 	}
 
 	if (tb[IFLA_VXLAN_LIMIT] &&
 	    ((maxaddr = rta_getattr_u32(tb[IFLA_VXLAN_LIMIT])) != 0))
-		print_uint(PRINT_ANY, "limit", "maxaddr %u ", maxaddr);
+		    fprintf(f, "maxaddr %u ", maxaddr);
 
-	if (tb[IFLA_VXLAN_UDP_CSUM]) {
-		__u8 udp_csum = rta_getattr_u8(tb[IFLA_VXLAN_UDP_CSUM]);
+	if (tb[IFLA_VXLAN_UDP_CSUM] && rta_getattr_u8(tb[IFLA_VXLAN_UDP_CSUM]))
+		fputs("udpcsum ", f);
 
-		if (is_json_context()) {
-			print_bool(PRINT_ANY, "udp_csum", NULL, udp_csum);
-		} else {
-			if (!udp_csum)
-				fputs("no", f);
-			fputs("udpcsum ", f);
-		}
-	}
+	if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX] &&
+	    rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]))
+		fputs("udp6zerocsumtx ", f);
 
-	if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]) {
-		__u8 csum6 = rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]);
-
-		if (is_json_context()) {
-			print_bool(PRINT_ANY,
-				   "udp_zero_csum6_tx", NULL, csum6);
-		} else {
-			if (!csum6)
-				fputs("no", f);
-			fputs("udp6zerocsumtx ", f);
-		}
-	}
-
-	if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]) {
-		__u8 csum6 = rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]);
-
-		if (is_json_context()) {
-			print_bool(PRINT_ANY,
-				   "udp_zero_csum6_rx",
-				   NULL,
-				   csum6);
-		} else {
-			if (!csum6)
-				fputs("no", f);
-			fputs("udp6zerocsumrx ", f);
-		}
-	}
+	if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX] &&
+	    rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]))
+		fputs("udp6zerocsumrx ", f);
 
 	if (tb[IFLA_VXLAN_REMCSUM_TX] &&
 	    rta_getattr_u8(tb[IFLA_VXLAN_REMCSUM_TX]))
-		print_bool(PRINT_ANY, "remcsum_tx", "remcsumtx ", true);
+		fputs("remcsumtx ", f);
 
 	if (tb[IFLA_VXLAN_REMCSUM_RX] &&
 	    rta_getattr_u8(tb[IFLA_VXLAN_REMCSUM_RX]))
-		print_bool(PRINT_ANY, "remcsum_rx", "remcsumrx ", true);
+		fputs("remcsumrx ", f);
+
+	if (tb[IFLA_VXLAN_COLLECT_METADATA] &&
+	    rta_getattr_u8(tb[IFLA_VXLAN_COLLECT_METADATA]))
+		fputs("external ", f);
 
 	if (tb[IFLA_VXLAN_GBP])
-		print_bool(PRINT_ANY, "gbp", "gbp ", true);
-	if (tb[IFLA_VXLAN_GPE])
-		print_bool(PRINT_ANY, "gpe", "gpe ", true);
+		fputs("gbp ", f);
 }
 
 static void vxlan_print_help(struct link_util *lu, int argc, char **argv,
-			     FILE *f)
+	FILE *f)
 {
 	print_explain(f);
 }
diff --git a/ip/iplink_xdp.c b/ip/iplink_xdp.c
deleted file mode 100644
index 4a490bc..0000000
--- a/ip/iplink_xdp.c
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * iplink_xdp.c XDP program loader
- *
- *              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:     Daniel Borkmann <daniel@iogearbox.net>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <linux/bpf.h>
-
-#include "bpf_util.h"
-#include "utils.h"
-#include "ip_common.h"
-
-extern int force;
-
-struct xdp_req {
-	struct iplink_req *req;
-	__u32 flags;
-};
-
-static void xdp_ebpf_cb(void *raw, int fd, const char *annotation)
-{
-	struct xdp_req *xdp = raw;
-	struct iplink_req *req = xdp->req;
-	struct rtattr *xdp_attr;
-
-	xdp_attr = addattr_nest(&req->n, sizeof(*req), IFLA_XDP);
-	addattr32(&req->n, sizeof(*req), IFLA_XDP_FD, fd);
-	if (xdp->flags)
-		addattr32(&req->n, sizeof(*req), IFLA_XDP_FLAGS, xdp->flags);
-	addattr_nest_end(&req->n, xdp_attr);
-}
-
-static const struct bpf_cfg_ops bpf_cb_ops = {
-	.ebpf_cb = xdp_ebpf_cb,
-};
-
-static int xdp_delete(struct xdp_req *xdp)
-{
-	xdp_ebpf_cb(xdp, -1, NULL);
-	return 0;
-}
-
-int xdp_parse(int *argc, char ***argv, struct iplink_req *req,
-	      const char *ifname, bool generic, bool drv, bool offload)
-{
-	struct bpf_cfg_in cfg = {
-		.type = BPF_PROG_TYPE_XDP,
-		.argc = *argc,
-		.argv = *argv,
-	};
-	struct xdp_req xdp = {
-		.req = req,
-	};
-
-	if (offload) {
-		int ifindex = ll_name_to_index(ifname);
-
-		if (!ifindex)
-			incomplete_command();
-		cfg.ifindex = ifindex;
-	}
-
-	if (!force)
-		xdp.flags |= XDP_FLAGS_UPDATE_IF_NOEXIST;
-	if (generic)
-		xdp.flags |= XDP_FLAGS_SKB_MODE;
-	if (drv)
-		xdp.flags |= XDP_FLAGS_DRV_MODE;
-	if (offload)
-		xdp.flags |= XDP_FLAGS_HW_MODE;
-
-	if (*argc == 1) {
-		if (strcmp(**argv, "none") == 0 ||
-		    strcmp(**argv, "off") == 0)
-			return xdp_delete(&xdp);
-	}
-
-	if (bpf_parse_and_load_common(&cfg, &bpf_cb_ops, &xdp))
-		return -1;
-
-	*argc = cfg.argc;
-	*argv = cfg.argv;
-	return 0;
-}
-
-static void xdp_dump_json_one(struct rtattr *tb[IFLA_XDP_MAX + 1], __u32 attr,
-			      __u8 mode)
-{
-	if (!tb[attr])
-		return;
-
-	open_json_object(NULL);
-	print_uint(PRINT_JSON, "mode", NULL, mode);
-	bpf_dump_prog_info(NULL, rta_getattr_u32(tb[attr]));
-	close_json_object();
-}
-
-static void xdp_dump_json(struct rtattr *tb[IFLA_XDP_MAX + 1])
-{
-	__u32 prog_id = 0;
-	__u8 mode;
-
-	mode = rta_getattr_u8(tb[IFLA_XDP_ATTACHED]);
-	if (tb[IFLA_XDP_PROG_ID])
-		prog_id = rta_getattr_u32(tb[IFLA_XDP_PROG_ID]);
-
-	open_json_object("xdp");
-	print_uint(PRINT_JSON, "mode", NULL, mode);
-	if (prog_id)
-		bpf_dump_prog_info(NULL, prog_id);
-
-	open_json_array(PRINT_JSON, "attached");
-	if (tb[IFLA_XDP_SKB_PROG_ID] ||
-	    tb[IFLA_XDP_DRV_PROG_ID] ||
-	    tb[IFLA_XDP_HW_PROG_ID]) {
-		xdp_dump_json_one(tb, IFLA_XDP_SKB_PROG_ID, XDP_ATTACHED_SKB);
-		xdp_dump_json_one(tb, IFLA_XDP_DRV_PROG_ID, XDP_ATTACHED_DRV);
-		xdp_dump_json_one(tb, IFLA_XDP_HW_PROG_ID, XDP_ATTACHED_HW);
-	} else if (tb[IFLA_XDP_PROG_ID]) {
-		/* Older kernel - use IFLA_XDP_PROG_ID */
-		xdp_dump_json_one(tb, IFLA_XDP_PROG_ID, mode);
-	}
-	close_json_array(PRINT_JSON, NULL);
-
-	close_json_object();
-}
-
-static void xdp_dump_prog_one(FILE *fp, struct rtattr *tb[IFLA_XDP_MAX + 1],
-			      __u32 attr, bool link, bool details,
-			      const char *pfx)
-{
-	__u32 prog_id;
-
-	if (!tb[attr])
-		return;
-
-	prog_id = rta_getattr_u32(tb[attr]);
-	if (!details) {
-		if (prog_id && !link && attr == IFLA_XDP_PROG_ID)
-			fprintf(fp, "/id:%u", prog_id);
-		return;
-	}
-
-	if (prog_id) {
-		fprintf(fp, "%s    prog/xdp%s ", _SL_, pfx);
-		bpf_dump_prog_info(fp, prog_id);
-	}
-}
-
-void xdp_dump(FILE *fp, struct rtattr *xdp, bool link, bool details)
-{
-	struct rtattr *tb[IFLA_XDP_MAX + 1];
-	__u8 mode;
-
-	parse_rtattr_nested(tb, IFLA_XDP_MAX, xdp);
-
-	if (!tb[IFLA_XDP_ATTACHED])
-		return;
-
-	mode = rta_getattr_u8(tb[IFLA_XDP_ATTACHED]);
-	if (mode == XDP_ATTACHED_NONE)
-		return;
-	else if (is_json_context())
-		return details ? (void)0 : xdp_dump_json(tb);
-	else if (details && link)
-		/* don't print mode */;
-	else if (mode == XDP_ATTACHED_DRV)
-		fprintf(fp, "xdp");
-	else if (mode == XDP_ATTACHED_SKB)
-		fprintf(fp, "xdpgeneric");
-	else if (mode == XDP_ATTACHED_HW)
-		fprintf(fp, "xdpoffload");
-	else if (mode == XDP_ATTACHED_MULTI)
-		fprintf(fp, "xdpmulti");
-	else
-		fprintf(fp, "xdp[%u]", mode);
-
-	xdp_dump_prog_one(fp, tb, IFLA_XDP_PROG_ID, link, details, "");
-
-	if (mode == XDP_ATTACHED_MULTI) {
-		xdp_dump_prog_one(fp, tb, IFLA_XDP_SKB_PROG_ID, link, details,
-				  "generic");
-		xdp_dump_prog_one(fp, tb, IFLA_XDP_DRV_PROG_ID, link, details,
-				  "drv");
-		xdp_dump_prog_one(fp, tb, IFLA_XDP_HW_PROG_ID, link, details,
-				  "offload");
-	}
-
-	if (!details || !link)
-		fprintf(fp, " ");
-}
diff --git a/ip/iplink_xstats.c b/ip/iplink_xstats.c
deleted file mode 100644
index c64e688..0000000
--- a/ip/iplink_xstats.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * iplink_stats.c       Extended statistics commands
- *
- *              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:     Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <linux/if_link.h>
-#include <netinet/ether.h>
-
-#include "utils.h"
-#include "ip_common.h"
-
-static void print_explain(FILE *f)
-{
-	fprintf(f, "Usage: ... xstats type TYPE [ ARGS ]\n");
-}
-
-int iplink_ifla_xstats(int argc, char **argv)
-{
-	struct link_util *lu = NULL;
-	__u32 filt_mask;
-
-	if (!argc) {
-		fprintf(stderr, "xstats: missing argument\n");
-		return -1;
-	}
-
-	if (matches(*argv, "type") == 0) {
-		NEXT_ARG();
-		lu = get_link_kind(*argv);
-		if (!lu)
-			invarg("invalid type", *argv);
-	} else if (matches(*argv, "help") == 0) {
-		print_explain(stdout);
-		return 0;
-	} else {
-		invarg("unknown argument", *argv);
-	}
-
-	if (!lu) {
-		print_explain(stderr);
-		return -1;
-	}
-
-	if (!lu->print_ifla_xstats) {
-		fprintf(stderr, "xstats: link type %s doesn't support xstats\n",
-			lu->id);
-		return -1;
-	}
-
-	if (lu->parse_ifla_xstats &&
-	    lu->parse_ifla_xstats(lu, argc-1, argv+1))
-		return -1;
-
-	if (strstr(lu->id, "_slave"))
-		filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE);
-	else
-		filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS);
-
-	if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
-		perror("Cannont send dump request");
-		return -1;
-	}
-
-	new_json_obj(json);
-	if (rtnl_dump_filter(&rth, lu->print_ifla_xstats, stdout) < 0) {
-		delete_json_obj();
-		fprintf(stderr, "Dump terminated\n");
-		return -1;
-	}
-	delete_json_obj();
-
-	return 0;
-}
diff --git a/ip/ipmacsec.c b/ip/ipmacsec.c
deleted file mode 100644
index ad6ad7d..0000000
--- a/ip/ipmacsec.c
+++ /dev/null
@@ -1,1381 +0,0 @@
-/*
- * ipmacsec.c		"ip macsec".
- *
- *		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:	Sabrina Dubroca <sd@queasysnail.net>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <linux/genetlink.h>
-#include <linux/if_ether.h>
-#include <linux/if_macsec.h>
-
-#include "rt_names.h"
-#include "utils.h"
-#include "ip_common.h"
-#include "ll_map.h"
-#include "libgenl.h"
-
-static const char * const values_on_off[] = { "off", "on" };
-
-static const char * const validate_str[] = {
-	[MACSEC_VALIDATE_DISABLED] = "disabled",
-	[MACSEC_VALIDATE_CHECK] = "check",
-	[MACSEC_VALIDATE_STRICT] = "strict",
-};
-
-struct sci {
-	__u64 sci;
-	__u16 port;
-	char abuf[6];
-};
-
-struct sa_desc {
-	__u8 an;
-	__u32 pn;
-	__u8 key_id[MACSEC_KEYID_LEN];
-	__u32 key_len;
-	__u8 key[MACSEC_MAX_KEY_LEN];
-	__u8 active;
-};
-
-struct cipher_args {
-	__u64 id;
-	__u8 icv_len;
-};
-
-struct txsc_desc {
-	int ifindex;
-	__u64 sci;
-	__be16 port;
-	struct cipher_args cipher;
-	__u32 window;
-	enum macsec_validation_type validate;
-	__u8 encoding_sa;
-};
-
-struct rxsc_desc {
-	int ifindex;
-	__u64 sci;
-	__u8 active;
-};
-
-#define MACSEC_BUFLEN 1024
-
-
-/* netlink socket */
-static struct rtnl_handle genl_rth;
-static int genl_family = -1;
-
-#define MACSEC_GENL_REQ(_req, _bufsiz, _cmd, _flags) \
-	GENL_REQUEST(_req, _bufsiz, genl_family, 0, MACSEC_GENL_VERSION, \
-		     _cmd, _flags)
-
-
-static void ipmacsec_usage(void)
-{
-	fprintf(stderr,
-		"Usage: ip macsec add DEV tx sa { 0..3 } [ OPTS ] key ID KEY\n"
-		"       ip macsec set DEV tx sa { 0..3 } [ OPTS ]\n"
-		"       ip macsec del DEV tx sa { 0..3 }\n"
-		"       ip macsec add DEV rx SCI [ on | off ]\n"
-		"       ip macsec set DEV rx SCI [ on | off ]\n"
-		"       ip macsec del DEV rx SCI\n"
-		"       ip macsec add DEV rx SCI sa { 0..3 } [ OPTS ] key ID KEY\n"
-		"       ip macsec set DEV rx SCI sa { 0..3 } [ OPTS ]\n"
-		"       ip macsec del DEV rx SCI sa { 0..3 }\n"
-		"       ip macsec show\n"
-		"       ip macsec show DEV\n"
-		"where  OPTS := [ pn <u32> ] [ on | off ]\n"
-		"       ID   := 128-bit hex string\n"
-		"       KEY  := 128-bit or 256-bit hex string\n"
-		"       SCI  := { sci <u64> | port { 1..2^16-1 } address <lladdr> }\n");
-
-	exit(-1);
-}
-
-static int one_of(const char *msg, const char *realval, const char * const *list,
-		  size_t len, int *index)
-{
-	int i;
-
-	for (i = 0; i < len; i++) {
-		if (matches(realval, list[i]) == 0) {
-			*index = i;
-			return 0;
-		}
-	}
-
-	fprintf(stderr, "Error: argument of \"%s\" must be one of ", msg);
-	for (i = 0; i < len; i++)
-		fprintf(stderr, "\"%s\", ", list[i]);
-	fprintf(stderr, "not \"%s\"\n", realval);
-	return -1;
-}
-
-static int get_an(__u8 *val, const char *arg)
-{
-	int ret = get_u8(val, arg, 0);
-
-	if (ret)
-		return ret;
-
-	if (*val > 3)
-		return -1;
-
-	return 0;
-}
-
-static int get_sci(__u64 *sci, const char *arg)
-{
-	return get_be64(sci, arg, 16);
-}
-
-static int get_port(__be16 *port, const char *arg)
-{
-	return get_be16(port, arg, 0);
-}
-
-#define _STR(a) #a
-#define STR(a) _STR(a)
-
-static void get_icvlen(__u8 *icvlen, char *arg)
-{
-	int ret = get_u8(icvlen, arg, 10);
-
-	if (ret)
-		invarg("expected ICV length", arg);
-
-	if (*icvlen < MACSEC_MIN_ICV_LEN || *icvlen > MACSEC_STD_ICV_LEN)
-		invarg("ICV length must be in the range {"
-		       STR(MACSEC_MIN_ICV_LEN) ".." STR(MACSEC_STD_ICV_LEN)
-		       "}", arg);
-}
-
-static bool get_sa(int *argcp, char ***argvp, __u8 *an)
-{
-	int argc = *argcp;
-	char **argv = *argvp;
-	int ret;
-
-	if (argc <= 0 || strcmp(*argv, "sa") != 0)
-		return false;
-
-	NEXT_ARG();
-	ret = get_an(an, *argv);
-	if (ret)
-		invarg("expected an { 0..3 }", *argv);
-	argc--; argv++;
-
-	*argvp = argv;
-	*argcp = argc;
-	return true;
-}
-
-static int parse_sa_args(int *argcp, char ***argvp, struct sa_desc *sa)
-{
-	int argc = *argcp;
-	char **argv = *argvp;
-	int ret;
-	bool active_set = false;
-
-	while (argc > 0) {
-		if (strcmp(*argv, "pn") == 0) {
-			if (sa->pn != 0)
-				duparg2("pn", "pn");
-			NEXT_ARG();
-			ret = get_u32(&sa->pn, *argv, 0);
-			if (ret)
-				invarg("expected pn", *argv);
-			if (sa->pn == 0)
-				invarg("expected pn != 0", *argv);
-		} else if (strcmp(*argv, "key") == 0) {
-			unsigned int len;
-
-			NEXT_ARG();
-			if (!hexstring_a2n(*argv, sa->key_id, MACSEC_KEYID_LEN,
-					   &len))
-				invarg("expected key id", *argv);
-			NEXT_ARG();
-			if (!hexstring_a2n(*argv, sa->key, MACSEC_MAX_KEY_LEN,
-					   &sa->key_len))
-				invarg("expected key", *argv);
-		} else if (strcmp(*argv, "on") == 0) {
-			if (active_set)
-				duparg2("on/off", "on");
-			sa->active = true;
-			active_set = true;
-		} else if (strcmp(*argv, "off") == 0) {
-			if (active_set)
-				duparg2("on/off", "off");
-			sa->active = false;
-			active_set = true;
-		} else {
-			fprintf(stderr, "macsec: unknown command \"%s\"?\n",
-				*argv);
-			ipmacsec_usage();
-		}
-
-		argv++; argc--;
-	}
-
-	*argvp = argv;
-	*argcp = argc;
-	return 0;
-}
-
-static __u64 make_sci(char *addr, __be16 port)
-{
-	__u64 sci;
-
-	memcpy(&sci, addr, ETH_ALEN);
-	memcpy(((char *)&sci) + ETH_ALEN, &port, sizeof(port));
-
-	return sci;
-}
-
-static bool sci_complete(bool sci, bool port, bool addr, bool port_only)
-{
-	return sci || (port && (addr || port_only));
-}
-
-static int get_sci_portaddr(struct sci *sci, int *argcp, char ***argvp,
-			    bool port_only, bool optional)
-{
-	int argc = *argcp;
-	char **argv = *argvp;
-	int ret;
-	bool p = false, a = false, s = false;
-
-	while (argc > 0) {
-		if (strcmp(*argv, "sci") == 0) {
-			if (p)
-				invarg("expected address", *argv);
-			if (a)
-				invarg("expected port", *argv);
-			NEXT_ARG();
-			ret = get_sci(&sci->sci, *argv);
-			if (ret)
-				invarg("expected sci", *argv);
-			s = true;
-		} else if (strcmp(*argv, "port") == 0) {
-			NEXT_ARG();
-			ret = get_port(&sci->port, *argv);
-			if (ret)
-				invarg("expected port", *argv);
-			if (sci->port == 0)
-				invarg("expected port != 0", *argv);
-			p = true;
-		} else if (strcmp(*argv, "address") == 0) {
-			NEXT_ARG();
-			ret = ll_addr_a2n(sci->abuf, sizeof(sci->abuf), *argv);
-			if (ret < 0)
-				invarg("expected lladdr", *argv);
-			a = true;
-		} else if (optional) {
-			break;
-		} else {
-			invarg("expected sci, port, or address", *argv);
-		}
-
-		argv++; argc--;
-
-		if (sci_complete(s, p, a, port_only))
-			break;
-	}
-
-	if (!optional && !sci_complete(s, p, a, port_only))
-		return -1;
-
-	if (p && a)
-		sci->sci = make_sci(sci->abuf, sci->port);
-
-	*argvp = argv;
-	*argcp = argc;
-
-	return p || a || s;
-}
-
-static bool parse_rxsci(int *argcp, char ***argvp, struct rxsc_desc *rxsc,
-			struct sa_desc *rxsa)
-{
-	struct sci sci = { 0 };
-
-	if (*argcp == 0 ||
-	    get_sci_portaddr(&sci, argcp, argvp, false, false) < 0) {
-		fprintf(stderr, "expected sci\n");
-		ipmacsec_usage();
-	}
-
-	rxsc->sci = sci.sci;
-
-	return get_sa(argcp, argvp, &rxsa->an);
-}
-
-static int parse_rxsci_args(int *argcp, char ***argvp, struct rxsc_desc *rxsc)
-{
-	int argc = *argcp;
-	char **argv = *argvp;
-	bool active_set = false;
-
-	while (argc > 0) {
-		if (strcmp(*argv, "on") == 0) {
-			if (active_set)
-				duparg2("on/off", "on");
-			rxsc->active = true;
-			active_set = true;
-		} else if (strcmp(*argv, "off") == 0) {
-			if (active_set)
-				duparg2("on/off", "off");
-			rxsc->active = false;
-			active_set = true;
-		} else {
-			fprintf(stderr, "macsec: unknown command \"%s\"?\n",
-				*argv);
-			ipmacsec_usage();
-		}
-
-		argv++; argc--;
-	}
-
-	*argvp = argv;
-	*argcp = argc;
-	return 0;
-}
-
-enum cmd {
-	CMD_ADD,
-	CMD_DEL,
-	CMD_UPD,
-	__CMD_MAX
-};
-
-static const enum macsec_nl_commands macsec_commands[__CMD_MAX][2][2] = {
-	[CMD_ADD] = {
-		[0] = {-1, MACSEC_CMD_ADD_RXSC},
-		[1] = {MACSEC_CMD_ADD_TXSA, MACSEC_CMD_ADD_RXSA},
-	},
-	[CMD_UPD] = {
-		[0] = {-1, MACSEC_CMD_UPD_RXSC},
-		[1] = {MACSEC_CMD_UPD_TXSA, MACSEC_CMD_UPD_RXSA},
-	},
-	[CMD_DEL] = {
-		[0] = {-1, MACSEC_CMD_DEL_RXSC},
-		[1] = {MACSEC_CMD_DEL_TXSA, MACSEC_CMD_DEL_RXSA},
-	},
-};
-
-static int do_modify_nl(enum cmd c, enum macsec_nl_commands cmd, int ifindex,
-			struct rxsc_desc *rxsc, struct sa_desc *sa)
-{
-	struct rtattr *attr_sa;
-
-	MACSEC_GENL_REQ(req, MACSEC_BUFLEN, cmd, NLM_F_REQUEST);
-
-	addattr32(&req.n, MACSEC_BUFLEN, MACSEC_ATTR_IFINDEX, ifindex);
-	if (rxsc) {
-		struct rtattr *attr_rxsc;
-
-		attr_rxsc = addattr_nest(&req.n, MACSEC_BUFLEN,
-					 MACSEC_ATTR_RXSC_CONFIG);
-		addattr64(&req.n, MACSEC_BUFLEN,
-			  MACSEC_RXSC_ATTR_SCI, rxsc->sci);
-		if (c != CMD_DEL && rxsc->active != 0xff)
-			addattr8(&req.n, MACSEC_BUFLEN,
-				 MACSEC_RXSC_ATTR_ACTIVE, rxsc->active);
-
-		addattr_nest_end(&req.n, attr_rxsc);
-	}
-
-	if (sa->an == 0xff)
-		goto talk;
-
-	attr_sa = addattr_nest(&req.n, MACSEC_BUFLEN, MACSEC_ATTR_SA_CONFIG);
-
-	addattr8(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_AN, sa->an);
-
-	if (c != CMD_DEL) {
-		if (sa->pn)
-			addattr32(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_PN,
-				  sa->pn);
-
-		if (sa->key_len) {
-			addattr_l(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_KEYID,
-				  sa->key_id, MACSEC_KEYID_LEN);
-			addattr_l(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_KEY,
-				  sa->key, sa->key_len);
-		}
-
-		if (sa->active != 0xff) {
-			addattr8(&req.n, MACSEC_BUFLEN,
-				 MACSEC_SA_ATTR_ACTIVE, sa->active);
-		}
-	}
-
-	addattr_nest_end(&req.n, attr_sa);
-
-talk:
-	if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
-		return -2;
-
-	return 0;
-}
-
-static bool check_sa_args(enum cmd c, struct sa_desc *sa)
-{
-	if (c == CMD_ADD) {
-		if (!sa->key_len) {
-			fprintf(stderr, "cannot create SA without key\n");
-			return -1;
-		}
-
-		if (sa->pn == 0) {
-			fprintf(stderr, "must specify a packet number != 0\n");
-			return -1;
-		}
-	} else if (c == CMD_UPD) {
-		if (sa->key_len) {
-			fprintf(stderr, "cannot change key on SA\n");
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-static int do_modify_txsa(enum cmd c, int argc, char **argv, int ifindex)
-{
-	struct sa_desc txsa = {0};
-	enum macsec_nl_commands cmd;
-
-	txsa.an = 0xff;
-	txsa.active = 0xff;
-
-	if (argc == 0 || !get_sa(&argc, &argv, &txsa.an))
-		ipmacsec_usage();
-
-	if (c == CMD_DEL)
-		goto modify;
-
-	if (parse_sa_args(&argc, &argv, &txsa))
-		return -1;
-
-	if (check_sa_args(c, &txsa))
-		return -1;
-
-modify:
-	cmd = macsec_commands[c][1][0];
-	return do_modify_nl(c, cmd, ifindex, NULL, &txsa);
-}
-
-static int do_modify_rxsci(enum cmd c, int argc, char **argv, int ifindex)
-{
-	struct rxsc_desc rxsc = {0};
-	struct sa_desc rxsa = {0};
-	bool sa_set;
-	enum macsec_nl_commands cmd;
-
-	rxsc.ifindex = ifindex;
-	rxsc.active = 0xff;
-	rxsa.an = 0xff;
-	rxsa.active = 0xff;
-
-	sa_set = parse_rxsci(&argc, &argv, &rxsc, &rxsa);
-
-	if (c == CMD_DEL)
-		goto modify;
-
-	if (sa_set && (parse_sa_args(&argc, &argv, &rxsa) ||
-		       check_sa_args(c, &rxsa)))
-		return -1;
-	if (!sa_set && parse_rxsci_args(&argc, &argv, &rxsc))
-		return -1;
-
-modify:
-	cmd = macsec_commands[c][sa_set][1];
-	return do_modify_nl(c, cmd, rxsc.ifindex, &rxsc, &rxsa);
-}
-
-static int do_modify(enum cmd c, int argc, char **argv)
-{
-	int ifindex;
-
-	if (argc == 0)
-		ipmacsec_usage();
-
-	ifindex = ll_name_to_index(*argv);
-	if (!ifindex) {
-		fprintf(stderr, "Device \"%s\" does not exist.\n", *argv);
-		return -1;
-	}
-	argc--; argv++;
-
-	if (argc == 0)
-		ipmacsec_usage();
-
-	if (strcmp(*argv, "tx") == 0)
-		return do_modify_txsa(c, argc-1, argv+1, ifindex);
-	if (strcmp(*argv, "rx") == 0)
-		return do_modify_rxsci(c, argc-1, argv+1, ifindex);
-
-	ipmacsec_usage();
-	return -1;
-}
-
-/* dump/show */
-static struct {
-	int ifindex;
-	__u64 sci;
-} filter;
-
-static int validate_dump(struct rtattr **attrs)
-{
-	return attrs[MACSEC_ATTR_IFINDEX] && attrs[MACSEC_ATTR_SECY] &&
-	       attrs[MACSEC_ATTR_TXSA_LIST] && attrs[MACSEC_ATTR_RXSC_LIST] &&
-	       attrs[MACSEC_ATTR_TXSC_STATS] && attrs[MACSEC_ATTR_SECY_STATS];
-
-}
-
-static int validate_secy_dump(struct rtattr **attrs)
-{
-	return attrs[MACSEC_SECY_ATTR_SCI] &&
-	       attrs[MACSEC_SECY_ATTR_ENCODING_SA] &&
-	       attrs[MACSEC_SECY_ATTR_CIPHER_SUITE] &&
-	       attrs[MACSEC_SECY_ATTR_ICV_LEN] &&
-	       attrs[MACSEC_SECY_ATTR_PROTECT] &&
-	       attrs[MACSEC_SECY_ATTR_REPLAY] &&
-	       attrs[MACSEC_SECY_ATTR_OPER] &&
-	       attrs[MACSEC_SECY_ATTR_VALIDATE] &&
-	       attrs[MACSEC_SECY_ATTR_ENCRYPT] &&
-	       attrs[MACSEC_SECY_ATTR_INC_SCI] &&
-	       attrs[MACSEC_SECY_ATTR_ES] &&
-	       attrs[MACSEC_SECY_ATTR_SCB];
-}
-
-static void print_flag(struct rtattr *attrs[], const char *desc,
-		       int field)
-{
-	__u8 flag;
-
-	if (!attrs[field])
-		return;
-
-	flag = rta_getattr_u8(attrs[field]);
-	if (is_json_context())
-		print_bool(PRINT_JSON, desc, NULL, flag);
-	else {
-		print_string(PRINT_FP, NULL, "%s ", desc);
-		print_string(PRINT_FP, NULL, "%s ",
-			     flag ? "on" : "off");
-	}
-}
-
-static void print_key(struct rtattr *key)
-{
-	SPRINT_BUF(keyid);
-
-	print_string(PRINT_ANY, "key", " key %s\n",
-		     hexstring_n2a(RTA_DATA(key), RTA_PAYLOAD(key),
-				   keyid, sizeof(keyid)));
-}
-
-#define CIPHER_NAME_GCM_AES_128 "GCM-AES-128"
-#define CIPHER_NAME_GCM_AES_256 "GCM-AES-256"
-#define DEFAULT_CIPHER_NAME CIPHER_NAME_GCM_AES_128
-
-static const char *cs_id_to_name(__u64 cid)
-{
-	switch (cid) {
-	case MACSEC_DEFAULT_CIPHER_ID:
-		return DEFAULT_CIPHER_NAME;
-	case MACSEC_CIPHER_ID_GCM_AES_128:
-	     /* MACSEC_DEFAULT_CIPHER_ALT: */
-		return CIPHER_NAME_GCM_AES_128;
-	case MACSEC_CIPHER_ID_GCM_AES_256:
-		return CIPHER_NAME_GCM_AES_256;
-	default:
-		return "(unknown)";
-	}
-}
-
-static void print_attrs(struct rtattr *attrs[])
-{
-	print_flag(attrs, "protect", MACSEC_SECY_ATTR_PROTECT);
-
-	if (attrs[MACSEC_SECY_ATTR_VALIDATE]) {
-		__u8 val = rta_getattr_u8(attrs[MACSEC_SECY_ATTR_VALIDATE]);
-
-		print_string(PRINT_ANY, "validate",
-			     "validate %s ", validate_str[val]);
-	}
-
-	print_flag(attrs, "sc", MACSEC_RXSC_ATTR_ACTIVE);
-	print_flag(attrs, "sa", MACSEC_SA_ATTR_ACTIVE);
-	print_flag(attrs, "encrypt", MACSEC_SECY_ATTR_ENCRYPT);
-	print_flag(attrs, "send_sci", MACSEC_SECY_ATTR_INC_SCI);
-	print_flag(attrs, "end_station", MACSEC_SECY_ATTR_ES);
-	print_flag(attrs, "scb", MACSEC_SECY_ATTR_SCB);
-	print_flag(attrs, "replay", MACSEC_SECY_ATTR_REPLAY);
-
-	if (attrs[MACSEC_SECY_ATTR_WINDOW]) {
-		__u32 win = rta_getattr_u32(attrs[MACSEC_SECY_ATTR_WINDOW]);
-
-		print_uint(PRINT_ANY, "window", "window %u ", win);
-	}
-
-	if (attrs[MACSEC_SECY_ATTR_CIPHER_SUITE]) {
-		__u64 cid = rta_getattr_u64(attrs[MACSEC_SECY_ATTR_CIPHER_SUITE]);
-
-		print_nl();
-		print_string(PRINT_ANY, "cipher_suite",
-			     "    cipher suite: %s,", cs_id_to_name(cid));
-	}
-
-	if (attrs[MACSEC_SECY_ATTR_ICV_LEN]) {
-		__u8 icv_len = rta_getattr_u8(attrs[MACSEC_SECY_ATTR_ICV_LEN]);
-
-		print_uint(PRINT_ANY, "icv_length",
-		     " using ICV length %u\n", icv_len);
-	}
-}
-
-static __u64 getattr_u64(const struct rtattr *stat)
-{
-	size_t len = RTA_PAYLOAD(stat);
-
-	switch (len) {
-	case sizeof(__u64):
-		return rta_getattr_u64(stat);
-	case sizeof(__u32):
-		return rta_getattr_u32(stat);
-	case sizeof(__u16):
-		return rta_getattr_u16(stat);
-	case sizeof(__u8):
-		return rta_getattr_u8(stat);
-	default:
-		fprintf(stderr, "invalid attribute length %zu\n",
-			len);
-		exit(-1);
-	}
-}
-
-static void print_fp_stats(const char *prefix,
-			   const char *names[], unsigned int num,
-			   struct rtattr *stats[])
-{
-	unsigned int i;
-	int pad;
-
-	printf("%sstats:", prefix);
-
-	for (i = 1; i < num; i++) {
-		if (!names[i])
-			continue;
-		printf(" %s", names[i]);
-	}
-
-	printf("\n%s      ", prefix);
-
-	for (i = 1; i < num; i++) {
-		if (!names[i])
-			continue;
-
-		pad = strlen(names[i]) + 1;
-		if (stats[i])
-			printf("%*llu", pad, getattr_u64(stats[i]));
-		else
-			printf("%*c", pad, '-');
-	}
-	printf("\n");
-}
-
-static void print_json_stats(const char *names[], unsigned int num,
-			     struct rtattr *stats[])
-{
-	unsigned int i;
-
-	for (i = 1; i < num; i++) {
-		if (!names[i] || !stats[i])
-			continue;
-
-		print_u64(PRINT_JSON, names[i],
-			   NULL, getattr_u64(stats[i]));
-	}
-}
-
-static void print_stats(const char *prefix,
-			const char *names[], unsigned int num,
-			struct rtattr *stats[])
-{
-
-	if (is_json_context())
-		print_json_stats(names, num, stats);
-	else
-		print_fp_stats(prefix, names, num, stats);
-}
-
-static const char *txsc_stats_names[NUM_MACSEC_TXSC_STATS_ATTR] = {
-	[MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED] = "OutPktsProtected",
-	[MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED] = "OutPktsEncrypted",
-	[MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED] = "OutOctetsProtected",
-	[MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED] = "OutOctetsEncrypted",
-};
-
-static void print_txsc_stats(const char *prefix, struct rtattr *attr)
-{
-	struct rtattr *stats[MACSEC_TXSC_STATS_ATTR_MAX + 1];
-
-	if (!attr || show_stats == 0)
-		return;
-
-	parse_rtattr_nested(stats, MACSEC_TXSC_STATS_ATTR_MAX, attr);
-
-	print_stats(prefix, txsc_stats_names, NUM_MACSEC_TXSC_STATS_ATTR,
-		    stats);
-}
-
-static const char *secy_stats_names[NUM_MACSEC_SECY_STATS_ATTR] = {
-	[MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED] = "OutPktsUntagged",
-	[MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED] = "InPktsUntagged",
-	[MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG] = "OutPktsTooLong",
-	[MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG] = "InPktsNoTag",
-	[MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG] = "InPktsBadTag",
-	[MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI] = "InPktsUnknownSCI",
-	[MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI] = "InPktsNoSCI",
-	[MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN] = "InPktsOverrun",
-};
-
-static void print_secy_stats(const char *prefix, struct rtattr *attr)
-{
-	struct rtattr *stats[MACSEC_SECY_STATS_ATTR_MAX + 1];
-
-	if (!attr || show_stats == 0)
-		return;
-
-	parse_rtattr_nested(stats, MACSEC_SECY_STATS_ATTR_MAX, attr);
-
-	print_stats(prefix, secy_stats_names,
-		    NUM_MACSEC_SECY_STATS_ATTR, stats);
-}
-
-static const char *rxsa_stats_names[NUM_MACSEC_SA_STATS_ATTR] = {
-	[MACSEC_SA_STATS_ATTR_IN_PKTS_OK] = "InPktsOK",
-	[MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID] = "InPktsInvalid",
-	[MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID] = "InPktsNotValid",
-	[MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA] = "InPktsNotUsingSA",
-	[MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA] = "InPktsUnusedSA",
-};
-
-static void print_rxsa_stats(const char *prefix, struct rtattr *attr)
-{
-	struct rtattr *stats[MACSEC_SA_STATS_ATTR_MAX + 1];
-
-	if (!attr || show_stats == 0)
-		return;
-
-	parse_rtattr_nested(stats, MACSEC_SA_STATS_ATTR_MAX, attr);
-
-	print_stats(prefix, rxsa_stats_names, NUM_MACSEC_SA_STATS_ATTR, stats);
-}
-
-static const char *txsa_stats_names[NUM_MACSEC_SA_STATS_ATTR] = {
-	[MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED] = "OutPktsProtected",
-	[MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED] = "OutPktsEncrypted",
-};
-
-static void print_txsa_stats(const char *prefix, struct rtattr *attr)
-{
-	struct rtattr *stats[MACSEC_SA_STATS_ATTR_MAX + 1];
-
-	if (!attr || show_stats == 0)
-		return;
-
-	parse_rtattr_nested(stats, MACSEC_SA_STATS_ATTR_MAX, attr);
-
-	print_stats(prefix, txsa_stats_names, NUM_MACSEC_SA_STATS_ATTR, stats);
-}
-
-static void print_tx_sc(const char *prefix, __u64 sci, __u8 encoding_sa,
-			struct rtattr *txsc_stats, struct rtattr *secy_stats,
-			struct rtattr *sa)
-{
-	struct rtattr *sa_attr[MACSEC_SA_ATTR_MAX + 1];
-	struct rtattr *a;
-	int rem;
-
-	print_string(PRINT_FP, NULL, "%s", prefix);
-	print_0xhex(PRINT_ANY, "sci",
-		    "TXSC: %016llx", ntohll(sci));
-	print_uint(PRINT_ANY, "encoding_sa",
-		   " on SA %d\n", encoding_sa);
-
-	print_secy_stats(prefix, secy_stats);
-	print_txsc_stats(prefix, txsc_stats);
-
-	open_json_array(PRINT_JSON, "sa_list");
-	rem = RTA_PAYLOAD(sa);
-	for (a = RTA_DATA(sa); RTA_OK(a, rem); a = RTA_NEXT(a, rem)) {
-		bool state;
-
-		open_json_object(NULL);
-		parse_rtattr_nested(sa_attr, MACSEC_SA_ATTR_MAX, a);
-		state = rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_ACTIVE]);
-
-		print_string(PRINT_FP, NULL, "%s", prefix);
-		print_string(PRINT_FP, NULL, "%s", prefix);
-		print_uint(PRINT_ANY, "an", "%d:",
-			   rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_AN]));
-		print_uint(PRINT_ANY, "pn", " PN %u,",
-			   rta_getattr_u32(sa_attr[MACSEC_SA_ATTR_PN]));
-
-		print_bool(PRINT_JSON, "active", NULL, state);
-		print_string(PRINT_FP, NULL,
-			     " state %s,", state ? "on" : "off");
-		print_key(sa_attr[MACSEC_SA_ATTR_KEYID]);
-
-		print_txsa_stats(prefix, sa_attr[MACSEC_SA_ATTR_STATS]);
-		close_json_object();
-	}
-	close_json_array(PRINT_JSON, NULL);
-}
-
-static const char *rxsc_stats_names[NUM_MACSEC_RXSC_STATS_ATTR] = {
-	[MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED] = "InOctetsValidated",
-	[MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED] = "InOctetsDecrypted",
-	[MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED] = "InPktsUnchecked",
-	[MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED] = "InPktsDelayed",
-	[MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK] = "InPktsOK",
-	[MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID] = "InPktsInvalid",
-	[MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE] = "InPktsLate",
-	[MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID] = "InPktsNotValid",
-	[MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA] = "InPktsNotUsingSA",
-	[MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA] = "InPktsUnusedSA",
-};
-
-static void print_rxsc_stats(const char *prefix, struct rtattr *attr)
-{
-	struct rtattr *stats[MACSEC_RXSC_STATS_ATTR_MAX + 1];
-
-	if (!attr || show_stats == 0)
-		return;
-
-	parse_rtattr_nested(stats, MACSEC_RXSC_STATS_ATTR_MAX, attr);
-
-	print_stats(prefix, rxsc_stats_names,
-		    NUM_MACSEC_RXSC_STATS_ATTR, stats);
-}
-
-static void print_rx_sc(const char *prefix, __be64 sci, __u8 active,
-			struct rtattr *rxsc_stats, struct rtattr *sa)
-{
-	struct rtattr *sa_attr[MACSEC_SA_ATTR_MAX + 1];
-	struct rtattr *a;
-	int rem;
-
-	print_string(PRINT_FP, NULL, "%s", prefix);
-	print_0xhex(PRINT_ANY, "sci",
-		    "RXSC: %016llx,", ntohll(sci));
-	print_bool(PRINT_JSON, "active", NULL, active);
-	print_string(PRINT_FP, NULL,
-		     " state %s\n", active ? "on" : "off");
-	print_rxsc_stats(prefix, rxsc_stats);
-
-	open_json_array(PRINT_JSON, "sa_list");
-	rem = RTA_PAYLOAD(sa);
-	for (a = RTA_DATA(sa); RTA_OK(a, rem); a = RTA_NEXT(a, rem)) {
-		bool state;
-
-		open_json_object(NULL);
-		parse_rtattr_nested(sa_attr, MACSEC_SA_ATTR_MAX, a);
-		state = rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_ACTIVE]);
-
-		print_string(PRINT_FP, NULL, "%s", prefix);
-		print_string(PRINT_FP, NULL, "%s", prefix);
-		print_uint(PRINT_ANY, "an", "%u:",
-			   rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_AN]));
-		print_uint(PRINT_ANY, "pn", " PN %u,",
-			   rta_getattr_u32(sa_attr[MACSEC_SA_ATTR_PN]));
-
-		print_bool(PRINT_JSON, "active", NULL, state);
-		print_string(PRINT_FP, NULL, " state %s,",
-			     state ? "on" : "off");
-
-		print_key(sa_attr[MACSEC_SA_ATTR_KEYID]);
-
-		print_rxsa_stats(prefix, sa_attr[MACSEC_SA_ATTR_STATS]);
-		close_json_object();
-	}
-	close_json_array(PRINT_JSON, NULL);
-}
-
-static void print_rxsc_list(struct rtattr *sc)
-{
-	int rem = RTA_PAYLOAD(sc);
-	struct rtattr *c;
-
-	open_json_array(PRINT_JSON, "rx_sc");
-	for (c = RTA_DATA(sc); RTA_OK(c, rem); c = RTA_NEXT(c, rem)) {
-		struct rtattr *sc_attr[MACSEC_RXSC_ATTR_MAX + 1];
-
-		open_json_object(NULL);
-
-		parse_rtattr_nested(sc_attr, MACSEC_RXSC_ATTR_MAX, c);
-		print_rx_sc("    ",
-			    rta_getattr_u64(sc_attr[MACSEC_RXSC_ATTR_SCI]),
-			    rta_getattr_u32(sc_attr[MACSEC_RXSC_ATTR_ACTIVE]),
-			    sc_attr[MACSEC_RXSC_ATTR_STATS],
-			    sc_attr[MACSEC_RXSC_ATTR_SA_LIST]);
-		close_json_object();
-	}
-	close_json_array(PRINT_JSON, NULL);
-}
-
-static int process(struct nlmsghdr *n, void *arg)
-{
-	struct genlmsghdr *ghdr;
-	struct rtattr *attrs[MACSEC_ATTR_MAX + 1];
-	struct rtattr *attrs_secy[MACSEC_SECY_ATTR_MAX + 1];
-	int len = n->nlmsg_len;
-	int ifindex;
-	__u64 sci;
-	__u8 encoding_sa;
-
-	if (n->nlmsg_type != genl_family)
-		return -1;
-
-	len -= NLMSG_LENGTH(GENL_HDRLEN);
-	if (len < 0)
-		return -1;
-
-	ghdr = NLMSG_DATA(n);
-	if (ghdr->cmd != MACSEC_CMD_GET_TXSC)
-		return 0;
-
-	parse_rtattr(attrs, MACSEC_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len);
-	if (!validate_dump(attrs)) {
-		fprintf(stderr, "incomplete dump message\n");
-		return -1;
-	}
-
-	ifindex = rta_getattr_u32(attrs[MACSEC_ATTR_IFINDEX]);
-	parse_rtattr_nested(attrs_secy, MACSEC_SECY_ATTR_MAX,
-			    attrs[MACSEC_ATTR_SECY]);
-
-	if (!validate_secy_dump(attrs_secy)) {
-		fprintf(stderr, "incomplete dump message\n");
-		return -1;
-	}
-
-	sci = rta_getattr_u64(attrs_secy[MACSEC_SECY_ATTR_SCI]);
-	encoding_sa = rta_getattr_u8(attrs_secy[MACSEC_SECY_ATTR_ENCODING_SA]);
-
-	if (filter.ifindex && ifindex != filter.ifindex)
-		return 0;
-
-	if (filter.sci && sci != filter.sci)
-		return 0;
-
-	open_json_object(NULL);
-	print_uint(PRINT_ANY, "ifindex", "%u: ", ifindex);
-	print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname",
-			   "%s: ", ll_index_to_name(ifindex));
-
-	print_attrs(attrs_secy);
-
-	print_tx_sc("    ", sci, encoding_sa,
-		    attrs[MACSEC_ATTR_TXSC_STATS],
-		    attrs[MACSEC_ATTR_SECY_STATS],
-		    attrs[MACSEC_ATTR_TXSA_LIST]);
-
-	if (attrs[MACSEC_ATTR_RXSC_LIST])
-		print_rxsc_list(attrs[MACSEC_ATTR_RXSC_LIST]);
-
-	close_json_object();
-
-	return 0;
-}
-
-static int do_dump(int ifindex)
-{
-	MACSEC_GENL_REQ(req, MACSEC_BUFLEN, MACSEC_CMD_GET_TXSC,
-			NLM_F_REQUEST | NLM_F_DUMP);
-
-	memset(&filter, 0, sizeof(filter));
-	filter.ifindex = ifindex;
-
-	req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq;
-	if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0) {
-		perror("Failed to send dump request");
-		exit(1);
-	}
-
-	new_json_obj(json);
-	if (rtnl_dump_filter(&genl_rth, process, stdout) < 0) {
-		delete_json_obj();
-		fprintf(stderr, "Dump terminated\n");
-		exit(1);
-	}
-	delete_json_obj();
-
-	return 0;
-}
-
-static int do_show(int argc, char **argv)
-{
-	int ifindex;
-
-	if (argc == 0)
-		return do_dump(0);
-
-	ifindex = ll_name_to_index(*argv);
-	if (ifindex == 0) {
-		fprintf(stderr, "Device \"%s\" does not exist.\n", *argv);
-		return -1;
-	}
-
-	argc--, argv++;
-	if (argc == 0)
-		return do_dump(ifindex);
-
-	ipmacsec_usage();
-	return -1;
-}
-
-int do_ipmacsec(int argc, char **argv)
-{
-	if (argc < 1)
-		ipmacsec_usage();
-
-	if (matches(*argv, "help") == 0)
-		ipmacsec_usage();
-
-	if (genl_init_handle(&genl_rth, MACSEC_GENL_NAME, &genl_family))
-		exit(1);
-
-	if (matches(*argv, "show") == 0)
-		return do_show(argc-1, argv+1);
-
-	if (matches(*argv, "add") == 0)
-		return do_modify(CMD_ADD, argc-1, argv+1);
-	if (matches(*argv, "set") == 0)
-		return do_modify(CMD_UPD, argc-1, argv+1);
-	if (matches(*argv, "delete") == 0)
-		return do_modify(CMD_DEL, argc-1, argv+1);
-
-	fprintf(stderr, "Command \"%s\" is unknown, try \"ip macsec help\".\n",
-		*argv);
-	exit(-1);
-}
-
-/* device creation */
-static void macsec_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
-{
-	if (!tb)
-		return;
-
-	if (tb[IFLA_MACSEC_SCI]) {
-		if (is_json_context()) {
-			SPRINT_BUF(b1);
-
-			snprintf(b1, sizeof(b1), "%016llx",
-				 ntohll(rta_getattr_u64(tb[IFLA_MACSEC_SCI])));
-			print_string(PRINT_JSON, "sci", NULL, b1);
-		} else {
-			fprintf(f, "sci %016llx ",
-				ntohll(rta_getattr_u64(tb[IFLA_MACSEC_SCI])));
-		}
-	}
-
-	print_flag(tb, "protect", IFLA_MACSEC_PROTECT);
-
-	if (tb[IFLA_MACSEC_CIPHER_SUITE]) {
-		__u64 csid
-			= rta_getattr_u64(tb[IFLA_MACSEC_CIPHER_SUITE]);
-
-		print_string(PRINT_ANY,
-			     "cipher_suite",
-			     "cipher %s ",
-			     cs_id_to_name(csid));
-	}
-
-	if (tb[IFLA_MACSEC_ICV_LEN]) {
-		if (is_json_context()) {
-			char b2[4];
-
-			snprintf(b2, sizeof(b2), "%hhu",
-				 rta_getattr_u8(tb[IFLA_MACSEC_ICV_LEN]));
-			print_uint(PRINT_JSON, "icv_len", NULL, atoi(b2));
-		} else {
-			fprintf(f, "icvlen %hhu ",
-				rta_getattr_u8(tb[IFLA_MACSEC_ICV_LEN]));
-		}
-	}
-
-	if (tb[IFLA_MACSEC_ENCODING_SA]) {
-		if (is_json_context()) {
-			char b2[4];
-
-			snprintf(b2, sizeof(b2), "%hhu",
-				 rta_getattr_u8(tb[IFLA_MACSEC_ENCODING_SA]));
-			print_uint(PRINT_JSON, "encoding_sa", NULL, atoi(b2));
-		} else {
-			fprintf(f, "encodingsa %hhu ",
-				rta_getattr_u8(tb[IFLA_MACSEC_ENCODING_SA]));
-		}
-	}
-
-	if (tb[IFLA_MACSEC_VALIDATION]) {
-		__u8 val = rta_getattr_u8(tb[IFLA_MACSEC_VALIDATION]);
-
-		print_string(PRINT_ANY,
-			     "validation",
-			     "validate %s ",
-			     validate_str[val]);
-	}
-
-	const char *inc_sci, *es, *replay;
-
-	if (is_json_context()) {
-		inc_sci = "inc_sci";
-		replay = "replay_protect";
-		es = "es";
-	} else {
-		inc_sci = "send_sci";
-		es = "end_station";
-		replay = "replay";
-	}
-
-	print_flag(tb, "encrypt", IFLA_MACSEC_ENCRYPT);
-	print_flag(tb, inc_sci, IFLA_MACSEC_INC_SCI);
-	print_flag(tb, es, IFLA_MACSEC_ES);
-	print_flag(tb, "scb", IFLA_MACSEC_SCB);
-	print_flag(tb, replay, IFLA_MACSEC_REPLAY_PROTECT);
-
-	if (tb[IFLA_MACSEC_WINDOW])
-		print_int(PRINT_ANY,
-			  "window",
-			  "window %d ",
-			  rta_getattr_u32(tb[IFLA_MACSEC_WINDOW]));
-}
-
-static bool check_txsc_flags(bool es, bool scb, bool sci)
-{
-	if (sci && (es || scb))
-		return false;
-	if (es && scb)
-		return false;
-	return true;
-}
-
-static void usage(FILE *f)
-{
-	fprintf(f,
-		"Usage: ... macsec [ [ address <lladdr> ] port { 1..2^16-1 } | sci <u64> ]\n"
-		"                  [ cipher { default | gcm-aes-128 | gcm-aes-256 } ]\n"
-		"                  [ icvlen { 8..16 } ]\n"
-		"                  [ encrypt { on | off } ]\n"
-		"                  [ send_sci { on | off } ]\n"
-		"                  [ end_station { on | off } ]\n"
-		"                  [ scb { on | off } ]\n"
-		"                  [ protect { on | off } ]\n"
-		"                  [ replay { on | off} window { 0..2^32-1 } ]\n"
-		"                  [ validate { strict | check | disabled } ]\n"
-		"                  [ encodingsa { 0..3 } ]\n"
-		);
-}
-
-static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
-			    struct nlmsghdr *n)
-{
-	int ret;
-	__u8 encoding_sa = 0xff;
-	__u32 window = -1;
-	struct cipher_args cipher = {0};
-	enum macsec_validation_type validate;
-	bool es = false, scb = false, send_sci = false;
-	int replay_protect = -1;
-	struct sci sci = { 0 };
-
-	ret = get_sci_portaddr(&sci, &argc, &argv, true, true);
-	if (ret < 0) {
-		fprintf(stderr, "expected sci\n");
-		return -1;
-	}
-
-	if (ret > 0) {
-		if (sci.sci)
-			addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_SCI,
-				  &sci.sci, sizeof(sci.sci));
-		else
-			addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_PORT,
-				  &sci.port, sizeof(sci.port));
-	}
-
-	while (argc > 0) {
-		if (strcmp(*argv, "cipher") == 0) {
-			NEXT_ARG();
-			if (cipher.id)
-				duparg("cipher", *argv);
-			if (strcmp(*argv, "default") == 0)
-				cipher.id = MACSEC_DEFAULT_CIPHER_ID;
-			else if (strcmp(*argv, "gcm-aes-128") == 0 ||
-			         strcmp(*argv, "GCM-AES-128") == 0)
-				cipher.id = MACSEC_CIPHER_ID_GCM_AES_128;
-			else if (strcmp(*argv, "gcm-aes-256") == 0 ||
-			         strcmp(*argv, "GCM-AES-256") == 0)
-				cipher.id = MACSEC_CIPHER_ID_GCM_AES_256;
-			else
-				invarg("expected: default, gcm-aes-128 or"
-				       " gcm-aes-256", *argv);
-		} else if (strcmp(*argv, "icvlen") == 0) {
-			NEXT_ARG();
-			if (cipher.icv_len)
-				duparg("icvlen", *argv);
-			get_icvlen(&cipher.icv_len, *argv);
-		} else if (strcmp(*argv, "encrypt") == 0) {
-			NEXT_ARG();
-			int i;
-
-			ret = one_of("encrypt", *argv, values_on_off,
-				     ARRAY_SIZE(values_on_off), &i);
-			if (ret != 0)
-				return ret;
-			addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_ENCRYPT, i);
-		} else if (strcmp(*argv, "send_sci") == 0) {
-			NEXT_ARG();
-			int i;
-
-			ret = one_of("send_sci", *argv, values_on_off,
-				     ARRAY_SIZE(values_on_off), &i);
-			if (ret != 0)
-				return ret;
-			send_sci = i;
-			addattr8(n, MACSEC_BUFLEN,
-				 IFLA_MACSEC_INC_SCI, send_sci);
-		} else if (strcmp(*argv, "end_station") == 0) {
-			NEXT_ARG();
-			int i;
-
-			ret = one_of("end_station", *argv, values_on_off,
-				     ARRAY_SIZE(values_on_off), &i);
-			if (ret != 0)
-				return ret;
-			es = i;
-			addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_ES, es);
-		} else if (strcmp(*argv, "scb") == 0) {
-			NEXT_ARG();
-			int i;
-
-			ret = one_of("scb", *argv, values_on_off,
-				     ARRAY_SIZE(values_on_off), &i);
-			if (ret != 0)
-				return ret;
-			scb = i;
-			addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_SCB, scb);
-		} else if (strcmp(*argv, "protect") == 0) {
-			NEXT_ARG();
-			int i;
-
-			ret = one_of("protect", *argv, values_on_off,
-				     ARRAY_SIZE(values_on_off), &i);
-			if (ret != 0)
-				return ret;
-			addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_PROTECT, i);
-		} else if (strcmp(*argv, "replay") == 0) {
-			NEXT_ARG();
-			int i;
-
-			ret = one_of("replay", *argv, values_on_off,
-				     ARRAY_SIZE(values_on_off), &i);
-			if (ret != 0)
-				return ret;
-			replay_protect = !!i;
-		} else if (strcmp(*argv, "window") == 0) {
-			NEXT_ARG();
-			ret = get_u32(&window, *argv, 0);
-			if (ret)
-				invarg("expected replay window size", *argv);
-		} else if (strcmp(*argv, "validate") == 0) {
-			NEXT_ARG();
-			ret = one_of("validate", *argv,
-				     validate_str, ARRAY_SIZE(validate_str),
-				     (int *)&validate);
-			if (ret != 0)
-				return ret;
-			addattr8(n, MACSEC_BUFLEN,
-				 IFLA_MACSEC_VALIDATION, validate);
-		} else if (strcmp(*argv, "encodingsa") == 0) {
-			if (encoding_sa != 0xff)
-				duparg2("encodingsa", "encodingsa");
-			NEXT_ARG();
-			ret = get_an(&encoding_sa, *argv);
-			if (ret)
-				invarg("expected an { 0..3 }", *argv);
-		} else {
-			fprintf(stderr, "macsec: unknown command \"%s\"?\n",
-				*argv);
-			usage(stderr);
-			return -1;
-		}
-
-		argv++; argc--;
-	}
-
-	if (!check_txsc_flags(es, scb, send_sci)) {
-		fprintf(stderr,
-			"invalid combination of send_sci/end_station/scb\n");
-		return -1;
-	}
-
-	if (window != -1 && replay_protect == -1) {
-		fprintf(stderr,
-			"replay window set, but replay protection not enabled. did you mean 'replay on window %u'?\n",
-			window);
-		return -1;
-	} else if (window == -1 && replay_protect == 1) {
-		fprintf(stderr,
-			"replay protection enabled, but no window set. did you mean 'replay on window VALUE'?\n");
-		return -1;
-	}
-
-	if (cipher.id)
-		addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_CIPHER_SUITE,
-			  &cipher.id, sizeof(cipher.id));
-	if (cipher.icv_len)
-		addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_ICV_LEN,
-			  &cipher.icv_len, sizeof(cipher.icv_len));
-
-	if (replay_protect != -1) {
-		addattr32(n, MACSEC_BUFLEN, IFLA_MACSEC_WINDOW, window);
-		addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_REPLAY_PROTECT,
-			 replay_protect);
-	}
-
-	if (encoding_sa != 0xff) {
-		addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_ENCODING_SA,
-			  &encoding_sa, sizeof(encoding_sa));
-	}
-
-	return 0;
-}
-
-static void macsec_print_help(struct link_util *lu, int argc, char **argv,
-			      FILE *f)
-{
-	usage(f);
-}
-
-struct link_util macsec_link_util = {
-	.id = "macsec",
-	.maxattr = IFLA_MACSEC_MAX,
-	.parse_opt = macsec_parse_opt,
-	.print_help = macsec_print_help,
-	.print_opt = macsec_print_opt,
-};
diff --git a/ip/ipmaddr.c b/ip/ipmaddr.c
index 3400e05..cbd6d11 100644
--- a/ip/ipmaddr.c
+++ b/ip/ipmaddr.c
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
@@ -28,7 +29,6 @@
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
-#include "json_print.h"
 
 static struct {
 	char *dev;
@@ -39,9 +39,8 @@
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: ip maddr [ add | del ] MULTIADDR dev STRING\n"
-		"       ip maddr show [ dev STRING ]\n");
+	fprintf(stderr, "Usage: ip maddr [ add | del ] MULTIADDR dev STRING\n");
+	fprintf(stderr, "       ip maddr show [ dev STRING ]\n");
 	exit(-1);
 }
 
@@ -51,7 +50,6 @@
 
 	while (*str && (len < 2 * size)) {
 		int tmp;
-
 		if (str[1] == 0)
 			return -1;
 		if (sscanf(str, "%02x", &tmp) != 1)
@@ -63,7 +61,8 @@
 	return len;
 }
 
-struct ma_info {
+struct ma_info
+{
 	struct ma_info *next;
 	int		index;
 	int		users;
@@ -94,16 +93,19 @@
 
 	while (fgets(buf, sizeof(buf), fp)) {
 		char hexa[256];
-		struct ma_info m = { .addr.family = AF_PACKET };
+		struct ma_info m;
 		int len;
 		int st;
 
+		memset(&m, 0, sizeof(m));
 		sscanf(buf, "%d%s%d%d%s", &m.index, m.name, &m.users, &st,
 		       hexa);
 		if (filter.dev && strcmp(filter.dev, m.name))
 			continue;
 
-		len = parse_hex(hexa, (unsigned char *)&m.addr.data, sizeof(m.addr.data));
+		m.addr.family = AF_PACKET;
+
+		len = parse_hex(hexa, (unsigned char*)&m.addr.data, sizeof (m.addr.data));
 		if (len >= 0) {
 			struct ma_info *ma = malloc(sizeof(m));
 
@@ -120,38 +122,34 @@
 
 static void read_igmp(struct ma_info **result_p)
 {
-	struct ma_info m = {
-		.addr.family = AF_INET,
-		.addr.bitlen = 32,
-		.addr.bytelen = 4,
-	};
+	struct ma_info m;
 	char buf[256];
 	FILE *fp = fopen("/proc/net/igmp", "r");
 
 	if (!fp)
 		return;
+	memset(&m, 0, sizeof(m));
 	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;
 
 		if (buf[0] != '\t') {
-			size_t len;
-
 			sscanf(buf, "%d%s", &m.index, m.name);
-			len = strlen(m.name);
-			if (m.name[len - 1] == ':')
-				m.name[len - 1] = '\0';
 			continue;
 		}
 
 		if (filter.dev && strcmp(filter.dev, m.name))
 			continue;
 
-		sscanf(buf, "%08x%d", (__u32 *)&m.addr.data, &m.users);
+		sscanf(buf, "%08x%d", (__u32*)&m.addr.data, &m.users);
 
 		ma = malloc(sizeof(m));
 		memcpy(ma, &m, sizeof(m));
@@ -171,15 +169,18 @@
 
 	while (fgets(buf, sizeof(buf), fp)) {
 		char hexa[256];
-		struct ma_info m = { .addr.family = AF_INET6 };
+		struct ma_info m;
 		int len;
 
+		memset(&m, 0, sizeof(m));
 		sscanf(buf, "%d%s%s%d", &m.index, m.name, hexa, &m.users);
 
 		if (filter.dev && strcmp(filter.dev, m.name))
 			continue;
 
-		len = parse_hex(hexa, (unsigned char *)&m.addr.data, sizeof(m.addr.data));
+		m.addr.family = AF_INET6;
+
+		len = parse_hex(hexa, (unsigned char*)&m.addr.data, sizeof (m.addr.data));
 		if (len >= 0) {
 			struct ma_info *ma = malloc(sizeof(m));
 
@@ -195,66 +196,53 @@
 
 static void print_maddr(FILE *fp, struct ma_info *list)
 {
-	print_string(PRINT_FP, NULL, "\t", NULL);
+	fprintf(fp, "\t");
 
-	open_json_object(NULL);
 	if (list->addr.family == AF_PACKET) {
 		SPRINT_BUF(b1);
-
-		print_string(PRINT_FP, NULL, "link  ", NULL);
-		print_color_string(PRINT_ANY, COLOR_MAC, "link", "%s",
-				   ll_addr_n2a((void *)list->addr.data, list->addr.bytelen,
-					       0, b1, sizeof(b1)));
+		fprintf(fp, "link  %s", ll_addr_n2a((unsigned char*)list->addr.data,
+						    list->addr.bytelen, 0,
+						    b1, sizeof(b1)));
 	} else {
-		print_string(PRINT_ANY, "family", "%-5s ",
-			     family_name(list->addr.family));
-		print_color_string(PRINT_ANY, ifa_family_color(list->addr.family),
-				   "address", "%s",
-				   format_host(list->addr.family,
-					       -1, list->addr.data));
+		char abuf[256];
+		switch(list->addr.family) {
+		case AF_INET:
+			fprintf(fp, "inet  ");
+			break;
+		case AF_INET6:
+			fprintf(fp, "inet6 ");
+			break;
+		default:
+			fprintf(fp, "family %d ", list->addr.family);
+			break;
+		}
+		fprintf(fp, "%s",
+			format_host(list->addr.family,
+				    -1,
+				    list->addr.data,
+				    abuf, sizeof(abuf)));
 	}
-
 	if (list->users != 1)
-		print_uint(PRINT_ANY, "users", " users %u", list->users);
-
+		fprintf(fp, " users %d", list->users);
 	if (list->features)
-		print_string(PRINT_ANY, "features", " %s", list->features);
-
-	print_string(PRINT_FP, NULL, "\n", NULL);
-	close_json_object();
+		fprintf(fp, " %s", list->features);
+	fprintf(fp, "\n");
 }
 
 static void print_mlist(FILE *fp, struct ma_info *list)
 {
 	int cur_index = 0;
 
-	new_json_obj(json);
 	for (; list; list = list->next) {
-
-		if (list->index != cur_index || oneline) {
-			if (cur_index) {
-				close_json_array(PRINT_JSON, NULL);
-				close_json_object();
-			}
-			open_json_object(NULL);
-
-			print_uint(PRINT_ANY, "ifindex", "%d:", list->index);
-			print_color_string(PRINT_ANY, COLOR_IFNAME,
-					   "ifname", "\t%s", list->name);
-			print_nl();
+		if (oneline) {
 			cur_index = list->index;
-
-			open_json_array(PRINT_JSON, "maddr");
+			fprintf(fp, "%d:\t%s%s", cur_index, list->name, _SL_);
+		} else if (cur_index != list->index) {
+			cur_index = list->index;
+			fprintf(fp, "%d:\t%s\n", cur_index, list->name);
 		}
-
 		print_maddr(fp, list);
 	}
-	if (cur_index) {
-		close_json_array(PRINT_JSON, NULL);
-		close_json_object();
-	}
-
-	delete_json_obj();
 }
 
 static int multiaddr_list(int argc, char **argv)
@@ -268,7 +256,8 @@
 		if (1) {
 			if (strcmp(*argv, "dev") == 0) {
 				NEXT_ARG();
-			} else if (matches(*argv, "help") == 0)
+			}
+			else if (matches(*argv, "help") == 0)
 				usage();
 			if (filter.dev)
 				duparg2("dev", *argv);
@@ -289,10 +278,11 @@
 
 static int multiaddr_modify(int cmd, int argc, char **argv)
 {
-	struct ifreq ifr = {};
-	int family;
+	struct ifreq ifr;
 	int fd;
 
+	memset(&ifr, 0, sizeof(ifr));
+
 	if (cmd == RTM_NEWADDR)
 		cmd = SIOCADDMULTI;
 	else
@@ -303,8 +293,7 @@
 			NEXT_ARG();
 			if (ifr.ifr_name[0])
 				duparg("dev", *argv);
-			if (get_ifname(ifr.ifr_name, *argv))
-				invarg("\"dev\" not a valid ifname", *argv);
+			strncpy(ifr.ifr_name, *argv, IFNAMSIZ);
 		} else {
 			if (matches(*argv, "address") == 0) {
 				NEXT_ARG();
@@ -326,22 +315,12 @@
 		exit(-1);
 	}
 
-	switch (preferred_family) {
-	case AF_INET6:
-	case AF_PACKET:
-	case AF_INET:
-		family = preferred_family;
-		break;
-	default:
-		family = AF_INET;
-	}
-
-	fd = socket(family, SOCK_DGRAM, 0);
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
 	if (fd < 0) {
 		perror("Cannot create socket");
 		exit(1);
 	}
-	if (ioctl(fd, cmd, (char *)&ifr) != 0) {
+	if (ioctl(fd, cmd, (char*)&ifr) != 0) {
 		perror("ioctl");
 		exit(1);
 	}
diff --git a/ip/ipmonitor.c b/ip/ipmonitor.c
index 685be52..99a237f 100644
--- a/ip/ipmonitor.c
+++ b/ip/ipmonitor.c
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -24,16 +25,16 @@
 #include "ip_common.h"
 
 static void usage(void) __attribute__((noreturn));
-static int prefix_banner;
+int prefix_banner;
 int listen_all_nsid;
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: ip monitor [ all | LISTofOBJECTS ] [ FILE ] [ label ] [all-nsid] [dev DEVICE]\n"
-		"LISTofOBJECTS := link | address | route | mroute | prefix |\n"
-		"		 neigh | netconf | rule | nsid\n"
-		"FILE := file FILENAME\n");
+	fprintf(stderr, "Usage: ip monitor [ all | LISTofOBJECTS ] [ FILE ] "
+			"[ label ] [all-nsid] [dev DEVICE]\n");
+	fprintf(stderr, "LISTofOBJECTS := link | address | route | mroute | prefix |\n");
+	fprintf(stderr, "                 neigh | netconf | rule | nsid\n");
+	fprintf(stderr, "FILE := file FILENAME\n");
 	exit(-1);
 }
 
@@ -53,14 +54,13 @@
 		fprintf(fp, "%s", label);
 }
 
-static int accept_msg(struct rtnl_ctrl_data *ctrl,
+static int accept_msg(const struct sockaddr_nl *who,
+		      struct rtnl_ctrl_data *ctrl,
 		      struct nlmsghdr *n, void *arg)
 {
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 
-	switch (n->nlmsg_type) {
-	case RTM_NEWROUTE:
-	case RTM_DELROUTE: {
+	if (n->nlmsg_type == RTM_NEWROUTE || n->nlmsg_type == RTM_DELROUTE) {
 		struct rtmsg *r = NLMSG_DATA(n);
 		int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
 
@@ -75,43 +75,33 @@
 		if (r->rtm_family == RTNL_FAMILY_IPMR ||
 		    r->rtm_family == RTNL_FAMILY_IP6MR) {
 			print_headers(fp, "[MROUTE]", ctrl);
-			print_mroute(n, arg);
+			print_mroute(who, n, arg);
 			return 0;
 		} else {
 			print_headers(fp, "[ROUTE]", ctrl);
-			print_route(n, arg);
+			print_route(who, n, arg);
 			return 0;
 		}
 	}
 
-	case RTM_NEWNEXTHOP:
-	case RTM_DELNEXTHOP:
-		print_headers(fp, "[NEXTHOP]", ctrl);
-		print_nexthop(n, arg);
-		return 0;
-
-	case RTM_NEWLINK:
-	case RTM_DELLINK:
-		ll_remember_index(n, NULL);
+	if (n->nlmsg_type == RTM_NEWLINK || n->nlmsg_type == RTM_DELLINK) {
+		ll_remember_index(who, n, NULL);
 		print_headers(fp, "[LINK]", ctrl);
-		print_linkinfo(n, arg);
+		print_linkinfo(who, n, arg);
 		return 0;
-
-	case RTM_NEWADDR:
-	case RTM_DELADDR:
+	}
+	if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
 		print_headers(fp, "[ADDR]", ctrl);
-		print_addrinfo(n, arg);
+		print_addrinfo(who, n, arg);
 		return 0;
-
-	case RTM_NEWADDRLABEL:
-	case RTM_DELADDRLABEL:
+	}
+	if (n->nlmsg_type == RTM_NEWADDRLABEL || n->nlmsg_type == RTM_DELADDRLABEL) {
 		print_headers(fp, "[ADDRLABEL]", ctrl);
-		print_addrlabel(n, arg);
+		print_addrlabel(who, n, arg);
 		return 0;
-
-	case RTM_NEWNEIGH:
-	case RTM_DELNEIGH:
-	case RTM_GETNEIGH:
+	}
+	if (n->nlmsg_type == RTM_NEWNEIGH || n->nlmsg_type == RTM_DELNEIGH ||
+	    n->nlmsg_type == RTM_GETNEIGH) {
 		if (preferred_family) {
 			struct ndmsg *r = NLMSG_DATA(n);
 
@@ -120,45 +110,37 @@
 		}
 
 		print_headers(fp, "[NEIGH]", ctrl);
-		print_neigh(n, arg);
+		print_neigh(who, n, arg);
 		return 0;
-
-	case RTM_NEWPREFIX:
+	}
+	if (n->nlmsg_type == RTM_NEWPREFIX) {
 		print_headers(fp, "[PREFIX]", ctrl);
-		print_prefix(n, arg);
+		print_prefix(who, n, arg);
 		return 0;
-
-	case RTM_NEWRULE:
-	case RTM_DELRULE:
+	}
+	if (n->nlmsg_type == RTM_NEWRULE || n->nlmsg_type == RTM_DELRULE) {
 		print_headers(fp, "[RULE]", ctrl);
-		print_rule(n, arg);
+		print_rule(who, n, arg);
 		return 0;
-
-	case NLMSG_TSTAMP:
+	}
+	if (n->nlmsg_type == RTM_NEWNETCONF) {
+		print_headers(fp, "[NETCONF]", ctrl);
+		print_netconf(who, ctrl, n, arg);
+		return 0;
+	}
+	if (n->nlmsg_type == NLMSG_TSTAMP) {
 		print_nlmsg_timestamp(fp, n);
 		return 0;
-
-	case RTM_NEWNETCONF:
-	case RTM_DELNETCONF:
-		print_headers(fp, "[NETCONF]", ctrl);
-		print_netconf(ctrl, n, arg);
-		return 0;
-
-	case RTM_DELNSID:
-	case RTM_NEWNSID:
+	}
+	if (n->nlmsg_type == RTM_NEWNSID || n->nlmsg_type == RTM_DELNSID) {
 		print_headers(fp, "[NSID]", ctrl);
-		print_nsid(n, arg);
+		print_nsid(who, n, arg);
 		return 0;
-
-	case NLMSG_ERROR:
-	case NLMSG_NOOP:
-	case NLMSG_DONE:
-		break;	/* ignore */
-
-	default:
-		fprintf(stderr,
-			"Unknown message: type=0x%08x(%d) flags=0x%08x(%d) len=0x%08x(%d)\n",
-			n->nlmsg_type, n->nlmsg_type,
+	}
+	if (n->nlmsg_type != NLMSG_ERROR && n->nlmsg_type != NLMSG_NOOP &&
+	    n->nlmsg_type != NLMSG_DONE) {
+		fprintf(fp, "Unknown message: type=0x%08x(%d) flags=0x%08x(%d)"
+			"len=0x%08x(%d)\n", n->nlmsg_type, n->nlmsg_type,
 			n->nlmsg_flags, n->nlmsg_flags, n->nlmsg_len,
 			n->nlmsg_len);
 	}
@@ -167,19 +149,18 @@
 
 int do_ipmonitor(int argc, char **argv)
 {
-	int lnexthop = 0, nh_set = 1;
 	char *file = NULL;
-	unsigned int groups = 0;
-	int llink = 0;
-	int laddr = 0;
-	int lroute = 0;
-	int lmroute = 0;
-	int lprefix = 0;
-	int lneigh = 0;
-	int lnetconf = 0;
-	int lrule = 0;
-	int lnsid = 0;
-	int ifindex = 0;
+	unsigned groups = 0;
+	int llink=0;
+	int laddr=0;
+	int lroute=0;
+	int lmroute=0;
+	int lprefix=0;
+	int lneigh=0;
+	int lnetconf=0;
+	int lrule=0;
+	int lnsid=0;
+	int ifindex=0;
 
 	groups |= nl_mgrp(RTNLGRP_LINK);
 	groups |= nl_mgrp(RTNLGRP_IPV4_IFADDR);
@@ -196,7 +177,6 @@
 	groups |= nl_mgrp(RTNLGRP_IPV4_RULE);
 	groups |= nl_mgrp(RTNLGRP_IPV6_RULE);
 	groups |= nl_mgrp(RTNLGRP_NSID);
-	groups |= nl_mgrp(RTNLGRP_MPLS_NETCONF);
 
 	rtnl_close(&rth);
 
@@ -206,49 +186,37 @@
 			file = *argv;
 		} else if (matches(*argv, "label") == 0) {
 			prefix_banner = 1;
+		} else if (matches(*argv, "all-nsid") == 0) {
+			listen_all_nsid = 1;
 		} else if (matches(*argv, "link") == 0) {
-			llink = 1;
+			llink=1;
 			groups = 0;
-			nh_set = 0;
 		} else if (matches(*argv, "address") == 0) {
-			laddr = 1;
+			laddr=1;
 			groups = 0;
-			nh_set = 0;
 		} else if (matches(*argv, "route") == 0) {
-			lroute = 1;
+			lroute=1;
 			groups = 0;
-			nh_set = 0;
 		} else if (matches(*argv, "mroute") == 0) {
-			lmroute = 1;
+			lmroute=1;
 			groups = 0;
-			nh_set = 0;
 		} else if (matches(*argv, "prefix") == 0) {
-			lprefix = 1;
+			lprefix=1;
 			groups = 0;
-			nh_set = 0;
 		} else if (matches(*argv, "neigh") == 0) {
 			lneigh = 1;
 			groups = 0;
-			nh_set = 0;
 		} else if (matches(*argv, "netconf") == 0) {
 			lnetconf = 1;
 			groups = 0;
-			nh_set = 0;
 		} else if (matches(*argv, "rule") == 0) {
 			lrule = 1;
 			groups = 0;
-			nh_set = 0;
 		} else if (matches(*argv, "nsid") == 0) {
 			lnsid = 1;
 			groups = 0;
-			nh_set = 0;
-		} else if (matches(*argv, "nexthop") == 0) {
-			lnexthop = 1;
-			groups = 0;
 		} else if (strcmp(*argv, "all") == 0) {
-			prefix_banner = 1;
-		} else if (matches(*argv, "all-nsid") == 0) {
-			listen_all_nsid = 1;
+			prefix_banner=1;
 		} else if (matches(*argv, "help") == 0) {
 			usage();
 		} else if (strcmp(*argv, "dev") == 0) {
@@ -304,8 +272,6 @@
 			groups |= nl_mgrp(RTNLGRP_IPV4_NETCONF);
 		if (!preferred_family || preferred_family == AF_INET6)
 			groups |= nl_mgrp(RTNLGRP_IPV6_NETCONF);
-		if (!preferred_family || preferred_family == AF_MPLS)
-			groups |= nl_mgrp(RTNLGRP_MPLS_NETCONF);
 	}
 	if (lrule) {
 		if (!preferred_family || preferred_family == AF_INET)
@@ -316,9 +282,6 @@
 	if (lnsid) {
 		groups |= nl_mgrp(RTNLGRP_NSID);
 	}
-	if (nh_set)
-		lnexthop = 1;
-
 	if (file) {
 		FILE *fp;
 		int err;
@@ -335,17 +298,10 @@
 
 	if (rtnl_open(&rth, groups) < 0)
 		exit(1);
-
-	if (lnexthop && rtnl_add_nl_group(&rth, RTNLGRP_NEXTHOP) < 0) {
-		fprintf(stderr, "Failed to add nexthop group to list\n");
-		exit(1);
-	}
-
 	if (listen_all_nsid && rtnl_listen_all_nsid(&rth) < 0)
 		exit(1);
 
 	ll_init_map(&rth);
-	netns_nsid_socket_init();
 	netns_map_init();
 
 	if (rtnl_listen(&rth, accept_msg, stdout) < 0)
diff --git a/ip/ipmroute.c b/ip/ipmroute.c
index 656ea0d..fffa9e2 100644
--- a/ip/ipmroute.c
+++ b/ip/ipmroute.c
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <sys/ioctl.h>
@@ -29,24 +30,22 @@
 #include <rt_names.h>
 #include "utils.h"
 #include "ip_common.h"
-#include "json_print.h"
 
 static void usage(void) __attribute__((noreturn));
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: ip mroute show [ [ to ] PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n"
-	"			[ table TABLE_ID ]\n"
-	"TABLE_ID := [ local | main | default | all | NUMBER ]\n"
+	fprintf(stderr, "Usage: ip mroute show [ [ to ] PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n");
+	fprintf(stderr, "                      [ table TABLE_ID ]\n");
+	fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n");
 #if 0
-	"Usage: ip mroute [ add | del ] DESTINATION from SOURCE [ iif DEVICE ] [ oif DEVICE ]\n"
+	fprintf(stderr, "Usage: ip mroute [ add | del ] DESTINATION from SOURCE [ iif DEVICE ] [ oif DEVICE ]\n");
 #endif
-	);
 	exit(-1);
 }
 
-static struct rtfilter {
+struct rtfilter
+{
 	int tb;
 	int af;
 	int iif;
@@ -54,15 +53,15 @@
 	inet_prefix msrc;
 } filter;
 
-int print_mroute(struct nlmsghdr *n, void *arg)
+int print_mroute(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];
-	FILE *fp = arg;
-	const char *src, *dst;
+	struct rtattr * tb[RTA_MAX+1];
+	char abuf[256];
+	char obuf[256];
 	SPRINT_BUF(b1);
-	SPRINT_BUF(b2);
 	__u32 table;
 	int iif = 0;
 	int family;
@@ -78,11 +77,10 @@
 		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 		return -1;
 	}
-
 	if (r->rtm_type != RTN_MULTICAST) {
-		fprintf(stderr,
-			"Non multicast route received, kernel does support IP multicast?\n");
-		return -1;
+		fprintf(stderr, "Not a multicast route (type: %s)\n",
+			rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+		return 0;
 	}
 
 	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
@@ -92,59 +90,65 @@
 		return 0;
 
 	if (tb[RTA_IIF])
-		iif = rta_getattr_u32(tb[RTA_IIF]);
+		iif = *(int*)RTA_DATA(tb[RTA_IIF]);
 	if (filter.iif && filter.iif != iif)
 		return 0;
 
 	if (filter.af && filter.af != r->rtm_family)
 		return 0;
 
-	if (inet_addr_match_rta(&filter.mdst, tb[RTA_DST]))
-		return 0;
+	if (tb[RTA_DST] && filter.mdst.bitlen > 0) {
+		inet_prefix dst;
 
-	if (inet_addr_match_rta(&filter.msrc, tb[RTA_SRC]))
-		return 0;
-
-	family = get_real_family(r->rtm_type, r->rtm_family);
-
-	open_json_object(NULL);
-	if (n->nlmsg_type == RTM_DELROUTE)
-		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
-
-	if (tb[RTA_SRC])
-		src = rt_addr_n2a_r(family, RTA_PAYLOAD(tb[RTA_SRC]),
-				    RTA_DATA(tb[RTA_SRC]), b1, sizeof(b1));
-	else
-		src = "unknown";
-
-	if (tb[RTA_DST])
-		dst = rt_addr_n2a_r(family, RTA_PAYLOAD(tb[RTA_DST]),
-				    RTA_DATA(tb[RTA_DST]), b2, sizeof(b2));
-	else
-		dst = "unknown";
-
-	if (is_json_context()) {
-		print_string(PRINT_JSON, "src", NULL, src);
-		print_string(PRINT_JSON, "dst", NULL, dst);
-	} else {
-		char obuf[256];
-
-		snprintf(obuf, sizeof(obuf), "(%s,%s)", src, dst);
-		print_string(PRINT_FP, NULL,
-			     "%-32s Iif: ", obuf);
+		memset(&dst, 0, sizeof(dst));
+		dst.family = r->rtm_family;
+		memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), RTA_PAYLOAD(tb[RTA_DST]));
+		if (inet_addr_match(&dst, &filter.mdst, filter.mdst.bitlen))
+			return 0;
 	}
 
-	if (iif)
-		print_color_string(PRINT_ANY, COLOR_IFNAME,
-				   "iif", "%-10s ", ll_index_to_name(iif));
+	if (tb[RTA_SRC] && filter.msrc.bitlen > 0) {
+		inet_prefix src;
+
+		memset(&src, 0, sizeof(src));
+		src.family = r->rtm_family;
+		memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), RTA_PAYLOAD(tb[RTA_SRC]));
+		if (inet_addr_match(&src, &filter.msrc, filter.msrc.bitlen))
+			return 0;
+	}
+
+	family = r->rtm_family == RTNL_FAMILY_IPMR ? AF_INET : AF_INET6;
+
+	if (n->nlmsg_type == RTM_DELROUTE)
+		fprintf(fp, "Deleted ");
+
+	if (tb[RTA_SRC])
+		len = snprintf(obuf, sizeof(obuf),
+			       "(%s, ", rt_addr_n2a(family,
+						    RTA_PAYLOAD(tb[RTA_SRC]),
+						    RTA_DATA(tb[RTA_SRC]),
+						    abuf, sizeof(abuf)));
 	else
-		print_string(PRINT_ANY,"iif", "%s ", "unresolved");
+		len = sprintf(obuf, "(unknown, ");
+	if (tb[RTA_DST])
+		snprintf(obuf + len, sizeof(obuf) - len,
+			 "%s)", rt_addr_n2a(family,
+					    RTA_PAYLOAD(tb[RTA_DST]),
+					    RTA_DATA(tb[RTA_DST]),
+					    abuf, sizeof(abuf)));
+	else
+		snprintf(obuf + len, sizeof(obuf) - len, "unknown) ");
+
+	fprintf(fp, "%-32s Iif: ", obuf);
+	if (iif)
+		fprintf(fp, "%-10s ", ll_index_to_name(iif));
+	else
+		fprintf(fp, "unresolved ");
 
 	if (tb[RTA_MULTIPATH]) {
 		struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]);
 		int first = 1;
 
-		open_json_array(PRINT_JSON, "multipath");
 		len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
 
 		for (;;) {
@@ -153,65 +157,30 @@
 			if (nh->rtnh_len > len)
 				break;
 
-			open_json_object(NULL);
 			if (first) {
-				print_string(PRINT_FP, NULL, "Oifs: ", NULL);
+				fprintf(fp, "Oifs: ");
 				first = 0;
 			}
-
-			print_color_string(PRINT_ANY, COLOR_IFNAME,
-					   "oif", "%s", ll_index_to_name(nh->rtnh_ifindex));
-
+			fprintf(fp, "%s", ll_index_to_name(nh->rtnh_ifindex));
 			if (nh->rtnh_hops > 1)
-				print_uint(PRINT_ANY,
-					   "ttl", "(ttl %u) ", nh->rtnh_hops);
+				fprintf(fp, "(ttl %d) ", nh->rtnh_hops);
 			else
-				print_string(PRINT_FP, NULL, " ", NULL);
-
-			close_json_object();
+				fprintf(fp, " ");
 			len -= NLMSG_ALIGN(nh->rtnh_len);
 			nh = RTNH_NEXT(nh);
 		}
-		close_json_array(PRINT_JSON, NULL);
 	}
-
-	print_string(PRINT_ANY, "state", " State: %s",
-		     (r->rtm_flags & RTNH_F_UNRESOLVED) ? "unresolved" : "resolved");
-
-	if (r->rtm_flags & RTNH_F_OFFLOAD)
-		print_null(PRINT_ANY, "offload", " offload", NULL);
-
 	if (show_stats && tb[RTA_MFC_STATS]) {
 		struct rta_mfc_stats *mfcs = RTA_DATA(tb[RTA_MFC_STATS]);
 
-		print_nl();
-		print_u64(PRINT_ANY, "packets", "  %"PRIu64" packets,",
-			   mfcs->mfcs_packets);
-		print_u64(PRINT_ANY, "bytes", " %"PRIu64" bytes", mfcs->mfcs_bytes);
-
+		fprintf(fp, "%s  %"PRIu64" packets, %"PRIu64" bytes", _SL_,
+			(uint64_t)mfcs->mfcs_packets,
+			(uint64_t)mfcs->mfcs_bytes);
 		if (mfcs->mfcs_wrong_if)
-			print_u64(PRINT_ANY, "wrong_if",
-				   ", %"PRIu64" arrived on wrong iif.",
-				   mfcs->mfcs_wrong_if);
+			fprintf(fp, ", %"PRIu64" arrived on wrong iif.",
+				(uint64_t)mfcs->mfcs_wrong_if);
 	}
-
-	if (show_stats && tb[RTA_EXPIRES]) {
-		struct timeval tv;
-		double age;
-
-		__jiffies_to_tv(&tv, rta_getattr_u64(tb[RTA_EXPIRES]));
-		age = tv.tv_sec;
-		age += tv.tv_usec / 1000000.;
-		print_float(PRINT_ANY, "expires",
-			    ", Age %.2f", age);
-	}
-
-	if (table && (table != RT_TABLE_MAIN || show_details > 0) && !filter.tb)
-		print_string(PRINT_ANY, "table", " Table: %s",
-			     rtnl_rttable_n2a(table, b1, sizeof(b1)));
-
-	print_string(PRINT_FP, NULL, "\n", NULL);
-	close_json_object();
+	fprintf(fp, "\n");
 	fflush(fp);
 	return 0;
 }
@@ -224,43 +193,25 @@
 	filter.iif = ifindex;
 }
 
-static int iproute_dump_filter(struct nlmsghdr *nlh, int reqlen)
-{
-	int err;
-
-	if (filter.tb) {
-		err = addattr32(nlh, reqlen, RTA_TABLE, filter.tb);
-		if (err)
-			return err;
-	}
-
-	return 0;
-}
-
 static int mroute_list(int argc, char **argv)
 {
 	char *id = NULL;
-	int family = preferred_family;
+	int family;
 
 	ipmroute_reset_filter(0);
-	if (family == AF_INET || family == AF_UNSPEC) {
-		family = RTNL_FAMILY_IPMR;
+	if (preferred_family == AF_UNSPEC)
+		family = AF_INET;
+	else
+		family = AF_INET6;
+	if (family == AF_INET) {
 		filter.af = RTNL_FAMILY_IPMR;
 		filter.tb = RT_TABLE_DEFAULT;  /* for backward compatibility */
-	} else if (family == AF_INET6) {
-		family = RTNL_FAMILY_IP6MR;
+	} else
 		filter.af = RTNL_FAMILY_IP6MR;
-	} else {
-		/* family does not have multicast routing */
-		return 0;
-	}
-
-	filter.msrc.family = filter.mdst.family = family;
 
 	while (argc > 0) {
 		if (matches(*argv, "table") == 0) {
 			__u32 tid;
-
 			NEXT_ARG();
 			if (rtnl_rttable_a2n(&tid, *argv)) {
 				if (strcmp(*argv, "all") == 0) {
@@ -277,16 +228,14 @@
 			id = *argv;
 		} else if (matches(*argv, "from") == 0) {
 			NEXT_ARG();
-			if (get_prefix(&filter.msrc, *argv, family))
-				invarg("from value is invalid\n", *argv);
+			get_prefix(&filter.msrc, *argv, family);
 		} else {
 			if (strcmp(*argv, "to") == 0) {
 				NEXT_ARG();
 			}
 			if (matches(*argv, "help") == 0)
 				usage();
-			if (get_prefix(&filter.mdst, *argv, family))
-				invarg("to value is invalid\n", *argv);
+			get_prefix(&filter.mdst, *argv, family);
 		}
 		argc--; argv++;
 	}
@@ -296,26 +245,24 @@
 	if (id)  {
 		int idx;
 
-		idx = ll_name_to_index(id);
-		if (!idx)
-			return nodev(id);
+		if ((idx = ll_name_to_index(id)) == 0) {
+			fprintf(stderr, "Cannot find device \"%s\"\n", id);
+			return -1;
+		}
 		filter.iif = idx;
 	}
 
-	if (rtnl_routedump_req(&rth, filter.af, iproute_dump_filter) < 0) {
+	if (rtnl_wilddump_request(&rth, filter.af, RTM_GETROUTE) < 0) {
 		perror("Cannot send dump request");
 		return 1;
 	}
 
-	new_json_obj(json);
 	if (rtnl_dump_filter(&rth, print_mroute, stdout) < 0) {
-		delete_json_obj();
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
-	delete_json_obj();
 
-	return 0;
+	exit(0);
 }
 
 int do_multiroute(int argc, char **argv)
diff --git a/ip/ipneigh.c b/ip/ipneigh.c
index a3869c8..92b7cd6 100644
--- a/ip/ipneigh.c
+++ b/ip/ipneigh.c
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <string.h>
 #include <sys/time.h>
@@ -23,7 +24,6 @@
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
-#include "json_print.h"
 
 #define NUD_VALID	(NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY)
 #define MAX_ROUNDS	10
@@ -31,7 +31,7 @@
 static struct
 {
 	int family;
-	int index;
+        int index;
 	int state;
 	int unused_only;
 	inet_prefix pfx;
@@ -40,28 +40,20 @@
 	int flushp;
 	int flushe;
 	int master;
-	int protocol;
-	__u8 ndm_flags;
 } filter;
 
 static void usage(void) __attribute__((noreturn));
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: ip neigh { add | del | change | replace }\n"
-		"		{ ADDR [ lladdr LLADDR ] [ nud STATE ] | proxy ADDR } [ dev DEV ]\n"
-		"		[ router ] [ extern_learn ] [ protocol PROTO ]\n"
-		"\n"
-		"	ip neigh { show | flush } [ proxy ] [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n"
-		"				  [ vrf NAME ]\n"
-		"\n"
-		"STATE := { permanent | noarp | stale | reachable | none |\n"
-		"           incomplete | delay | probe | failed }\n");
+	fprintf(stderr, "Usage: ip neigh { add | del | change | replace } { ADDR [ lladdr LLADDR ]\n"
+		        "          [ nud { permanent | noarp | stale | reachable } ]\n"
+		        "          | proxy ADDR } [ dev DEV ]\n");
+	fprintf(stderr, "       ip neigh {show|flush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n");
 	exit(-1);
 }
 
-static int nud_state_a2n(unsigned int *state, const char *arg)
+static int nud_state_a2n(unsigned *state, const char *arg)
 {
 	if (matches(arg, "permanent") == 0)
 		*state = NUD_PERMANENT;
@@ -84,7 +76,7 @@
 	else {
 		if (get_unsigned(state, arg, 0))
 			return -1;
-		if (*state >= 0x100 || (*state&((*state)-1)))
+		if (*state>=0x100 || (*state&((*state)-1)))
 			return -1;
 	}
 	return 0;
@@ -106,21 +98,23 @@
 	struct {
 		struct nlmsghdr	n;
 		struct ndmsg		ndm;
-		char			buf[256];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST | flags,
-		.n.nlmsg_type = cmd,
-		.ndm.ndm_family = preferred_family,
-		.ndm.ndm_state = NUD_PERMANENT,
-	};
+		char  			buf[256];
+	} req;
 	char  *dev = NULL;
 	int dst_ok = 0;
 	int dev_ok = 0;
 	int lladdr_ok = 0;
-	char *lla = NULL;
+	char * lla = NULL;
 	inet_prefix dst;
 
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+	req.n.nlmsg_type = cmd;
+	req.ndm.ndm_family = preferred_family;
+	req.ndm.ndm_state = NUD_PERMANENT;
+
 	while (argc > 0) {
 		if (matches(*argv, "lladdr") == 0) {
 			NEXT_ARG();
@@ -129,8 +123,7 @@
 			lla = *argv;
 			lladdr_ok = 1;
 		} else if (strcmp(*argv, "nud") == 0) {
-			unsigned int state;
-
+			unsigned state;
 			NEXT_ARG();
 			if (nud_state_a2n(&state, *argv))
 				invarg("nud state is bad", *argv);
@@ -145,22 +138,10 @@
 			dst_ok = 1;
 			dev_ok = 1;
 			req.ndm.ndm_flags |= NTF_PROXY;
-		} else if (strcmp(*argv, "router") == 0) {
-			req.ndm.ndm_flags |= NTF_ROUTER;
-		} else if (matches(*argv, "extern_learn") == 0) {
-			req.ndm.ndm_flags |= NTF_EXT_LEARNED;
 		} else if (strcmp(*argv, "dev") == 0) {
 			NEXT_ARG();
 			dev = *argv;
 			dev_ok = 1;
-		} else if (matches(*argv, "protocol") == 0) {
-			__u32 proto;
-
-			NEXT_ARG();
-			if (rtnl_rtprot_a2n(&proto, *argv))
-				invarg("\"protocol\" value is invalid\n", *argv);
-			if (addattr8(&req.n, sizeof(req), NDA_PROTOCOL, proto))
-				return -1;
 		} else {
 			if (strcmp(*argv, "to") == 0) {
 				NEXT_ARG();
@@ -197,67 +178,26 @@
 
 	ll_init_map(&rth);
 
-	if (dev) {
-		req.ndm.ndm_ifindex = ll_name_to_index(dev);
-		if (!req.ndm.ndm_ifindex)
-			return nodev(dev);
+	if (dev && (req.ndm.ndm_ifindex = ll_name_to_index(dev)) == 0) {
+		fprintf(stderr, "Cannot find device \"%s\"\n", dev);
+		return -1;
 	}
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		exit(2);
 
 	return 0;
 }
 
-static void print_cacheinfo(const struct nda_cacheinfo *ci)
+
+int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
-	static int hz;
-
-	if (!hz)
-		hz = get_user_hz();
-
-	if (ci->ndm_refcnt)
-		print_uint(PRINT_ANY, "refcnt",
-				" ref %u", ci->ndm_refcnt);
-
-	print_uint(PRINT_ANY, "used", " used %u", ci->ndm_used / hz);
-	print_uint(PRINT_ANY, "confirmed", "/%u", ci->ndm_confirmed / hz);
-	print_uint(PRINT_ANY, "updated", "/%u", ci->ndm_updated / hz);
-}
-
-static void print_neigh_state(unsigned int nud)
-{
-
-	open_json_array(PRINT_JSON,
-			is_json_context() ?  "state" : "");
-
-#define PRINT_FLAG(f)						\
-	if (nud & NUD_##f) {					\
-		nud &= ~NUD_##f;				\
-		print_string(PRINT_ANY, NULL, " %s", #f);	\
-	}
-
-	PRINT_FLAG(INCOMPLETE);
-	PRINT_FLAG(REACHABLE);
-	PRINT_FLAG(STALE);
-	PRINT_FLAG(DELAY);
-	PRINT_FLAG(PROBE);
-	PRINT_FLAG(FAILED);
-	PRINT_FLAG(NOARP);
-	PRINT_FLAG(PERMANENT);
-#undef PRINT_FLAG
-
-	close_json_array(PRINT_JSON, NULL);
-}
-
-int print_neigh(struct nlmsghdr *n, void *arg)
-{
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 	struct ndmsg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
-	struct rtattr *tb[NDA_MAX+1];
+	struct rtattr * tb[NDA_MAX+1];
+	char abuf[256];
 	static int logit = 1;
-	__u8 protocol = 0;
 
 	if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH &&
 	    n->nlmsg_type != RTM_GETNEIGH) {
@@ -281,9 +221,8 @@
 		return 0;
 	if (!(filter.state&r->ndm_state) &&
 	    !(r->ndm_flags & NTF_PROXY) &&
-	    !(r->ndm_flags & NTF_EXT_LEARNED) &&
 	    (r->ndm_state || !(filter.state&0x100)) &&
-	    (r->ndm_family != AF_DECnet))
+             (r->ndm_family != AF_DECnet))
 		return 0;
 
 	if (filter.master && !(n->nlmsg_flags & NLM_F_DUMP_FILTERED)) {
@@ -296,123 +235,99 @@
 
 	parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
 
-	if (inet_addr_match_rta(&filter.pfx, tb[NDA_DST]))
-		return 0;
-
-	if (tb[NDA_PROTOCOL])
-		protocol = rta_getattr_u8(tb[NDA_PROTOCOL]);
-
-	if (filter.protocol && filter.protocol != protocol)
-		return 0;
-
+	if (tb[NDA_DST]) {
+		if (filter.pfx.family) {
+			inet_prefix dst;
+			memset(&dst, 0, sizeof(dst));
+			dst.family = r->ndm_family;
+			memcpy(&dst.data, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
+			if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
+				return 0;
+		}
+	}
 	if (filter.unused_only && tb[NDA_CACHEINFO]) {
 		struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
-
 		if (ci->ndm_refcnt)
 			return 0;
 	}
 
 	if (filter.flushb) {
 		struct nlmsghdr *fn;
-
 		if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
 			if (flush_update())
 				return -1;
 		}
-		fn = (struct nlmsghdr *)(filter.flushb + NLMSG_ALIGN(filter.flushp));
+		fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
 		memcpy(fn, n, n->nlmsg_len);
 		fn->nlmsg_type = RTM_DELNEIGH;
 		fn->nlmsg_flags = NLM_F_REQUEST;
 		fn->nlmsg_seq = ++rth.seq;
-		filter.flushp = (((char *)fn) + n->nlmsg_len) - filter.flushb;
+		filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
 		filter.flushed++;
 		if (show_stats < 2)
 			return 0;
 	}
 
-	open_json_object(NULL);
 	if (n->nlmsg_type == RTM_DELNEIGH)
-		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
+		fprintf(fp, "Deleted ");
 	else if (n->nlmsg_type == RTM_GETNEIGH)
-		print_null(PRINT_ANY, "miss", "%s ", "miss");
-
+		fprintf(fp, "miss ");
 	if (tb[NDA_DST]) {
-		const char *dst;
-		int family = r->ndm_family;
-
-		if (family == AF_BRIDGE) {
-			if (RTA_PAYLOAD(tb[NDA_DST]) == sizeof(struct in6_addr))
-				family = AF_INET6;
-			else
-				family = AF_INET;
-		}
-
-		dst = format_host_rta(family, tb[NDA_DST]);
-		print_color_string(PRINT_ANY,
-				   ifa_family_color(family),
-				   "dst", "%s ", dst);
+		fprintf(fp, "%s ",
+			format_host(r->ndm_family,
+				    RTA_PAYLOAD(tb[NDA_DST]),
+				    RTA_DATA(tb[NDA_DST]),
+				    abuf, sizeof(abuf)));
 	}
-
-	if (!filter.index && r->ndm_ifindex) {
-		if (!is_json_context())
-			fprintf(fp, "dev ");
-
-		print_color_string(PRINT_ANY, COLOR_IFNAME,
-				   "dev", "%s ",
-				   ll_index_to_name(r->ndm_ifindex));
-	}
-
+	if (!filter.index && r->ndm_ifindex)
+		fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex));
 	if (tb[NDA_LLADDR]) {
-		const char *lladdr;
 		SPRINT_BUF(b1);
+		fprintf(fp, "lladdr %s", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
+					      RTA_PAYLOAD(tb[NDA_LLADDR]),
+					      ll_index_to_type(r->ndm_ifindex),
+					      b1, sizeof(b1)));
+	}
+	if (r->ndm_flags & NTF_ROUTER) {
+		fprintf(fp, " router");
+	}
+	if (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();
 
-		lladdr = ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
-				     RTA_PAYLOAD(tb[NDA_LLADDR]),
-				     ll_index_to_type(r->ndm_ifindex),
-				     b1, sizeof(b1));
-
-		if (!is_json_context())
-			fprintf(fp, "lladdr ");
-
-		print_color_string(PRINT_ANY, COLOR_MAC,
-				   "lladdr", "%s", lladdr);
+		if (ci->ndm_refcnt)
+			printf(" ref %d", ci->ndm_refcnt);
+		fprintf(fp, " used %d/%d/%d", ci->ndm_used/hz,
+		       ci->ndm_confirmed/hz, ci->ndm_updated/hz);
 	}
 
-	if (r->ndm_flags & NTF_ROUTER)
-		print_null(PRINT_ANY, "router", " %s", "router");
-
-	if (r->ndm_flags & NTF_PROXY)
-		print_null(PRINT_ANY, "proxy", " %s", "proxy");
-
-	if (r->ndm_flags & NTF_EXT_LEARNED)
-		print_null(PRINT_ANY, "extern_learn", " %s ", "extern_learn");
-
-	if (r->ndm_flags & NTF_OFFLOADED)
-		print_null(PRINT_ANY, "offload", " %s", "offload");
-
-	if (show_stats) {
-		if (tb[NDA_CACHEINFO])
-			print_cacheinfo(RTA_DATA(tb[NDA_CACHEINFO]));
-
-		if (tb[NDA_PROBES])
-			print_uint(PRINT_ANY, "probes", " probes %u",
-				   rta_getattr_u32(tb[NDA_PROBES]));
+	if (tb[NDA_PROBES] && show_stats) {
+		__u32 p = rta_getattr_u32(tb[NDA_PROBES]);
+		fprintf(fp, " probes %u", p);
 	}
 
-	if (r->ndm_state)
-		print_neigh_state(r->ndm_state);
+	if (r->ndm_state) {
+		int nud = r->ndm_state;
+		fprintf(fp, " ");
 
-	if (protocol) {
-		SPRINT_BUF(b1);
-
-		print_string(PRINT_ANY, "protocol", " proto %s ",
-			     rtnl_rtprot_n2a(protocol, b1, sizeof(b1)));
+#define PRINT_FLAG(f) if (nud & NUD_##f) { \
+	nud &= ~NUD_##f; fprintf(fp, #f "%s", nud ? "," : ""); }
+		PRINT_FLAG(INCOMPLETE);
+		PRINT_FLAG(REACHABLE);
+		PRINT_FLAG(STALE);
+		PRINT_FLAG(DELAY);
+		PRINT_FLAG(PROBE);
+		PRINT_FLAG(FAILED);
+		PRINT_FLAG(NOARP);
+		PRINT_FLAG(PERMANENT);
+#undef PRINT_FLAG
 	}
+	fprintf(fp, "\n");
 
-	print_string(PRINT_FP, NULL, "\n", "");
-	close_json_object();
-	fflush(stdout);
-
+	fflush(fp);
 	return 0;
 }
 
@@ -423,32 +338,21 @@
 	filter.index = ifindex;
 }
 
-static int ipneigh_dump_filter(struct nlmsghdr *nlh, int reqlen)
-{
-	struct ndmsg *ndm = NLMSG_DATA(nlh);
-	int err;
-
-	ndm->ndm_flags = filter.ndm_flags;
-
-	if (filter.index) {
-		err = addattr32(nlh, reqlen, NDA_IFINDEX, filter.index);
-		if (err)
-			return err;
-	}
-	if (filter.master) {
-		err = addattr32(nlh, reqlen, NDA_MASTER, filter.master);
-		if (err)
-			return err;
-	}
-
-	return 0;
-}
-
 static int do_show_or_flush(int argc, char **argv, int flush)
 {
+	struct {
+		struct nlmsghdr	n;
+		struct ndmsg		ndm;
+		char  			buf[256];
+	} req;
 	char *filter_dev = NULL;
 	int state_given = 0;
 
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_type = RTM_GETNEIGH;
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+
 	ipneigh_reset_filter(0);
 
 	if (!filter.family)
@@ -471,27 +375,16 @@
 			filter_dev = *argv;
 		} else if (strcmp(*argv, "master") == 0) {
 			int ifindex;
-
 			NEXT_ARG();
 			ifindex = ll_name_to_index(*argv);
 			if (!ifindex)
 				invarg("Device does not exist\n", *argv);
-			filter.master = ifindex;
-		} else if (strcmp(*argv, "vrf") == 0) {
-			int ifindex;
-
-			NEXT_ARG();
-			ifindex = ll_name_to_index(*argv);
-			if (!ifindex)
-				invarg("Not a valid VRF name\n", *argv);
-			if (!name_is_vrf(*argv))
-				invarg("Not a valid VRF name\n", *argv);
+			addattr32(&req.n, sizeof(req), NDA_MASTER, ifindex);
 			filter.master = ifindex;
 		} else if (strcmp(*argv, "unused") == 0) {
 			filter.unused_only = 1;
 		} else if (strcmp(*argv, "nud") == 0) {
-			unsigned int state;
-
+			unsigned state;
 			NEXT_ARG();
 			if (!state_given) {
 				state_given = 1;
@@ -507,26 +400,15 @@
 			if (state == 0)
 				state = 0x100;
 			filter.state |= state;
-		} else if (strcmp(*argv, "proxy") == 0) {
-			filter.ndm_flags = NTF_PROXY;
-		} else if (matches(*argv, "protocol") == 0) {
-			__u32 prot;
-
-			NEXT_ARG();
-			if (rtnl_rtprot_a2n(&prot, *argv)) {
-				if (strcmp(*argv, "all"))
-					invarg("invalid \"protocol\"\n", *argv);
-				prot = 0;
-			}
-			filter.protocol = prot;
-		} else {
+		} else if (strcmp(*argv, "proxy") == 0)
+			req.ndm.ndm_flags = NTF_PROXY;
+		else {
 			if (strcmp(*argv, "to") == 0) {
 				NEXT_ARG();
 			}
 			if (matches(*argv, "help") == 0)
 				usage();
-			if (get_prefix(&filter.pfx, *argv, filter.family))
-				invarg("to value is invalid\n", *argv);
+			get_prefix(&filter.pfx, *argv, filter.family);
 			if (filter.family == AF_UNSPEC)
 				filter.family = filter.pfx.family;
 		}
@@ -536,9 +418,11 @@
 	ll_init_map(&rth);
 
 	if (filter_dev) {
-		filter.index = ll_name_to_index(filter_dev);
-		if (!filter.index)
-			return nodev(filter_dev);
+		if ((filter.index = ll_name_to_index(filter_dev)) == 0) {
+			fprintf(stderr, "Cannot find device \"%s\"\n", filter_dev);
+			return -1;
+		}
+		addattr32(&req.n, sizeof(req), NDA_IFINDEX, filter.index);
 	}
 
 	if (flush) {
@@ -548,10 +432,10 @@
 		filter.flushb = flushb;
 		filter.flushp = 0;
 		filter.flushe = sizeof(flushb);
+		filter.state &= ~NUD_FAILED;
 
 		while (round < MAX_ROUNDS) {
-			if (rtnl_neighdump_req(&rth, filter.family,
-					       ipneigh_dump_filter) < 0) {
+			if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) {
 				perror("Cannot send dump request");
 				exit(1);
 			}
@@ -565,7 +449,7 @@
 					if (round == 0)
 						printf("Nothing to flush.\n");
 					else
-						printf("*** Flush is complete after %d round%s ***\n", round, round > 1?"s":"");
+						printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
 				}
 				fflush(stdout);
 				return 0;
@@ -577,24 +461,23 @@
 				printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed);
 				fflush(stdout);
 			}
-			filter.state &= ~NUD_FAILED;
 		}
 		printf("*** Flush not complete bailing out after %d rounds\n",
 			MAX_ROUNDS);
 		return 1;
 	}
 
-	if (rtnl_neighdump_req(&rth, filter.family, ipneigh_dump_filter) < 0) {
+	req.ndm.ndm_family = filter.family;
+
+	if (rtnl_dump_request_n(&rth, &req.n) < 0) {
 		perror("Cannot send dump request");
 		exit(1);
 	}
 
-	new_json_obj(json);
 	if (rtnl_dump_filter(&rth, print_neigh, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
-	delete_json_obj();
 
 	return 0;
 }
diff --git a/ip/ipnetconf.c b/ip/ipnetconf.c
index 0e946ca..eca6eee 100644
--- a/ip/ipnetconf.c
+++ b/ip/ipnetconf.c
@@ -13,26 +13,23 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <string.h>
 #include <sys/time.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
-#include <errno.h>
 
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
 
-static struct {
+static struct
+{
 	int family;
 	int ifindex;
 } filter;
 
-static const char * const rp_filter_names[] = {
-	"off", "strict", "loose"
-};
-
 static void usage(void) __attribute__((noreturn));
 
 static void usage(void)
@@ -41,34 +38,20 @@
 	exit(-1);
 }
 
-static void print_onoff(FILE *fp, const char *flag, __u32 val)
-{
-	if (is_json_context())
-		print_bool(PRINT_JSON, flag, NULL, val);
-	else
-		fprintf(fp, "%s %s ", flag, val ? "on" : "off");
-}
+#define NETCONF_RTA(r)	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct netconfmsg))))
 
-static struct rtattr *netconf_rta(struct netconfmsg *ncm)
+int print_netconf(const struct sockaddr_nl *who, struct rtnl_ctrl_data *ctrl,
+		  struct nlmsghdr *n, void *arg)
 {
-	return (struct rtattr *)((char *)ncm
-				 + NLMSG_ALIGN(sizeof(struct netconfmsg)));
-}
-
-int print_netconf(struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg)
-{
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 	struct netconfmsg *ncm = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr *tb[NETCONFA_MAX+1];
-	int ifindex = 0;
 
 	if (n->nlmsg_type == NLMSG_ERROR)
 		return -1;
-
-	if (n->nlmsg_type != RTM_NEWNETCONF &&
-	    n->nlmsg_type != RTM_DELNETCONF) {
-		fprintf(stderr, "Not a netconf message: %08x %08x %08x\n",
+	if (n->nlmsg_type != RTM_NEWNETCONF) {
+		fprintf(stderr, "Not RTM_NEWNETCONF: %08x %08x %08x\n",
 			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
 
 		return -1;
@@ -82,80 +65,69 @@
 	if (filter.family && filter.family != ncm->ncm_family)
 		return 0;
 
-	parse_rtattr(tb, NETCONFA_MAX, netconf_rta(ncm),
+	parse_rtattr(tb, NETCONFA_MAX, NETCONF_RTA(ncm),
 		     NLMSG_PAYLOAD(n, sizeof(*ncm)));
 
-	if (tb[NETCONFA_IFINDEX])
-		ifindex = rta_getattr_u32(tb[NETCONFA_IFINDEX]);
-
-	if (filter.ifindex && filter.ifindex != ifindex)
-		return 0;
-
-	open_json_object(NULL);
-	if (n->nlmsg_type == RTM_DELNETCONF)
-		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
-
-	print_string(PRINT_ANY, "family",
-		     "%s ", family_name(ncm->ncm_family));
+	switch (ncm->ncm_family) {
+	case AF_INET:
+		fprintf(fp, "ipv4 ");
+		break;
+	case AF_INET6:
+		fprintf(fp, "ipv6 ");
+		break;
+	default:
+		fprintf(fp, "unknown ");
+		break;
+	}
 
 	if (tb[NETCONFA_IFINDEX]) {
-		const char *dev;
+		int *ifindex = (int *)RTA_DATA(tb[NETCONFA_IFINDEX]);
 
-		switch (ifindex) {
+		switch (*ifindex) {
 		case NETCONFA_IFINDEX_ALL:
-			dev = "all";
+			fprintf(fp, "all ");
 			break;
 		case NETCONFA_IFINDEX_DEFAULT:
-			dev = "default";
+			fprintf(fp, "default ");
 			break;
 		default:
-			dev = ll_index_to_name(ifindex);
+			fprintf(fp, "dev %s ", ll_index_to_name(*ifindex));
 			break;
 		}
-		print_color_string(PRINT_ANY, COLOR_IFNAME,
-				   "interface", "%s ", dev);
 	}
 
 	if (tb[NETCONFA_FORWARDING])
-		print_onoff(fp, "forwarding",
-				rta_getattr_u32(tb[NETCONFA_FORWARDING]));
-
+		fprintf(fp, "forwarding %s ",
+			*(int *)RTA_DATA(tb[NETCONFA_FORWARDING])?"on":"off");
 	if (tb[NETCONFA_RP_FILTER]) {
-		__u32 rp_filter = rta_getattr_u32(tb[NETCONFA_RP_FILTER]);
+		int rp_filter = *(int *)RTA_DATA(tb[NETCONFA_RP_FILTER]);
 
-		if (rp_filter < ARRAY_SIZE(rp_filter_names))
-			print_string(PRINT_ANY, "rp_filter",
-				     "rp_filter %s ",
-				     rp_filter_names[rp_filter]);
+		if (rp_filter == 0)
+			fprintf(fp, "rp_filter off ");
+		else if (rp_filter == 1)
+			fprintf(fp, "rp_filter strict ");
+		else if (rp_filter == 2)
+			fprintf(fp, "rp_filter loose ");
 		else
-			print_uint(PRINT_ANY, "rp_filter",
-				   "rp_filter %u ", rp_filter);
+			fprintf(fp, "rp_filter unknown mode ");
 	}
-
 	if (tb[NETCONFA_MC_FORWARDING])
-		print_onoff(fp, "mc_forwarding",
-				rta_getattr_u32(tb[NETCONFA_MC_FORWARDING]));
+		fprintf(fp, "mc_forwarding %d ",
+			*(int *)RTA_DATA(tb[NETCONFA_MC_FORWARDING]));
 
 	if (tb[NETCONFA_PROXY_NEIGH])
-		print_onoff(fp, "proxy_neigh",
-				rta_getattr_u32(tb[NETCONFA_PROXY_NEIGH]));
+		fprintf(fp, "proxy_neigh %s ",
+			*(int *)RTA_DATA(tb[NETCONFA_PROXY_NEIGH])?"on":"off");
 
-	if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN])
-		print_onoff(fp, "ignore_routes_with_linkdown",
-		     rta_getattr_u32(tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]));
-
-	if (tb[NETCONFA_INPUT])
-		print_onoff(fp, "input", rta_getattr_u32(tb[NETCONFA_INPUT]));
-
-	close_json_object();
-	print_string(PRINT_FP, NULL, "\n", NULL);
+	fprintf(fp, "\n");
 	fflush(fp);
 	return 0;
 }
 
-static int print_netconf2(struct nlmsghdr *n, void *arg)
+static int print_netconf2(const struct sockaddr_nl *who,
+			  struct nlmsghdr *n, void *arg)
 {
-	return print_netconf(NULL, n, arg);
+	return print_netconf(who, NULL, n, arg);
 }
 
 void ipnetconf_reset_filter(int ifindex)
@@ -170,22 +142,19 @@
 		struct nlmsghdr		n;
 		struct netconfmsg	ncm;
 		char			buf[1024];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct netconfmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
-		.n.nlmsg_type = RTM_GETNETCONF,
-	};
+	} req;
 
 	ipnetconf_reset_filter(0);
 	filter.family = preferred_family;
+	if (filter.family == AF_UNSPEC)
+		filter.family = AF_INET;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "dev") == 0) {
 			NEXT_ARG();
 			filter.ifindex = ll_name_to_index(*argv);
 			if (filter.ifindex <= 0) {
-				fprintf(stderr,
-					"Device \"%s\" does not exist.\n",
+				fprintf(stderr, "Device \"%s\" does not exist.\n",
 					*argv);
 				return -1;
 			}
@@ -194,11 +163,15 @@
 	}
 
 	ll_init_map(&rth);
-
-	if (filter.ifindex && filter.family != AF_UNSPEC) {
+	if (filter.ifindex) {
+		memset(&req, 0, sizeof(req));
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct netconfmsg));
+		req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
+		req.n.nlmsg_type = RTM_GETNETCONF;
 		req.ncm.ncm_family = filter.family;
-		addattr_l(&req.n, sizeof(req), NETCONFA_IFINDEX,
-			  &filter.ifindex, sizeof(filter.ifindex));
+		if (filter.ifindex)
+			addattr_l(&req.n, sizeof(req), NETCONFA_IFINDEX,
+				  &filter.ifindex, sizeof(filter.ifindex));
 
 		if (rtnl_send(&rth, &req.n, req.n.nlmsg_len) < 0) {
 			perror("Can not send request");
@@ -206,29 +179,16 @@
 		}
 		rtnl_listen(&rth, print_netconf, stdout);
 	} else {
-		rth.flags = RTNL_HANDLE_F_SUPPRESS_NLERR;
 dump:
-		if (rtnl_netconfdump_req(&rth, filter.family) < 0) {
+		if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNETCONF) < 0) {
 			perror("Cannot send dump request");
 			exit(1);
 		}
-
-		new_json_obj(json);
 		if (rtnl_dump_filter(&rth, print_netconf2, stdout) < 0) {
-			/* kernel does not support netconf dump on AF_UNSPEC;
-			 * fall back to requesting by family
-			 */
-			if (errno == EOPNOTSUPP &&
-			    filter.family == AF_UNSPEC) {
-				filter.family = AF_INET;
-				goto dump;
-			}
-			perror("RTNETLINK answers");
 			fprintf(stderr, "Dump terminated\n");
 			exit(1);
 		}
-		delete_json_obj();
-		if (preferred_family == AF_UNSPEC && filter.family == AF_INET) {
+		if (preferred_family == AF_UNSPEC) {
 			preferred_family = AF_INET6;
 			filter.family = AF_INET6;
 			goto dump;
@@ -249,8 +209,6 @@
 	} else
 		return do_show(0, NULL);
 
-	fprintf(stderr,
-		"Command \"%s\" is unknown, try \"ip netconf help\".\n",
-		*argv);
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip netconf help\".\n", *argv);
 	exit(-1);
 }
diff --git a/ip/ipnetns.c b/ip/ipnetns.c
index fc58a04..088096f 100644
--- a/ip/ipnetns.c
+++ b/ip/ipnetns.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #define _ATFILE_SOURCE
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -19,25 +18,21 @@
 #include <linux/net_namespace.h>
 
 #include "utils.h"
-#include "list.h"
+#include "hlist.h"
 #include "ip_common.h"
 #include "namespace.h"
-#include "json_print.h"
 
 static int usage(void)
 {
-	fprintf(stderr,
-		"Usage:	ip netns list\n"
-		"	ip netns add NAME\n"
-		"	ip netns attach NAME PID\n"
-		"	ip netns set NAME NETNSID\n"
-		"	ip [-all] netns delete [NAME]\n"
-		"	ip netns identify [PID]\n"
-		"	ip netns pids NAME\n"
-		"	ip [-all] netns exec [NAME] cmd ...\n"
-		"	ip netns monitor\n"
-		"	ip netns list-id [target-nsid POSITIVE-INT] [nsid POSITIVE-INT]\n"
-		"NETNSID := auto | POSITIVE-INT\n");
+	fprintf(stderr, "Usage: ip netns list\n");
+	fprintf(stderr, "       ip netns add NAME\n");
+	fprintf(stderr, "       ip netns set NAME NETNSID\n");
+	fprintf(stderr, "       ip [-all] netns delete [NAME]\n");
+	fprintf(stderr, "       ip netns identify [PID]\n");
+	fprintf(stderr, "       ip netns pids NAME\n");
+	fprintf(stderr, "       ip [-all] netns exec [NAME] cmd ...\n");
+	fprintf(stderr, "       ip netns monitor\n");
+	fprintf(stderr, "       ip netns list-id\n");
 	exit(-1);
 }
 
@@ -45,10 +40,9 @@
 static struct rtnl_handle rtnsh = { .fd = -1 };
 
 static int have_rtnl_getnsid = -1;
-static int saved_netns = -1;
-static struct link_filter filter;
 
-static int ipnetns_accept_msg(struct rtnl_ctrl_data *ctrl,
+static int ipnetns_accept_msg(const struct sockaddr_nl *who,
+			      struct rtnl_ctrl_data *ctrl,
 			      struct nlmsghdr *n, void *arg)
 {
 	struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n);
@@ -67,19 +61,20 @@
 		struct nlmsghdr n;
 		struct rtgenmsg g;
 		char            buf[1024];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_GETNSID,
-		.g.rtgen_family = AF_UNSPEC,
-	};
+	} req;
 	int fd;
 
 	if (have_rtnl_getnsid < 0) {
+		memset(&req, 0, sizeof(req));
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETNSID;
+		req.g.rtgen_family = AF_UNSPEC;
+
 		fd = open("/proc/self/ns/net", O_RDONLY);
 		if (fd < 0) {
-			have_rtnl_getnsid = 0;
-			return 0;
+			perror("open(\"/proc/self/ns/net\")");
+			exit(1);
 		}
 
 		addattr32(&req.n, 1024, NETNSA_FD, fd);
@@ -95,54 +90,49 @@
 	return have_rtnl_getnsid;
 }
 
-int get_netnsid_from_name(const char *name)
+static int get_netnsid_from_name(const char *name)
 {
 	struct {
 		struct nlmsghdr n;
 		struct rtgenmsg g;
 		char            buf[1024];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_GETNSID,
-		.g.rtgen_family = AF_UNSPEC,
-	};
-	struct nlmsghdr *answer;
+	} req, answer;
 	struct rtattr *tb[NETNSA_MAX + 1];
 	struct rtgenmsg *rthdr;
-	int len, fd, ret = -1;
+	int len, fd;
 
-	netns_nsid_socket_init();
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = RTM_GETNSID;
+	req.g.rtgen_family = AF_UNSPEC;
 
 	fd = netns_get_fd(name);
 	if (fd < 0)
 		return fd;
 
 	addattr32(&req.n, 1024, NETNSA_FD, fd);
-	if (rtnl_talk(&rtnsh, &req.n, &answer) < 0) {
+	if (rtnl_talk(&rtnsh, &req.n, &answer.n, sizeof(answer)) < 0) {
 		close(fd);
 		return -2;
 	}
 	close(fd);
 
 	/* Validate message and parse attributes */
-	if (answer->nlmsg_type == NLMSG_ERROR)
-		goto out;
+	if (answer.n.nlmsg_type == NLMSG_ERROR)
+		return -1;
 
-	rthdr = NLMSG_DATA(answer);
-	len = answer->nlmsg_len - NLMSG_SPACE(sizeof(*rthdr));
+	rthdr = NLMSG_DATA(&answer.n);
+	len = answer.n.nlmsg_len - NLMSG_SPACE(sizeof(*rthdr));
 	if (len < 0)
-		goto out;
+		return -1;
 
 	parse_rtattr(tb, NETNSA_MAX, NETNS_RTA(rthdr), len);
 
-	if (tb[NETNSA_NSID]) {
-		ret = rta_getattr_u32(tb[NETNSA_NSID]);
-	}
+	if (tb[NETNSA_NSID])
+		return rta_getattr_u32(tb[NETNSA_NSID]);
 
-out:
-	free(answer);
-	return ret;
+	return -1;
 }
 
 struct nsid_cache {
@@ -174,20 +164,6 @@
 	return NULL;
 }
 
-char *get_name_from_nsid(int nsid)
-{
-	struct nsid_cache *c;
-
-	netns_nsid_socket_init();
-	netns_map_init();
-
-	c = netns_map_get_by_nsid(nsid);
-	if (c)
-		return c->name;
-
-	return NULL;
-}
-
 static int netns_map_add(int nsid, const char *name)
 {
 	struct nsid_cache *c;
@@ -196,7 +172,7 @@
 	if (netns_map_get_by_nsid(nsid) != NULL)
 		return -EEXIST;
 
-	c = malloc(sizeof(*c) + strlen(name) + 1);
+	c = malloc(sizeof(*c) + strlen(name));
 	if (c == NULL) {
 		perror("malloc");
 		return -ENOMEM;
@@ -220,18 +196,6 @@
 	free(c);
 }
 
-void netns_nsid_socket_init(void)
-{
-	if (rtnsh.fd > -1 || !ipnetns_have_nsid())
-		return;
-
-	if (rtnl_open(&rtnsh, 0) < 0) {
-		fprintf(stderr, "Cannot open rtnetlink\n");
-		exit(1);
-	}
-
-}
-
 void netns_map_init(void)
 {
 	static int initialized;
@@ -242,6 +206,11 @@
 	if (initialized || !ipnetns_have_nsid())
 		return;
 
+	if (rtnl_open(&rtnsh, 0) < 0) {
+		fprintf(stderr, "Cannot open rtnetlink\n");
+		exit(1);
+	}
+
 	dir = opendir(NETNS_RUN_DIR);
 	if (!dir)
 		return;
@@ -287,7 +256,7 @@
 	return -ENOENT;
 }
 
-int print_nsid(struct nlmsghdr *n, void *arg)
+int print_nsid(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
 	struct rtgenmsg *rthdr = NLMSG_DATA(n);
 	struct rtattr *tb[NETNSA_MAX+1];
@@ -295,7 +264,7 @@
 	FILE *fp = (FILE *)arg;
 	struct nsid_cache *c;
 	char name[NAME_MAX];
-	int nsid, current;
+	int nsid;
 
 	if (n->nlmsg_type != RTM_NEWNSID && n->nlmsg_type != RTM_DELNSID)
 		return 0;
@@ -313,158 +282,46 @@
 		return -1;
 	}
 
-	open_json_object(NULL);
 	if (n->nlmsg_type == RTM_DELNSID)
-		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
+		fprintf(fp, "Deleted ");
 
 	nsid = rta_getattr_u32(tb[NETNSA_NSID]);
-	if (nsid < 0)
-		print_string(PRINT_ANY, "nsid", "nsid %s ", "not-assigned");
-	else
-		print_uint(PRINT_ANY, "nsid", "nsid %u ", nsid);
+	fprintf(fp, "nsid %u ", nsid);
 
-	if (tb[NETNSA_CURRENT_NSID]) {
-		current = rta_getattr_u32(tb[NETNSA_CURRENT_NSID]);
-		if (current < 0)
-			print_string(PRINT_ANY, "current-nsid",
-				     "current-nsid %s ", "not-assigned");
-		else
-			print_uint(PRINT_ANY, "current-nsid",
-				   "current-nsid %u ", current);
-	}
-
-	c = netns_map_get_by_nsid(tb[NETNSA_CURRENT_NSID] ? current : nsid);
+	c = netns_map_get_by_nsid(nsid);
 	if (c != NULL) {
-		print_string(PRINT_ANY, "name",
-			     "(iproute2 netns name: %s)", c->name);
+		fprintf(fp, "(iproute2 netns name: %s)", c->name);
 		netns_map_del(c);
 	}
 
 	/* During 'ip monitor nsid', no chance to have new nsid in cache. */
 	if (c == NULL && n->nlmsg_type == RTM_NEWNSID)
 		if (netns_get_name(nsid, name) == 0) {
-			print_string(PRINT_ANY, "name",
-				     "(iproute2 netns name: %s)", name);
+			fprintf(fp, "(iproute2 netns name: %s)", name);
 			netns_map_add(nsid, name);
 		}
 
-	print_string(PRINT_FP, NULL, "\n", NULL);
-	close_json_object();
+	fprintf(fp, "\n");
 	fflush(fp);
 	return 0;
 }
 
-static int get_netnsid_from_netnsid(int nsid)
-{
-	struct {
-		struct nlmsghdr n;
-		struct rtgenmsg g;
-		char            buf[1024];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct rtgenmsg))),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_GETNSID,
-		.g.rtgen_family = AF_UNSPEC,
-	};
-	struct nlmsghdr *answer;
-	int err;
-
-	netns_nsid_socket_init();
-
-	err = addattr32(&req.n, sizeof(req), NETNSA_NSID, nsid);
-	if (err)
-		return err;
-
-	if (filter.target_nsid >= 0) {
-		err = addattr32(&req.n, sizeof(req), NETNSA_TARGET_NSID,
-				filter.target_nsid);
-		if (err)
-			return err;
-	}
-
-	if (rtnl_talk(&rtnsh, &req.n, &answer) < 0)
-		return -2;
-
-	/* Validate message and parse attributes */
-	if (answer->nlmsg_type == NLMSG_ERROR)
-		goto err_out;
-
-	new_json_obj(json);
-	err = print_nsid(answer, stdout);
-	delete_json_obj();
-err_out:
-	free(answer);
-	return err;
-}
-
-static int netns_filter_req(struct nlmsghdr *nlh, int reqlen)
-{
-	int err;
-
-	if (filter.target_nsid >= 0) {
-		err = addattr32(nlh, reqlen, NETNSA_TARGET_NSID,
-				filter.target_nsid);
-		if (err)
-			return err;
-	}
-
-	return 0;
-}
-
 static int netns_list_id(int argc, char **argv)
 {
-	int nsid = -1;
-
 	if (!ipnetns_have_nsid()) {
 		fprintf(stderr,
 			"RTM_GETNSID is not supported by the kernel.\n");
 		return -ENOTSUP;
 	}
 
-	filter.target_nsid = -1;
-	while (argc > 0) {
-		if (strcmp(*argv, "target-nsid") == 0) {
-			if (filter.target_nsid >= 0)
-				duparg("target-nsid", *argv);
-			NEXT_ARG();
-
-			if (get_integer(&filter.target_nsid, *argv, 0))
-				invarg("\"target-nsid\" value is invalid\n",
-				       *argv);
-			else if (filter.target_nsid < 0)
-				invarg("\"target-nsid\" value should be >= 0\n",
-				       argv[1]);
-		} else if (strcmp(*argv, "nsid") == 0) {
-			if (nsid >= 0)
-				duparg("nsid", *argv);
-			NEXT_ARG();
-
-			if (get_integer(&nsid, *argv, 0))
-				invarg("\"nsid\" value is invalid\n", *argv);
-			else if (nsid < 0)
-				invarg("\"nsid\" value should be >= 0\n",
-				       argv[1]);
-		} else
-			usage();
-		argc--; argv++;
-	}
-
-	if (nsid >= 0)
-		return get_netnsid_from_netnsid(nsid);
-
-	if (rtnl_nsiddump_req_filter_fn(&rth, AF_UNSPEC,
-					netns_filter_req) < 0) {
+	if (rtnl_wilddump_request(&rth, AF_UNSPEC, RTM_GETNSID) < 0) {
 		perror("Cannot send dump request");
 		exit(1);
 	}
-
-	new_json_obj(json);
 	if (rtnl_dump_filter(&rth, print_nsid, stdout) < 0) {
-		delete_json_obj();
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
-	delete_json_obj();
 	return 0;
 }
 
@@ -478,48 +335,61 @@
 	if (!dir)
 		return 0;
 
-	new_json_obj(json);
 	while ((entry = readdir(dir)) != NULL) {
 		if (strcmp(entry->d_name, ".") == 0)
 			continue;
 		if (strcmp(entry->d_name, "..") == 0)
 			continue;
-
-		open_json_object(NULL);
-		print_string(PRINT_ANY, "name",
-			     "%s", entry->d_name);
+		printf("%s", entry->d_name);
 		if (ipnetns_have_nsid()) {
 			id = get_netnsid_from_name(entry->d_name);
 			if (id >= 0)
-				print_uint(PRINT_ANY, "id",
-					   " (id: %d)", id);
+				printf(" (id: %d)", id);
 		}
-		print_string(PRINT_FP, NULL, "\n", NULL);
-		close_json_object();
+		printf("\n");
 	}
 	closedir(dir);
-	delete_json_obj();
 	return 0;
 }
 
-static int do_switch(void *arg)
+static int cmd_exec(const char *cmd, char **argv, bool do_fork)
 {
-	char *netns = arg;
+	fflush(stdout);
+	if (do_fork) {
+		int status;
+		pid_t pid;
 
-	/* we just changed namespaces. clear any vrf association
-	 * with prior namespace before exec'ing command
-	 */
-	vrf_reset();
+		pid = fork();
+		if (pid < 0) {
+			perror("fork");
+			exit(1);
+		}
 
-	return netns_switch(netns);
+		if (pid != 0) {
+			/* Parent  */
+			if (waitpid(pid, &status, 0) < 0) {
+				perror("waitpid");
+				exit(1);
+			}
+
+			if (WIFEXITED(status)) {
+				return WEXITSTATUS(status);
+			}
+
+			exit(1);
+		}
+	}
+
+	if (execvp(cmd, argv)  < 0)
+		fprintf(stderr, "exec of \"%s\" failed: %s\n",
+				cmd, strerror(errno));
+	_exit(1);
 }
 
 static int on_netns_exec(char *nsname, void *arg)
 {
 	char **argv = arg;
-
-	printf("\nnetns: %s\n", nsname);
-	cmd_exec(argv[0], argv, true, do_switch, nsname);
+	cmd_exec(argv[1], argv + 1, true);
 	return 0;
 }
 
@@ -528,6 +398,8 @@
 	/* Setup the proper environment for apps that are not netns
 	 * aware, and execute a program in that environment.
 	 */
+	const char *cmd;
+
 	if (argc < 1 && !do_all) {
 		fprintf(stderr, "No netns name specified\n");
 		return -1;
@@ -538,19 +410,22 @@
 	}
 
 	if (do_all)
-		return netns_foreach(on_netns_exec, argv);
+		return do_each_netns(on_netns_exec, --argv, 1);
+
+	if (netns_switch(argv[0]))
+		return -1;
 
 	/* ip must return the status of the child,
 	 * but do_cmd() will add a minus to this,
 	 * so let's add another one here to cancel it.
 	 */
-	return -cmd_exec(argv[1], argv + 1, !!batch_mode, do_switch, argv[0]);
+	cmd = argv[1];
+	return -cmd_exec(cmd, argv + 1, !!batch_mode);
 }
 
 static int is_pid(const char *str)
 {
 	int ch;
-
 	for (; (ch = *str); str++) {
 		if (!isdigit(ch))
 			return 0;
@@ -595,10 +470,9 @@
 			strerror(errno));
 		return -1;
 	}
-	while ((entry = readdir(dir))) {
+	while((entry = readdir(dir))) {
 		char pid_net_path[PATH_MAX];
 		struct stat st;
-
 		if (!is_pid(entry->d_name))
 			continue;
 		snprintf(pid_net_path, sizeof(pid_net_path), "/proc/%s/ns/net",
@@ -615,15 +489,28 @@
 
 }
 
-int netns_identify_pid(const char *pidstr, char *name, int len)
+static int netns_identify(int argc, char **argv)
 {
+	const char *pidstr;
 	char net_path[PATH_MAX];
 	int netns;
 	struct stat netst;
 	DIR *dir;
 	struct dirent *entry;
 
-	name[0] = '\0';
+	if (argc < 1) {
+		pidstr = "self";
+	} else if (argc > 1) {
+		fprintf(stderr, "extra arguments specified\n");
+		return -1;
+	} else {
+		pidstr = argv[0];
+		if (!is_pid(pidstr)) {
+			fprintf(stderr, "Specified string '%s' is not a pid\n",
+					pidstr);
+			return -1;
+		}
+	}
 
 	snprintf(net_path, sizeof(net_path), "/proc/%s/ns/net", pidstr);
 	netns = open(net_path, O_RDONLY);
@@ -648,7 +535,7 @@
 		return -1;
 	}
 
-	while ((entry = readdir(dir))) {
+	while((entry = readdir(dir))) {
 		char name_path[PATH_MAX];
 		struct stat st;
 
@@ -665,7 +552,7 @@
 
 		if ((st.st_dev == netst.st_dev) &&
 		    (st.st_ino == netst.st_ino)) {
-			strlcpy(name, entry->d_name, len);
+			printf("%s\n", entry->d_name);
 		}
 	}
 	closedir(dir);
@@ -673,33 +560,6 @@
 
 }
 
-static int netns_identify(int argc, char **argv)
-{
-	const char *pidstr;
-	char name[256];
-	int rc;
-
-	if (argc < 1) {
-		pidstr = "self";
-	} else if (argc > 1) {
-		fprintf(stderr, "extra arguments specified\n");
-		return -1;
-	} else {
-		pidstr = argv[0];
-		if (!is_pid(pidstr)) {
-			fprintf(stderr, "Specified string '%s' is not a pid\n",
-					pidstr);
-			return -1;
-		}
-	}
-
-	rc = netns_identify_pid(pidstr, name, sizeof(name));
-	if (!rc)
-		printf("%s\n", name);
-
-	return rc;
-}
-
 static int on_netns_del(char *nsname, void *arg)
 {
 	char netns_path[PATH_MAX];
@@ -741,67 +601,24 @@
 	return 0;
 }
 
-/* Obtain a FD for the current namespace, so we can reenter it later */
-static void netns_save(void)
-{
-	if (saved_netns != -1)
-		return;
-
-	saved_netns = open("/proc/self/ns/net", O_RDONLY | O_CLOEXEC);
-	if (saved_netns == -1) {
-		perror("Cannot open init namespace");
-		exit(1);
-	}
-}
-
-static void netns_restore(void)
-{
-	if (saved_netns == -1)
-		return;
-
-	if (setns(saved_netns, CLONE_NEWNET)) {
-		perror("setns");
-		exit(1);
-	}
-
-	close(saved_netns);
-	saved_netns = -1;
-}
-
-static int netns_add(int argc, char **argv, bool create)
+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.
 	 *
-	 * If create is true, a new namespace will be created,
-	 * otherwise an existing one will be attached to the file.
-	 *
 	 * 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 users.
+	 * a new /etc/resolv.conf can be shared between uers.
 	 */
-	char netns_path[PATH_MAX], proc_path[PATH_MAX];
+	char netns_path[PATH_MAX];
 	const char *name;
-	pid_t pid;
 	int fd;
 	int made_netns_run_dir_mount = 0;
 
-	if (create) {
-		if (argc < 1) {
-			fprintf(stderr, "No netns name specified\n");
-			return -1;
-		}
-	} else {
-		if (argc < 2) {
-			fprintf(stderr, "No netns name and PID specified\n");
-			return -1;
-		}
-
-		if (get_s32(&pid, argv[1], 0) || !pid) {
-			fprintf(stderr, "Invalid PID: %s\n", argv[1]);
-			return -1;
-		}
+	if (argc < 1) {
+		fprintf(stderr, "No netns name specified\n");
+		return -1;
 	}
 	name = argv[0];
 
@@ -825,7 +642,7 @@
 		}
 
 		/* Upgrade NETNS_RUN_DIR to a mount point */
-		if (mount(NETNS_RUN_DIR, NETNS_RUN_DIR, "none", MS_BIND | MS_REC, NULL)) {
+		if (mount(NETNS_RUN_DIR, NETNS_RUN_DIR, "none", MS_BIND, NULL)) {
 			fprintf(stderr, "mount --bind %s %s failed: %s\n",
 				NETNS_RUN_DIR, NETNS_RUN_DIR, strerror(errno));
 			return -1;
@@ -841,55 +658,38 @@
 		return -1;
 	}
 	close(fd);
-
-	if (create) {
-		netns_save();
-		if (unshare(CLONE_NEWNET) < 0) {
-			fprintf(stderr, "Failed to create a new network namespace \"%s\": %s\n",
-				name, strerror(errno));
-			goto out_delete;
-		}
-
-		strcpy(proc_path, "/proc/self/ns/net");
-	} else {
-		snprintf(proc_path, sizeof(proc_path), "/proc/%d/ns/net", pid);
+	if (unshare(CLONE_NEWNET) < 0) {
+		fprintf(stderr, "Failed to create a new network namespace \"%s\": %s\n",
+			name, strerror(errno));
+		goto out_delete;
 	}
 
 	/* Bind the netns last so I can watch for it */
-	if (mount(proc_path, netns_path, "none", MS_BIND, NULL) < 0) {
-		fprintf(stderr, "Bind %s -> %s failed: %s\n",
-			proc_path, netns_path, strerror(errno));
+	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;
 	}
-	netns_restore();
-
 	return 0;
 out_delete:
-	if (create) {
-		netns_restore();
-		netns_delete(argc, argv);
-	} else if (unlink(netns_path) < 0) {
-		fprintf(stderr, "Cannot remove namespace file \"%s\": %s\n",
-			netns_path, strerror(errno));
-	}
+	netns_delete(argc, argv);
 	return -1;
 }
 
-int set_netnsid_from_name(const char *name, int nsid)
+static int set_netnsid_from_name(const char *name, int nsid)
 {
 	struct {
 		struct nlmsghdr n;
 		struct rtgenmsg g;
 		char            buf[1024];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_NEWNSID,
-		.g.rtgen_family = AF_UNSPEC,
-	};
+	} req;
 	int fd, err = 0;
 
-	netns_nsid_socket_init();
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = RTM_NEWNSID;
+	req.g.rtgen_family = AF_UNSPEC;
 
 	fd = netns_get_fd(name);
 	if (fd < 0)
@@ -897,7 +697,7 @@
 
 	addattr32(&req.n, 1024, NETNSA_FD, fd);
 	addattr32(&req.n, 1024, NETNSA_NSID, nsid);
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		err = -2;
 
 	close(fd);
@@ -919,13 +719,7 @@
 		return -1;
 	}
 	name = argv[0];
-	/* If a negative nsid is specified the kernel will select the nsid. */
-	if (strcmp(argv[1], "auto") == 0)
-		nsid = -1;
-	else if (get_integer(&nsid, argv[1], 0))
-		invarg("Invalid \"netnsid\" value\n", argv[1]);
-	else if (nsid < 0)
-		invarg("\"netnsid\" value should be >= 0\n", argv[1]);
+	nsid = atoi(argv[1]);
 
 	snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);
 	netns = open(netns_path, O_RDONLY | O_CLOEXEC);
@@ -943,7 +737,6 @@
 	char buf[4096];
 	struct inotify_event *event;
 	int fd;
-
 	fd = inotify_init();
 	if (fd < 0) {
 		fprintf(stderr, "inotify_init failed: %s\n",
@@ -959,9 +752,8 @@
 			strerror(errno));
 		return -1;
 	}
-	for (;;) {
+	for(;;) {
 		ssize_t len = read(fd, buf, sizeof(buf));
-
 		if (len < 0) {
 			fprintf(stderr, "read failed: %s\n",
 				strerror(errno));
@@ -979,42 +771,25 @@
 	return 0;
 }
 
-static int invalid_name(const char *name)
-{
-	return !*name || strlen(name) > NAME_MAX ||
-		strchr(name, '/') || !strcmp(name, ".") || !strcmp(name, "..");
-}
-
 int do_netns(int argc, char **argv)
 {
-	netns_nsid_socket_init();
+	netns_map_init();
 
-	if (argc < 1) {
-		netns_map_init();
+	if (argc < 1)
 		return netns_list(0, NULL);
-	}
-
-	if (!do_all && argc > 1 && invalid_name(argv[1])) {
-		fprintf(stderr, "Invalid netns name \"%s\"\n", argv[1]);
-		exit(-1);
-	}
 
 	if ((matches(*argv, "list") == 0) || (matches(*argv, "show") == 0) ||
-	    (matches(*argv, "lst") == 0)) {
-		netns_map_init();
+	    (matches(*argv, "lst") == 0))
 		return netns_list(argc-1, argv+1);
-	}
 
-	if ((matches(*argv, "list-id") == 0)) {
-		netns_map_init();
+	if ((matches(*argv, "list-id") == 0))
 		return netns_list_id(argc-1, argv+1);
-	}
 
 	if (matches(*argv, "help") == 0)
 		return usage();
 
 	if (matches(*argv, "add") == 0)
-		return netns_add(argc-1, argv+1, true);
+		return netns_add(argc-1, argv+1);
 
 	if (matches(*argv, "set") == 0)
 		return netns_set(argc-1, argv+1);
@@ -1034,9 +809,6 @@
 	if (matches(*argv, "monitor") == 0)
 		return netns_monitor(argc-1, argv+1);
 
-	if (matches(*argv, "attach") == 0)
-		return netns_add(argc-1, argv+1, false);
-
 	fprintf(stderr, "Command \"%s\" is unknown, try \"ip netns help\".\n", *argv);
 	exit(-1);
 }
diff --git a/ip/ipnexthop.c b/ip/ipnexthop.c
deleted file mode 100644
index 9f860c8..0000000
--- a/ip/ipnexthop.c
+++ /dev/null
@@ -1,573 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * ip nexthop
- *
- * Copyright (c) 2017-19 David Ahern <dsahern@gmail.com>
- */
-
-#include <linux/nexthop.h>
-#include <stdio.h>
-#include <string.h>
-#include <rt_names.h>
-#include <errno.h>
-
-#include "utils.h"
-#include "ip_common.h"
-
-static struct {
-	unsigned int flushed;
-	unsigned int groups;
-	unsigned int ifindex;
-	unsigned int master;
-	unsigned int proto;
-} filter;
-
-enum {
-	IPNH_LIST,
-	IPNH_FLUSH,
-};
-
-#define RTM_NHA(h)  ((struct rtattr *)(((char *)(h)) + \
-			NLMSG_ALIGN(sizeof(struct nhmsg))))
-
-static void usage(void) __attribute__((noreturn));
-
-static void usage(void)
-{
-	fprintf(stderr,
-		"Usage: ip nexthop { list | flush } [ protocol ID ] SELECTOR\n"
-		"       ip nexthop { add | replace } id ID NH [ protocol ID ]\n"
-		"       ip nexthop { get| del } id ID\n"
-		"SELECTOR := [ id ID ] [ dev DEV ] [ vrf NAME ] [ master DEV ]\n"
-		"            [ groups ]\n"
-		"NH := { blackhole | [ via ADDRESS ] [ dev DEV ] [ onlink ]\n"
-		"      [ encap ENCAPTYPE ENCAPHDR ] | group GROUP ] }\n"
-		"GROUP := [ id[,weight]>/<id[,weight]>/... ]\n"
-		"ENCAPTYPE := [ mpls ]\n"
-		"ENCAPHDR := [ MPLSLABEL ]\n");
-	exit(-1);
-}
-
-static int nh_dump_filter(struct nlmsghdr *nlh, int reqlen)
-{
-	int err;
-
-	if (filter.ifindex) {
-		err = addattr32(nlh, reqlen, NHA_OIF, filter.ifindex);
-		if (err)
-			return err;
-	}
-
-	if (filter.groups) {
-		addattr_l(nlh, reqlen, NHA_GROUPS, NULL, 0);
-		if (err)
-			return err;
-	}
-
-	if (filter.master) {
-		addattr32(nlh, reqlen, NHA_MASTER, filter.master);
-		if (err)
-			return err;
-	}
-
-	return 0;
-}
-
-static struct rtnl_handle rth_del = { .fd = -1 };
-
-static int delete_nexthop(__u32 id)
-{
-	struct {
-		struct nlmsghdr	n;
-		struct nhmsg	nhm;
-		char		buf[64];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_DELNEXTHOP,
-		.nhm.nh_family = AF_UNSPEC,
-	};
-
-	req.n.nlmsg_seq = ++rth_del.seq;
-
-	addattr32(&req.n, sizeof(req), NHA_ID, id);
-
-	if (rtnl_talk(&rth_del, &req.n, NULL) < 0)
-		return -1;
-	return 0;
-}
-
-static int flush_nexthop(struct nlmsghdr *nlh, void *arg)
-{
-	struct nhmsg *nhm = NLMSG_DATA(nlh);
-	struct rtattr *tb[NHA_MAX+1];
-	__u32 id = 0;
-	int len;
-
-	len = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*nhm));
-	if (len < 0) {
-		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
-		return -1;
-	}
-
-	if (filter.proto && nhm->nh_protocol != filter.proto)
-		return 0;
-
-	parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len);
-	if (tb[NHA_ID])
-		id = rta_getattr_u32(tb[NHA_ID]);
-
-	if (id && !delete_nexthop(id))
-		filter.flushed++;
-
-	return 0;
-}
-
-static int ipnh_flush(unsigned int all)
-{
-	int rc = -2;
-
-	if (all) {
-		filter.groups = 1;
-		filter.ifindex = 0;
-		filter.master = 0;
-	}
-
-	if (rtnl_open(&rth_del, 0) < 0) {
-		fprintf(stderr, "Cannot open rtnetlink\n");
-		return EXIT_FAILURE;
-	}
-again:
-	if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) {
-		perror("Cannot send dump request");
-		goto out;
-	}
-
-	if (rtnl_dump_filter(&rth, flush_nexthop, stdout) < 0) {
-		fprintf(stderr, "Dump terminated. Failed to flush nexthops\n");
-		goto out;
-	}
-
-	/* if deleting all, then remove groups first */
-	if (all && filter.groups) {
-		filter.groups = 0;
-		goto again;
-	}
-
-	rc = 0;
-out:
-	rtnl_close(&rth_del);
-	if (!filter.flushed)
-		printf("Nothing to flush\n");
-	else
-		printf("Flushed %d nexthops\n", filter.flushed);
-
-	return rc;
-}
-
-static void print_nh_group(FILE *fp, const struct rtattr *grps_attr)
-{
-	struct nexthop_grp *nhg = RTA_DATA(grps_attr);
-	int num = RTA_PAYLOAD(grps_attr) / sizeof(*nhg);
-	int i;
-
-	if (!num || num * sizeof(*nhg) != RTA_PAYLOAD(grps_attr)) {
-		fprintf(fp, "<invalid nexthop group>");
-		return;
-	}
-
-	open_json_array(PRINT_JSON, "group");
-	print_string(PRINT_FP, NULL, "%s", "group ");
-	for (i = 0; i < num; ++i) {
-		open_json_object(NULL);
-
-		if (i)
-			print_string(PRINT_FP, NULL, "%s", "/");
-
-		print_uint(PRINT_ANY, "id", "%u", nhg[i].id);
-		if (nhg[i].weight)
-			print_uint(PRINT_ANY, "weight", ",%u", nhg[i].weight + 1);
-
-		close_json_object();
-	}
-	print_string(PRINT_FP, NULL, "%s", " ");
-	close_json_array(PRINT_JSON, NULL);
-}
-
-int print_nexthop(struct nlmsghdr *n, void *arg)
-{
-	struct nhmsg *nhm = NLMSG_DATA(n);
-	struct rtattr *tb[NHA_MAX+1];
-	FILE *fp = (FILE *)arg;
-	int len;
-
-	SPRINT_BUF(b1);
-
-	if (n->nlmsg_type != RTM_DELNEXTHOP &&
-	    n->nlmsg_type != RTM_NEWNEXTHOP) {
-		fprintf(stderr, "Not a nexthop: %08x %08x %08x\n",
-			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
-		return -1;
-	}
-
-	len = n->nlmsg_len - NLMSG_SPACE(sizeof(*nhm));
-	if (len < 0) {
-		close_json_object();
-		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
-		return -1;
-	}
-
-	if (filter.proto && filter.proto != nhm->nh_protocol)
-		return 0;
-
-	parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len);
-
-	open_json_object(NULL);
-
-	if (n->nlmsg_type == RTM_DELROUTE)
-		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
-
-	if (tb[NHA_ID])
-		print_uint(PRINT_ANY, "id", "id %u ",
-			   rta_getattr_u32(tb[NHA_ID]));
-
-	if (tb[NHA_GROUP])
-		print_nh_group(fp, tb[NHA_GROUP]);
-
-	if (tb[NHA_ENCAP])
-		lwt_print_encap(fp, tb[NHA_ENCAP_TYPE], tb[NHA_ENCAP]);
-
-	if (tb[NHA_GATEWAY])
-		print_rta_gateway(fp, nhm->nh_family, tb[NHA_GATEWAY]);
-
-	if (tb[NHA_OIF])
-		print_rta_if(fp, tb[NHA_OIF], "dev");
-
-	if (nhm->nh_scope != RT_SCOPE_UNIVERSE || show_details > 0) {
-		print_string(PRINT_ANY, "scope", "scope %s ",
-			     rtnl_rtscope_n2a(nhm->nh_scope, b1, sizeof(b1)));
-	}
-
-	if (tb[NHA_BLACKHOLE])
-		print_null(PRINT_ANY, "blackhole", "blackhole ", NULL);
-
-	if (nhm->nh_protocol != RTPROT_UNSPEC || show_details > 0) {
-		print_string(PRINT_ANY, "protocol", "proto %s ",
-			     rtnl_rtprot_n2a(nhm->nh_protocol, b1, sizeof(b1)));
-	}
-
-	if (tb[NHA_OIF])
-		print_rt_flags(fp, nhm->nh_flags);
-
-	print_string(PRINT_FP, NULL, "%s", "\n");
-	close_json_object();
-	fflush(fp);
-
-	return 0;
-}
-
-static int add_nh_group_attr(struct nlmsghdr *n, int maxlen, char *argv)
-{
-	struct nexthop_grp *grps;
-	int count = 0, i;
-	char *sep, *wsep;
-
-	if (*argv != '\0')
-		count = 1;
-
-	/* separator is '/' */
-	sep = strchr(argv, '/');
-	while (sep) {
-		count++;
-		sep = strchr(sep + 1, '/');
-	}
-
-	if (count == 0)
-		return -1;
-
-	grps = calloc(count, sizeof(*grps));
-	if (!grps)
-		return -1;
-
-	for (i = 0; i < count; ++i) {
-		sep = strchr(argv, '/');
-		if (sep)
-			*sep = '\0';
-
-		wsep = strchr(argv, ',');
-		if (wsep)
-			*wsep = '\0';
-
-		if (get_unsigned(&grps[i].id, argv, 0))
-			return -1;
-		if (wsep) {
-			unsigned int w;
-
-			wsep++;
-			if (get_unsigned(&w, wsep, 0) || w == 0 || w > 256)
-				invarg("\"weight\" is invalid\n", wsep);
-			grps[i].weight = w - 1;
-		}
-
-		if (!sep)
-			break;
-
-		argv = sep + 1;
-	}
-
-	return addattr_l(n, maxlen, NHA_GROUP, grps, count * sizeof(*grps));
-}
-
-static int ipnh_modify(int cmd, unsigned int flags, int argc, char **argv)
-{
-	struct {
-		struct nlmsghdr	n;
-		struct nhmsg	nhm;
-		char		buf[1024];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST | flags,
-		.n.nlmsg_type = cmd,
-		.nhm.nh_family = preferred_family,
-	};
-	__u32 nh_flags = 0;
-
-	while (argc > 0) {
-		if (!strcmp(*argv, "id")) {
-			__u32 id;
-
-			NEXT_ARG();
-			if (get_unsigned(&id, *argv, 0))
-				invarg("invalid id value", *argv);
-			addattr32(&req.n, sizeof(req), NHA_ID, id);
-		} else if (!strcmp(*argv, "dev")) {
-			int ifindex;
-
-			NEXT_ARG();
-			ifindex = ll_name_to_index(*argv);
-			if (!ifindex)
-				invarg("Device does not exist\n", *argv);
-			addattr32(&req.n, sizeof(req), NHA_OIF, ifindex);
-			if (req.nhm.nh_family == AF_UNSPEC)
-				req.nhm.nh_family = AF_INET;
-		} else if (strcmp(*argv, "via") == 0) {
-			inet_prefix addr;
-			int family;
-
-			NEXT_ARG();
-			family = read_family(*argv);
-			if (family == AF_UNSPEC)
-				family = req.nhm.nh_family;
-			else
-				NEXT_ARG();
-			get_addr(&addr, *argv, family);
-			if (req.nhm.nh_family == AF_UNSPEC)
-				req.nhm.nh_family = addr.family;
-			else if (req.nhm.nh_family != addr.family)
-				invarg("address family mismatch\n", *argv);
-			addattr_l(&req.n, sizeof(req), NHA_GATEWAY,
-				  &addr.data, addr.bytelen);
-		} else if (strcmp(*argv, "encap") == 0) {
-			char buf[1024];
-			struct rtattr *rta = (void *)buf;
-
-			rta->rta_type = NHA_ENCAP;
-			rta->rta_len = RTA_LENGTH(0);
-
-			lwt_parse_encap(rta, sizeof(buf), &argc, &argv,
-					NHA_ENCAP, NHA_ENCAP_TYPE);
-
-			if (rta->rta_len > RTA_LENGTH(0)) {
-				addraw_l(&req.n, 1024, RTA_DATA(rta),
-					 RTA_PAYLOAD(rta));
-			}
-		} else if (!strcmp(*argv, "blackhole")) {
-			addattr_l(&req.n, sizeof(req), NHA_BLACKHOLE, NULL, 0);
-			if (req.nhm.nh_family == AF_UNSPEC)
-				req.nhm.nh_family = AF_INET;
-		} else if (!strcmp(*argv, "onlink")) {
-			nh_flags |= RTNH_F_ONLINK;
-		} else if (!strcmp(*argv, "group")) {
-			NEXT_ARG();
-
-			if (add_nh_group_attr(&req.n, sizeof(req), *argv))
-				invarg("\"group\" value is invalid\n", *argv);
-		} else if (matches(*argv, "protocol") == 0) {
-			__u32 prot;
-
-			NEXT_ARG();
-			if (rtnl_rtprot_a2n(&prot, *argv))
-				invarg("\"protocol\" value is invalid\n", *argv);
-			req.nhm.nh_protocol = prot;
-		} else if (strcmp(*argv, "help") == 0) {
-			usage();
-		} else {
-			invarg("", *argv);
-		}
-		argc--; argv++;
-	}
-
-	req.nhm.nh_flags = nh_flags;
-
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
-		return -2;
-
-	return 0;
-}
-
-static int ipnh_get_id(__u32 id)
-{
-	struct {
-		struct nlmsghdr	n;
-		struct nhmsg	nhm;
-		char		buf[1024];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type  = RTM_GETNEXTHOP,
-		.nhm.nh_family = preferred_family,
-	};
-	struct nlmsghdr *answer;
-
-	addattr32(&req.n, sizeof(req), NHA_ID, id);
-
-	if (rtnl_talk(&rth, &req.n, &answer) < 0)
-		return -2;
-
-	new_json_obj(json);
-
-	if (print_nexthop(answer, (void *)stdout) < 0) {
-		free(answer);
-		return -1;
-	}
-
-	delete_json_obj();
-	fflush(stdout);
-
-	free(answer);
-
-	return 0;
-}
-
-static int ipnh_list_flush(int argc, char **argv, int action)
-{
-	unsigned int all = (argc == 0);
-
-	while (argc > 0) {
-		if (!matches(*argv, "dev")) {
-			NEXT_ARG();
-			filter.ifindex = ll_name_to_index(*argv);
-			if (!filter.ifindex)
-				invarg("Device does not exist\n", *argv);
-		} else if (!matches(*argv, "groups")) {
-			filter.groups = 1;
-		} else if (!matches(*argv, "master")) {
-			NEXT_ARG();
-			filter.master = ll_name_to_index(*argv);
-			if (!filter.master)
-				invarg("Device does not exist\n", *argv);
-		} else if (matches(*argv, "vrf") == 0) {
-			NEXT_ARG();
-			if (!name_is_vrf(*argv))
-				invarg("Invalid VRF\n", *argv);
-			filter.master = ll_name_to_index(*argv);
-			if (!filter.master)
-				invarg("VRF does not exist\n", *argv);
-		} else if (!strcmp(*argv, "id")) {
-			__u32 id;
-
-			NEXT_ARG();
-			if (get_unsigned(&id, *argv, 0))
-				invarg("invalid id value", *argv);
-			return ipnh_get_id(id);
-		} else if (!matches(*argv, "protocol")) {
-			__u32 proto;
-
-			NEXT_ARG();
-			if (get_unsigned(&proto, *argv, 0))
-				invarg("invalid protocol value", *argv);
-			filter.proto = proto;
-		} else if (matches(*argv, "help") == 0) {
-			usage();
-		} else {
-			invarg("", *argv);
-		}
-		argc--; argv++;
-	}
-
-	if (action == IPNH_FLUSH)
-		return ipnh_flush(all);
-
-	if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) {
-		perror("Cannot send dump request");
-		return -2;
-	}
-
-	new_json_obj(json);
-
-	if (rtnl_dump_filter(&rth, print_nexthop, stdout) < 0) {
-		fprintf(stderr, "Dump terminated\n");
-		return -2;
-	}
-
-	delete_json_obj();
-	fflush(stdout);
-
-	return 0;
-}
-
-static int ipnh_get(int argc, char **argv)
-{
-	__u32 id = 0;
-
-	while (argc > 0) {
-		if (!strcmp(*argv, "id")) {
-			NEXT_ARG();
-			if (get_unsigned(&id, *argv, 0))
-				invarg("invalid id value", *argv);
-		} else  {
-			usage();
-		}
-		argc--; argv++;
-	}
-
-	if (!id) {
-		usage();
-		return -1;
-	}
-
-	return ipnh_get_id(id);
-}
-
-int do_ipnh(int argc, char **argv)
-{
-	if (argc < 1)
-		return ipnh_list_flush(0, NULL, IPNH_LIST);
-
-	if (!matches(*argv, "add"))
-		return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_EXCL,
-				   argc-1, argv+1);
-	if (!matches(*argv, "replace"))
-		return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_REPLACE,
-				   argc-1, argv+1);
-	if (!matches(*argv, "delete"))
-		return ipnh_modify(RTM_DELNEXTHOP, 0, argc-1, argv+1);
-
-	if (!matches(*argv, "list") ||
-	    !matches(*argv, "show") ||
-	    !matches(*argv, "lst"))
-		return ipnh_list_flush(argc-1, argv+1, IPNH_LIST);
-
-	if (!matches(*argv, "get"))
-		return ipnh_get(argc-1, argv+1);
-
-	if (!matches(*argv, "flush"))
-		return ipnh_list_flush(argc-1, argv+1, IPNH_FLUSH);
-
-	if (!matches(*argv, "help"))
-		usage();
-
-	fprintf(stderr,
-		"Command \"%s\" is unknown, try \"ip nexthop help\".\n", *argv);
-	exit(-1);
-}
diff --git a/ip/ipntable.c b/ip/ipntable.c
index ddee490..6eb84e7 100644
--- a/ip/ipntable.c
+++ b/ip/ipntable.c
@@ -31,14 +31,13 @@
 
 #include "utils.h"
 #include "ip_common.h"
-#include "json_print.h"
 
 static struct
 {
 	int family;
-	int index;
+        int index;
 #define NONE_DEV	(-1)
-	const char *name;
+	char name[1024];
 } filter;
 
 static void usage(void) __attribute__((noreturn));
@@ -47,15 +46,15 @@
 {
 	fprintf(stderr,
 		"Usage: ip ntable change name NAME [ dev DEV ]\n"
-		"	 [ thresh1 VAL ] [ thresh2 VAL ] [ thresh3 VAL ] [ gc_int MSEC ]\n"
-		"	 [ PARMS ]\n"
+		"          [ thresh1 VAL ] [ thresh2 VAL ] [ thresh3 VAL ] [ gc_int MSEC ]\n"
+		"          [ PARMS ]\n"
 		"Usage: ip ntable show [ dev DEV ] [ name NAME ]\n"
 
 		"PARMS := [ base_reachable MSEC ] [ retrans MSEC ] [ gc_stale MSEC ]\n"
-		"	 [ delay_probe MSEC ] [ queue LEN ]\n"
-		"	 [ app_probes VAL ] [ ucast_probes VAL ] [ mcast_probes VAL ]\n"
-		"	 [ anycast_delay MSEC ] [ proxy_delay MSEC ] [ proxy_queue LEN ]\n"
-		"	 [ locktime MSEC ]\n"
+		"         [ delay_probe MSEC ] [ queue LEN ]\n"
+		"         [ app_probs VAL ] [ ucast_probes VAL ] [ mcast_probes VAL ]\n"
+		"         [ anycast_delay MSEC ] [ proxy_delay MSEC ] [ proxy_queue LEN ]\n"
+		"         [ locktime MSEC ]\n"
 		);
 
 	exit(-1);
@@ -66,20 +65,27 @@
 	struct {
 		struct nlmsghdr	n;
 		struct ndtmsg		ndtm;
-		char			buf[1024];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndtmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST | flags,
-		.n.nlmsg_type = cmd,
-		.ndtm.ndtm_family = preferred_family,
-	};
+		char  			buf[1024];
+	} req;
 	char *namep = NULL;
 	char *threshsp = NULL;
 	char *gc_intp = NULL;
-	char parms_buf[1024] = {};
+	char parms_buf[1024];
 	struct rtattr *parms_rta = (struct rtattr *)parms_buf;
 	int parms_change = 0;
 
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndtmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+	req.n.nlmsg_type = cmd;
+
+	req.ndtm.ndtm_family = preferred_family;
+	req.ndtm.ndtm_pad1 = 0;
+	req.ndtm.ndtm_pad2 = 0;
+
+	memset(&parms_buf, 0, sizeof(parms_buf));
+
 	parms_rta->rta_type = NDTA_PARMS;
 	parms_rta->rta_len = RTA_LENGTH(0);
 
@@ -140,8 +146,10 @@
 
 			NEXT_ARG();
 			ifindex = ll_name_to_index(*argv);
-			if (!ifindex)
-				return nodev(*argv);
+			if (ifindex == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n", *argv);
+				return -1;
+			}
 
 			rta_addattr32(parms_rta, sizeof(parms_buf),
 				      NDTPA_IFINDEX, ifindex);
@@ -201,6 +209,8 @@
 			if (get_u32(&queue, *argv, 0))
 				invarg("\"queue\" value is invalid", *argv);
 
+			if (!parms_rta)
+				parms_rta = (struct rtattr *)&parms_buf;
 			rta_addattr32(parms_rta, sizeof(parms_buf),
 				      NDTPA_QUEUE_LEN, queue);
 			parms_change = 1;
@@ -303,7 +313,7 @@
 			  RTA_PAYLOAD(parms_rta));
 	}
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		exit(2);
 
 	return 0;
@@ -312,13 +322,15 @@
 static const char *ntable_strtime_delta(__u32 msec)
 {
 	static char str[32];
-	struct timeval now = {};
+	struct timeval now;
 	time_t t;
 	struct tm *tp;
 
 	if (msec == 0)
 		goto error;
 
+	memset(&now, 0, sizeof(now));
+
 	if (gettimeofday(&now, NULL) < 0) {
 		perror("gettimeofday");
 		goto error;
@@ -337,192 +349,9 @@
 	return str;
 }
 
-static void print_ndtconfig(const struct ndt_config *ndtc)
+static int print_ntable(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
-
-	print_uint(PRINT_ANY, "key_length",
-		   "    config key_len %u ", ndtc->ndtc_key_len);
-	print_uint(PRINT_ANY, "entry_size",
-		   "entry_size %u ", ndtc->ndtc_entry_size);
-	print_uint(PRINT_ANY, "entries", "entries %u ", ndtc->ndtc_entries);
-
-	print_nl();
-
-	print_string(PRINT_ANY, "last_flush",
-		     "        last_flush %s ",
-		     ntable_strtime_delta(ndtc->ndtc_last_flush));
-	print_string(PRINT_ANY, "last_rand",
-		     "last_rand %s ",
-		     ntable_strtime_delta(ndtc->ndtc_last_rand));
-
-	print_nl();
-
-	print_uint(PRINT_ANY, "hash_rnd",
-		   "        hash_rnd %u ", ndtc->ndtc_hash_rnd);
-	print_0xhex(PRINT_ANY, "hash_mask",
-		    "hash_mask %08llx ", ndtc->ndtc_hash_mask);
-
-	print_uint(PRINT_ANY, "hash_chain_gc",
-		   "hash_chain_gc %u ", ndtc->ndtc_hash_chain_gc);
-	print_uint(PRINT_ANY, "proxy_qlen",
-		   "proxy_qlen %u ", ndtc->ndtc_proxy_qlen);
-
-	print_nl();
-}
-
-static void print_ndtparams(struct rtattr *tpb[])
-{
-
-	if (tpb[NDTPA_IFINDEX]) {
-		__u32 ifindex = rta_getattr_u32(tpb[NDTPA_IFINDEX]);
-
-		print_string(PRINT_FP, NULL, "    dev ", NULL);
-		print_color_string(PRINT_ANY, COLOR_IFNAME,
-				   "dev", "%s ", ll_index_to_name(ifindex));
-		print_nl();
-	}
-
-	print_string(PRINT_FP, NULL, "    ", NULL);
-	if (tpb[NDTPA_REFCNT]) {
-		__u32 refcnt = rta_getattr_u32(tpb[NDTPA_REFCNT]);
-
-		print_uint(PRINT_ANY, "refcnt", "refcnt %u ", refcnt);
-	}
-
-	if (tpb[NDTPA_REACHABLE_TIME]) {
-		__u64 reachable = rta_getattr_u64(tpb[NDTPA_REACHABLE_TIME]);
-
-		print_u64(PRINT_ANY, "reachable",
-			   "reachable %llu ", reachable);
-	}
-
-	if (tpb[NDTPA_BASE_REACHABLE_TIME]) {
-		__u64 breachable
-			= rta_getattr_u64(tpb[NDTPA_BASE_REACHABLE_TIME]);
-
-		print_u64(PRINT_ANY, "base_reachable",
-			   "base_reachable %llu ", breachable);
-	}
-
-	if (tpb[NDTPA_RETRANS_TIME]) {
-		__u64 retrans = rta_getattr_u64(tpb[NDTPA_RETRANS_TIME]);
-
-		print_u64(PRINT_ANY, "retrans", "retrans %llu ", retrans);
-	}
-
-	print_string(PRINT_FP, NULL, "%s    ", _SL_);
-
-	if (tpb[NDTPA_GC_STALETIME]) {
-		__u64 gc_stale = rta_getattr_u64(tpb[NDTPA_GC_STALETIME]);
-
-		print_u64(PRINT_ANY, "gc_stale", "gc_stale %llu ", gc_stale);
-	}
-
-	if (tpb[NDTPA_DELAY_PROBE_TIME]) {
-		__u64 delay_probe
-			= rta_getattr_u64(tpb[NDTPA_DELAY_PROBE_TIME]);
-
-		print_u64(PRINT_ANY, "delay_probe",
-			   "delay_probe %llu ", delay_probe);
-	}
-
-	if (tpb[NDTPA_QUEUE_LEN]) {
-		__u32 queue = rta_getattr_u32(tpb[NDTPA_QUEUE_LEN]);
-
-		print_uint(PRINT_ANY, "queue", "queue %u ", queue);
-	}
-
-	print_string(PRINT_FP, NULL, "%s    ", _SL_);
-
-	if (tpb[NDTPA_APP_PROBES]) {
-		__u32 aprobe = rta_getattr_u32(tpb[NDTPA_APP_PROBES]);
-
-		print_uint(PRINT_ANY, "app_probes", "app_probes %u ", aprobe);
-	}
-
-	if (tpb[NDTPA_UCAST_PROBES]) {
-		__u32 uprobe = rta_getattr_u32(tpb[NDTPA_UCAST_PROBES]);
-
-		print_uint(PRINT_ANY, "ucast_probes",
-			   "ucast_probes %u ", uprobe);
-	}
-
-	if (tpb[NDTPA_MCAST_PROBES]) {
-		__u32 mprobe = rta_getattr_u32(tpb[NDTPA_MCAST_PROBES]);
-
-		print_uint(PRINT_ANY, "mcast_probes",
-			   "mcast_probes %u ", mprobe);
-	}
-
-	print_string(PRINT_FP, NULL, "%s    ", _SL_);
-
-	if (tpb[NDTPA_ANYCAST_DELAY]) {
-		__u64 anycast_delay = rta_getattr_u64(tpb[NDTPA_ANYCAST_DELAY]);
-
-		print_u64(PRINT_ANY, "anycast_delay",
-			   "anycast_delay %llu ", anycast_delay);
-	}
-
-	if (tpb[NDTPA_PROXY_DELAY]) {
-		__u64 proxy_delay = rta_getattr_u64(tpb[NDTPA_PROXY_DELAY]);
-
-		print_u64(PRINT_ANY, "proxy_delay",
-			   "proxy_delay %llu ", proxy_delay);
-	}
-
-	if (tpb[NDTPA_PROXY_QLEN]) {
-		__u32 pqueue = rta_getattr_u32(tpb[NDTPA_PROXY_QLEN]);
-
-		print_uint(PRINT_ANY, "proxy_queue", "proxy_queue %u ", pqueue);
-	}
-
-	if (tpb[NDTPA_LOCKTIME]) {
-		__u64 locktime = rta_getattr_u64(tpb[NDTPA_LOCKTIME]);
-
-		print_u64(PRINT_ANY, "locktime", "locktime %llu ", locktime);
-	}
-
-	print_nl();
-}
-
-static void print_ndtstats(const struct ndt_stats *ndts)
-{
-
-	print_string(PRINT_FP, NULL, "    stats ", NULL);
-
-	print_u64(PRINT_ANY, "allocs", "allocs %llu ", ndts->ndts_allocs);
-	print_u64(PRINT_ANY, "destroys", "destroys %llu ",
-		   ndts->ndts_destroys);
-	print_u64(PRINT_ANY, "hash_grows", "hash_grows %llu ",
-		   ndts->ndts_hash_grows);
-
-	print_string(PRINT_FP, NULL, "%s    ", _SL_);
-
-	print_u64(PRINT_ANY, "res_failed", "res_failed %llu ",
-		   ndts->ndts_res_failed);
-	print_u64(PRINT_ANY, "lookups", "lookups %llu ", ndts->ndts_lookups);
-	print_u64(PRINT_ANY, "hits", "hits %llu ", ndts->ndts_hits);
-
-	print_string(PRINT_FP, NULL, "%s    ", _SL_);
-
-	print_u64(PRINT_ANY, "rcv_probes_mcast", "rcv_probes_mcast %llu ",
-		   ndts->ndts_rcv_probes_mcast);
-	print_u64(PRINT_ANY, "rcv_probes_ucast", "rcv_probes_ucast %llu ",
-		   ndts->ndts_rcv_probes_ucast);
-
-	print_string(PRINT_FP, NULL, "%s    ", _SL_);
-
-	print_u64(PRINT_ANY, "periodic_gc_runs", "periodic_gc_runs %llu ",
-		   ndts->ndts_periodic_gc_runs);
-	print_u64(PRINT_ANY, "forced_gc_runs", "forced_gc_runs %llu ",
-		   ndts->ndts_forced_gc_runs);
-
-	print_nl();
-}
-
-static int print_ntable(struct nlmsghdr *n, void *arg)
-{
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 	struct ndtmsg *ndtm = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr *tb[NDTA_MAX+1];
@@ -549,10 +378,9 @@
 	if (tb[NDTA_NAME]) {
 		const char *name = rta_getattr_str(tb[NDTA_NAME]);
 
-		if (filter.name && strcmp(filter.name, name))
+		if (strlen(filter.name) > 0 && strcmp(filter.name, name))
 			return 0;
 	}
-
 	if (tb[NDTA_PARMS]) {
 		parse_rtattr(tpb, NDTPA_MAX, RTA_DATA(tb[NDTA_PARMS]),
 			     RTA_PAYLOAD(tb[NDTA_PARMS]));
@@ -568,63 +396,208 @@
 		}
 	}
 
-	open_json_object(NULL);
-	print_string(PRINT_ANY, "family",
-		     "%s ", family_name(ndtm->ndtm_family));
+	if (ndtm->ndtm_family == AF_INET)
+		fprintf(fp, "inet ");
+	else if (ndtm->ndtm_family == AF_INET6)
+		fprintf(fp, "inet6 ");
+	else if (ndtm->ndtm_family == AF_DECnet)
+		fprintf(fp, "dnet ");
+	else
+		fprintf(fp, "(%d) ", ndtm->ndtm_family);
 
 	if (tb[NDTA_NAME]) {
 		const char *name = rta_getattr_str(tb[NDTA_NAME]);
-
-		print_string(PRINT_ANY, "name", "%s ", name);
+		fprintf(fp, "%s ", name);
 	}
 
-	print_nl();
+	fprintf(fp, "%s", _SL_);
 
 	ret = (tb[NDTA_THRESH1] || tb[NDTA_THRESH2] || tb[NDTA_THRESH3] ||
 	       tb[NDTA_GC_INTERVAL]);
 	if (ret)
-		print_string(PRINT_FP, NULL, "    ", NULL);
+		fprintf(fp, "    ");
 
 	if (tb[NDTA_THRESH1]) {
 		__u32 thresh1 = rta_getattr_u32(tb[NDTA_THRESH1]);
-
-		print_uint(PRINT_ANY, "thresh1", "thresh1 %u ", thresh1);
+		fprintf(fp, "thresh1 %u ", thresh1);
 	}
-
 	if (tb[NDTA_THRESH2]) {
 		__u32 thresh2 = rta_getattr_u32(tb[NDTA_THRESH2]);
-
-		print_uint(PRINT_ANY, "thresh2", "thresh2 %u ", thresh2);
+		fprintf(fp, "thresh2 %u ", thresh2);
 	}
-
 	if (tb[NDTA_THRESH3]) {
 		__u32 thresh3 = rta_getattr_u32(tb[NDTA_THRESH3]);
-
-		print_uint(PRINT_ANY, "thresh3", "thresh3 %u ", thresh3);
+		fprintf(fp, "thresh3 %u ", thresh3);
 	}
-
 	if (tb[NDTA_GC_INTERVAL]) {
-		__u64 gc_int = rta_getattr_u64(tb[NDTA_GC_INTERVAL]);
-
-		print_u64(PRINT_ANY, "gc_interval", "gc_int %llu ", gc_int);
+		unsigned long long gc_int = rta_getattr_u64(tb[NDTA_GC_INTERVAL]);
+		fprintf(fp, "gc_int %llu ", gc_int);
 	}
 
 	if (ret)
-		print_nl();
+		fprintf(fp, "%s", _SL_);
 
-	if (tb[NDTA_CONFIG] && show_stats)
-		print_ndtconfig(RTA_DATA(tb[NDTA_CONFIG]));
+	if (tb[NDTA_CONFIG] && show_stats) {
+		struct ndt_config *ndtc = RTA_DATA(tb[NDTA_CONFIG]);
 
-	if (tb[NDTA_PARMS])
-		print_ndtparams(tpb);
+		fprintf(fp, "    ");
+		fprintf(fp, "config ");
 
-	if (tb[NDTA_STATS] && show_stats)
-		print_ndtstats(RTA_DATA(tb[NDTA_STATS]));
+		fprintf(fp, "key_len %u ", ndtc->ndtc_key_len);
+		fprintf(fp, "entry_size %u ", ndtc->ndtc_entry_size);
+		fprintf(fp, "entries %u ", ndtc->ndtc_entries);
 
-	print_string(PRINT_FP, NULL, "\n", "");
-	close_json_object();
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "        ");
+
+		fprintf(fp, "last_flush %s ",
+			ntable_strtime_delta(ndtc->ndtc_last_flush));
+		fprintf(fp, "last_rand %s ",
+			ntable_strtime_delta(ndtc->ndtc_last_rand));
+
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "        ");
+
+		fprintf(fp, "hash_rnd %u ", ndtc->ndtc_hash_rnd);
+		fprintf(fp, "hash_mask %08x ", ndtc->ndtc_hash_mask);
+
+		fprintf(fp, "hash_chain_gc %u ", ndtc->ndtc_hash_chain_gc);
+		fprintf(fp, "proxy_qlen %u ", ndtc->ndtc_proxy_qlen);
+
+		fprintf(fp, "%s", _SL_);
+	}
+
+	if (tb[NDTA_PARMS]) {
+		if (tpb[NDTPA_IFINDEX]) {
+			__u32 ifindex = rta_getattr_u32(tpb[NDTPA_IFINDEX]);
+
+			fprintf(fp, "    ");
+			fprintf(fp, "dev %s ", ll_index_to_name(ifindex));
+			fprintf(fp, "%s", _SL_);
+		}
+
+		fprintf(fp, "    ");
+
+		if (tpb[NDTPA_REFCNT]) {
+			__u32 refcnt = rta_getattr_u32(tpb[NDTPA_REFCNT]);
+			fprintf(fp, "refcnt %u ", refcnt);
+		}
+		if (tpb[NDTPA_REACHABLE_TIME]) {
+			unsigned long long reachable = rta_getattr_u64(tpb[NDTPA_REACHABLE_TIME]);
+			fprintf(fp, "reachable %llu ", reachable);
+		}
+		if (tpb[NDTPA_BASE_REACHABLE_TIME]) {
+			unsigned long long breachable = rta_getattr_u64(tpb[NDTPA_BASE_REACHABLE_TIME]);
+			fprintf(fp, "base_reachable %llu ", breachable);
+		}
+		if (tpb[NDTPA_RETRANS_TIME]) {
+			unsigned long long retrans = rta_getattr_u64(tpb[NDTPA_RETRANS_TIME]);
+			fprintf(fp, "retrans %llu ", retrans);
+		}
+
+		fprintf(fp, "%s", _SL_);
+
+		fprintf(fp, "    ");
+
+		if (tpb[NDTPA_GC_STALETIME]) {
+			unsigned long long gc_stale = rta_getattr_u64(tpb[NDTPA_GC_STALETIME]);
+			fprintf(fp, "gc_stale %llu ", gc_stale);
+		}
+		if (tpb[NDTPA_DELAY_PROBE_TIME]) {
+			unsigned long long delay_probe = rta_getattr_u64(tpb[NDTPA_DELAY_PROBE_TIME]);
+			fprintf(fp, "delay_probe %llu ", delay_probe);
+		}
+		if (tpb[NDTPA_QUEUE_LEN]) {
+			__u32 queue = rta_getattr_u32(tpb[NDTPA_QUEUE_LEN]);
+			fprintf(fp, "queue %u ", queue);
+		}
+
+		fprintf(fp, "%s", _SL_);
+
+		fprintf(fp, "    ");
+
+		if (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 = rta_getattr_u32(tpb[NDTPA_UCAST_PROBES]);
+			fprintf(fp, "ucast_probes %u ", uprobe);
+		}
+		if (tpb[NDTPA_MCAST_PROBES]) {
+			__u32 mprobe = rta_getattr_u32(tpb[NDTPA_MCAST_PROBES]);
+			fprintf(fp, "mcast_probes %u ", mprobe);
+		}
+
+		fprintf(fp, "%s", _SL_);
+
+		fprintf(fp, "    ");
+
+		if (tpb[NDTPA_ANYCAST_DELAY]) {
+			unsigned long long anycast_delay = rta_getattr_u64(tpb[NDTPA_ANYCAST_DELAY]);
+			fprintf(fp, "anycast_delay %llu ", anycast_delay);
+		}
+		if (tpb[NDTPA_PROXY_DELAY]) {
+			unsigned long long proxy_delay = rta_getattr_u64(tpb[NDTPA_PROXY_DELAY]);
+			fprintf(fp, "proxy_delay %llu ", proxy_delay);
+		}
+		if (tpb[NDTPA_PROXY_QLEN]) {
+			__u32 pqueue = rta_getattr_u32(tpb[NDTPA_PROXY_QLEN]);
+			fprintf(fp, "proxy_queue %u ", pqueue);
+		}
+		if (tpb[NDTPA_LOCKTIME]) {
+			unsigned long long locktime = rta_getattr_u64(tpb[NDTPA_LOCKTIME]);
+			fprintf(fp, "locktime %llu ", locktime);
+		}
+
+		fprintf(fp, "%s", _SL_);
+	}
+
+	if (tb[NDTA_STATS] && show_stats) {
+		struct ndt_stats *ndts = RTA_DATA(tb[NDTA_STATS]);
+
+		fprintf(fp, "    ");
+		fprintf(fp, "stats ");
+
+		fprintf(fp, "allocs %llu ",
+			(unsigned long long) ndts->ndts_allocs);
+		fprintf(fp, "destroys %llu ",
+			(unsigned long long) ndts->ndts_destroys);
+		fprintf(fp, "hash_grows %llu ",
+			(unsigned long long) ndts->ndts_hash_grows);
+
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "        ");
+
+		fprintf(fp, "res_failed %llu ",
+			(unsigned long long) ndts->ndts_res_failed);
+		fprintf(fp, "lookups %llu ",
+			(unsigned long long) ndts->ndts_lookups);
+		fprintf(fp, "hits %llu ",
+			(unsigned long long) ndts->ndts_hits);
+
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "        ");
+
+		fprintf(fp, "rcv_probes_mcast %llu ",
+			(unsigned long long) ndts->ndts_rcv_probes_mcast);
+		fprintf(fp, "rcv_probes_ucast %llu ",
+			(unsigned long long) ndts->ndts_rcv_probes_ucast);
+
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "        ");
+
+		fprintf(fp, "periodic_gc_runs %llu ",
+			(unsigned long long) ndts->ndts_periodic_gc_runs);
+		fprintf(fp, "forced_gc_runs %llu ",
+			(unsigned long long) ndts->ndts_forced_gc_runs);
+
+		fprintf(fp, "%s", _SL_);
+	}
+
+	fprintf(fp, "\n");
+
 	fflush(fp);
-
 	return 0;
 }
 
@@ -650,24 +623,22 @@
 		} else if (strcmp(*argv, "name") == 0) {
 			NEXT_ARG();
 
-			filter.name = *argv;
+			strncpy(filter.name, *argv, sizeof(filter.name));
 		} else
 			invarg("unknown", *argv);
 
 		argc--; argv++;
 	}
 
-	if (rtnl_neightbldump_req(&rth, preferred_family) < 0) {
+	if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETNEIGHTBL) < 0) {
 		perror("Cannot send dump request");
 		exit(1);
 	}
 
-	new_json_obj(json);
 	if (rtnl_dump_filter(&rth, print_ntable, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		exit(1);
 	}
-	delete_json_obj();
 
 	return 0;
 }
diff --git a/ip/ipprefix.c b/ip/ipprefix.c
index 466af20..ee51f04 100644
--- a/ip/ipprefix.c
+++ b/ip/ipprefix.c
@@ -35,12 +35,12 @@
 #define IF_PREFIX_ONLINK	0x01
 #define IF_PREFIX_AUTOCONF	0x02
 
-int print_prefix(struct nlmsghdr *n, void *arg)
+int print_prefix(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 	struct prefixmsg *prefix = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
-	struct rtattr *tb[RTA_MAX+1];
+	struct rtattr * tb[RTA_MAX+1];
 	int family = preferred_family;
 
 	if (n->nlmsg_type != RTM_NEWPREFIX) {
@@ -71,11 +71,22 @@
 
 	parse_rtattr(tb, RTA_MAX, RTM_RTA(prefix), len);
 
+	fprintf(fp, "prefix ");
+
 	if (tb[PREFIX_ADDRESS]) {
-		fprintf(fp, "prefix %s/%u",
-		        rt_addr_n2a_rta(family, tb[PREFIX_ADDRESS]),
-			prefix->prefix_len);
+		struct in6_addr *pfx;
+		char abuf[256];
+
+		pfx = (struct in6_addr *)RTA_DATA(tb[PREFIX_ADDRESS]);
+
+		memset(abuf, '\0', sizeof(abuf));
+		fprintf(fp, "%s", rt_addr_n2a(family,
+					      RTA_PAYLOAD(tb[PREFIX_ADDRESS]),
+					      pfx,
+					      abuf, sizeof(abuf)));
 	}
+	fprintf(fp, "/%u ", prefix->prefix_len);
+
 	fprintf(fp, "dev %s ", ll_index_to_name(prefix->prefix_ifindex));
 
 	if (prefix->prefix_flags & IF_PREFIX_ONLINK)
@@ -84,8 +95,8 @@
 		fprintf(fp, "autoconf ");
 
 	if (tb[PREFIX_CACHEINFO]) {
-		const struct prefix_cacheinfo *pc
-			 = RTA_DATA(tb[PREFIX_CACHEINFO]);
+		struct prefix_cacheinfo *pc;
+		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 32bb52d..7f81145 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <string.h>
 #include <time.h>
@@ -28,6 +29,7 @@
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
+#include "iproute_lwtunnel.h"
 
 #ifndef RTAX_RTTVAR
 #define RTAX_RTTVAR RTAX_HOPS
@@ -39,73 +41,64 @@
 	IPROUTE_SAVE,
 };
 static const char *mx_names[RTAX_MAX+1] = {
-	[RTAX_MTU]			= "mtu",
-	[RTAX_WINDOW]			= "window",
-	[RTAX_RTT]			= "rtt",
-	[RTAX_RTTVAR]			= "rttvar",
-	[RTAX_SSTHRESH]			= "ssthresh",
-	[RTAX_CWND]			= "cwnd",
-	[RTAX_ADVMSS]			= "advmss",
-	[RTAX_REORDERING]		= "reordering",
-	[RTAX_HOPLIMIT]			= "hoplimit",
-	[RTAX_INITCWND]			= "initcwnd",
-	[RTAX_FEATURES]			= "features",
-	[RTAX_RTO_MIN]			= "rto_min",
-	[RTAX_INITRWND]			= "initrwnd",
-	[RTAX_QUICKACK]			= "quickack",
-	[RTAX_CC_ALGO]			= "congctl",
-	[RTAX_FASTOPEN_NO_COOKIE]	= "fastopen_no_cookie"
+	[RTAX_MTU]	= "mtu",
+	[RTAX_WINDOW]	= "window",
+	[RTAX_RTT]	= "rtt",
+	[RTAX_RTTVAR]	= "rttvar",
+	[RTAX_SSTHRESH] = "ssthresh",
+	[RTAX_CWND]	= "cwnd",
+	[RTAX_ADVMSS]	= "advmss",
+	[RTAX_REORDERING]="reordering",
+	[RTAX_HOPLIMIT] = "hoplimit",
+	[RTAX_INITCWND] = "initcwnd",
+	[RTAX_FEATURES] = "features",
+	[RTAX_RTO_MIN]	= "rto_min",
+	[RTAX_INITRWND]	= "initrwnd",
+	[RTAX_QUICKACK]	= "quickack",
+	[RTAX_CC_ALGO]	= "congctl",
 };
 static void usage(void) __attribute__((noreturn));
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: ip route { list | flush } SELECTOR\n"
-		"       ip route save SELECTOR\n"
-		"       ip route restore\n"
-		"       ip route showdump\n"
-		"       ip route get [ ROUTE_GET_FLAGS ] ADDRESS\n"
-		"                            [ from ADDRESS iif STRING ]\n"
-		"                            [ oif STRING ] [ tos TOS ]\n"
-		"                            [ mark NUMBER ] [ vrf NAME ]\n"
-		"                            [ uid NUMBER ] [ ipproto PROTOCOL ]\n"
-		"                            [ sport NUMBER ] [ dport NUMBER ]\n"
-		"       ip route { add | del | change | append | replace } ROUTE\n"
-		"SELECTOR := [ root PREFIX ] [ match PREFIX ] [ exact PREFIX ]\n"
-		"            [ table TABLE_ID ] [ vrf NAME ] [ proto RTPROTO ]\n"
-		"            [ type TYPE ] [ scope SCOPE ]\n"
-		"ROUTE := NODE_SPEC [ INFO_SPEC ]\n"
-		"NODE_SPEC := [ TYPE ] PREFIX [ tos TOS ]\n"
-		"             [ table TABLE_ID ] [ proto RTPROTO ]\n"
-		"             [ scope SCOPE ] [ metric METRIC ]\n"
-		"             [ ttl-propagate { enabled | disabled } ]\n"
-		"INFO_SPEC := { NH | nhid ID } OPTIONS FLAGS [ nexthop NH ]...\n"
-		"NH := [ encap ENCAPTYPE ENCAPHDR ] [ via [ FAMILY ] ADDRESS ]\n"
-		"	    [ dev STRING ] [ weight NUMBER ] NHFLAGS\n"
-		"FAMILY := [ inet | inet6 | mpls | bridge | link ]\n"
-		"OPTIONS := FLAGS [ mtu NUMBER ] [ advmss NUMBER ] [ as [ to ] ADDRESS ]\n"
-		"           [ rtt TIME ] [ rttvar TIME ] [ reordering NUMBER ]\n"
-		"           [ window NUMBER ] [ cwnd NUMBER ] [ initcwnd NUMBER ]\n"
-		"           [ ssthresh NUMBER ] [ realms REALM ] [ src ADDRESS ]\n"
-		"           [ rto_min TIME ] [ hoplimit NUMBER ] [ initrwnd NUMBER ]\n"
-		"           [ features FEATURES ] [ quickack BOOL ] [ congctl NAME ]\n"
-		"           [ pref PREF ] [ expires TIME ] [ fastopen_no_cookie BOOL ]\n"
-		"TYPE := { unicast | local | broadcast | multicast | throw |\n"
-		"          unreachable | prohibit | blackhole | nat }\n"
-		"TABLE_ID := [ local | main | default | all | NUMBER ]\n"
-		"SCOPE := [ host | link | global | NUMBER ]\n"
-		"NHFLAGS := [ onlink | pervasive ]\n"
-		"RTPROTO := [ kernel | boot | static | NUMBER ]\n"
-		"PREF := [ low | medium | high ]\n"
-		"TIME := NUMBER[s|ms]\n"
-		"BOOL := [1|0]\n"
-		"FEATURES := ecn\n"
-		"ENCAPTYPE := [ mpls | ip | ip6 | seg6 | seg6local ]\n"
-		"ENCAPHDR := [ MPLSLABEL | SEG6HDR ]\n"
-		"SEG6HDR := [ mode SEGMODE ] segs ADDR1,ADDRi,ADDRn [hmac HMACKEYID] [cleanup]\n"
-		"SEGMODE := [ encap | inline ]\n"
-		"ROUTE_GET_FLAGS := [ fibmatch ]\n");
+	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 showdump\n");
+	fprintf(stderr, "       ip route get ADDRESS [ from ADDRESS iif STRING ]\n");
+	fprintf(stderr, "                            [ oif STRING ] [ tos TOS ]\n");
+	fprintf(stderr, "                            [ mark NUMBER ] [ uid 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");
+	fprintf(stderr, "ROUTE := NODE_SPEC [ INFO_SPEC ]\n");
+	fprintf(stderr, "NODE_SPEC := [ TYPE ] PREFIX [ tos TOS ]\n");
+	fprintf(stderr, "             [ table TABLE_ID ] [ proto RTPROTO ]\n");
+	fprintf(stderr, "             [ scope SCOPE ] [ metric METRIC ]\n");
+	fprintf(stderr, "INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ]...\n");
+	fprintf(stderr, "NH := [ encap ENCAPTYPE ENCAPHDR ] [ via [ FAMILY ] ADDRESS ]\n");
+	fprintf(stderr, "	    [ dev STRING ] [ weight NUMBER ] NHFLAGS\n");
+	fprintf(stderr, "FAMILY := [ inet | inet6 | ipx | dnet | mpls | bridge | link ]\n");
+	fprintf(stderr, "OPTIONS := FLAGS [ mtu NUMBER ] [ advmss NUMBER ] [ as [ to ] ADDRESS ]\n");
+	fprintf(stderr, "           [ rtt TIME ] [ rttvar TIME ] [ reordering NUMBER ]\n");
+	fprintf(stderr, "           [ window NUMBER] [ cwnd NUMBER ] [ initcwnd NUMBER ]\n");
+	fprintf(stderr, "           [ ssthresh NUMBER ] [ realms REALM ] [ src ADDRESS ]\n");
+	fprintf(stderr, "           [ rto_min TIME ] [ hoplimit NUMBER ] [ initrwnd NUMBER ]\n");
+	fprintf(stderr, "           [ features FEATURES ] [ quickack BOOL ] [ congctl NAME ]\n");
+	fprintf(stderr, "           [ pref PREF ] [ expires TIME ]\n");
+	fprintf(stderr, "TYPE := [ unicast | local | broadcast | multicast | throw |\n");
+	fprintf(stderr, "          unreachable | prohibit | blackhole | nat ]\n");
+	fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n");
+	fprintf(stderr, "SCOPE := [ host | link | global | NUMBER ]\n");
+	fprintf(stderr, "NHFLAGS := [ onlink | pervasive ]\n");
+	fprintf(stderr, "RTPROTO := [ kernel | boot | static | NUMBER ]\n");
+	fprintf(stderr, "PREF := [ low | medium | high ]\n");
+	fprintf(stderr, "TIME := NUMBER[s|ms]\n");
+	fprintf(stderr, "BOOL := [1|0]\n");
+	fprintf(stderr, "FEATURES := ecn\n");
+	fprintf(stderr, "ENCAPTYPE := [ mpls | ip | ip6 ]\n");
+	fprintf(stderr, "ENCAPHDR := [ MPLSLABEL ]\n");
 	exit(-1);
 }
 
@@ -120,13 +113,12 @@
 	int flushe;
 	int protocol, protocolmask;
 	int scope, scopemask;
-	__u64 typemask;
+	int type, typemask;
 	int tos, tosmask;
 	int iif, iifmask;
 	int oif, oifmask;
 	int mark, markmask;
 	int realm, realmmask;
-	__u32 metric, metricmask;
 	inet_prefix rprefsrc;
 	inet_prefix rvia;
 	inet_prefix rdst;
@@ -139,7 +131,7 @@
 {
 	if (rtnl_send_check(&rth, filter.flushb, filter.flushp) < 0) {
 		perror("Failed to send flush request");
-		return -2;
+		return -1;
 	}
 	filter.flushp = 0;
 	return 0;
@@ -148,10 +140,10 @@
 static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len)
 {
 	struct rtmsg *r = NLMSG_DATA(n);
-	inet_prefix dst = { .family = r->rtm_family };
-	inet_prefix src = { .family = r->rtm_family };
-	inet_prefix via = { .family = r->rtm_family };
-	inet_prefix prefsrc = { .family = r->rtm_family };
+	inet_prefix dst;
+	inet_prefix src;
+	inet_prefix via;
+	inet_prefix prefsrc;
 	__u32 table;
 	static int ip6_multiple_tables;
 
@@ -163,7 +155,7 @@
 	if (r->rtm_family == AF_INET6 && table != RT_TABLE_MAIN)
 		ip6_multiple_tables = 1;
 
-	if (filter.cloned == !(r->rtm_flags & RTM_F_CLONED))
+	if (filter.cloned == !(r->rtm_flags&RTM_F_CLONED))
 		return 0;
 
 	if (r->rtm_family == AF_INET6 && !ip6_multiple_tables) {
@@ -186,53 +178,28 @@
 		return 0;
 	if ((filter.scope^r->rtm_scope)&filter.scopemask)
 		return 0;
-
-	if (filter.typemask && !(filter.typemask & (1 << r->rtm_type)))
+	if ((filter.type^r->rtm_type)&filter.typemask)
 		return 0;
 	if ((filter.tos^r->rtm_tos)&filter.tosmask)
 		return 0;
-	if (filter.rdst.family) {
-		if (r->rtm_family != filter.rdst.family ||
-		    filter.rdst.bitlen > r->rtm_dst_len)
-			return 0;
-	} else if (filter.rdst.flags & PREFIXLEN_SPECIFIED) {
-		if (filter.rdst.bitlen > r->rtm_dst_len)
-			return 0;
-	}
-	if (filter.mdst.family) {
-		if (r->rtm_family != filter.mdst.family ||
-		    (filter.mdst.bitlen >= 0 &&
-		     filter.mdst.bitlen < r->rtm_dst_len))
-			return 0;
-	} else if (filter.mdst.flags & PREFIXLEN_SPECIFIED) {
-		if (filter.mdst.bitlen >= 0 &&
-		    filter.mdst.bitlen < r->rtm_dst_len)
-			return 0;
-	}
-	if (filter.rsrc.family) {
-		if (r->rtm_family != filter.rsrc.family ||
-		    filter.rsrc.bitlen > r->rtm_src_len)
-			return 0;
-	} else if (filter.rsrc.flags & PREFIXLEN_SPECIFIED) {
-		if (filter.rsrc.bitlen > r->rtm_src_len)
-			return 0;
-	}
-	if (filter.msrc.family) {
-		if (r->rtm_family != filter.msrc.family ||
-		    (filter.msrc.bitlen >= 0 &&
-		     filter.msrc.bitlen < r->rtm_src_len))
-			return 0;
-	} else if (filter.msrc.flags & PREFIXLEN_SPECIFIED) {
-		if (filter.msrc.bitlen >= 0 &&
-		    filter.msrc.bitlen < r->rtm_src_len)
-			return 0;
-	}
+	if (filter.rdst.family &&
+	    (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len))
+		return 0;
+	if (filter.mdst.family &&
+	    (r->rtm_family != filter.mdst.family ||
+	     (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len)))
+		return 0;
+	if (filter.rsrc.family &&
+	    (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len))
+		return 0;
+	if (filter.msrc.family &&
+	    (r->rtm_family != filter.msrc.family ||
+	     (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len)))
+		return 0;
 	if (filter.rvia.family) {
 		int family = r->rtm_family;
-
 		if (tb[RTA_VIA]) {
 			struct rtvia *via = RTA_DATA(tb[RTA_VIA]);
-
 			family = via->rtvia_family;
 		}
 		if (family != filter.rvia.family)
@@ -241,42 +208,44 @@
 	if (filter.rprefsrc.family && r->rtm_family != filter.rprefsrc.family)
 		return 0;
 
+	memset(&dst, 0, sizeof(dst));
+	dst.family = r->rtm_family;
 	if (tb[RTA_DST])
 		memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), (r->rtm_dst_len+7)/8);
-	if (filter.rsrc.family || filter.msrc.family ||
-	    filter.rsrc.flags & PREFIXLEN_SPECIFIED ||
-	    filter.msrc.flags & PREFIXLEN_SPECIFIED) {
+	if (filter.rsrc.family || filter.msrc.family) {
+		memset(&src, 0, sizeof(src));
+		src.family = r->rtm_family;
 		if (tb[RTA_SRC])
 			memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), (r->rtm_src_len+7)/8);
 	}
-	if (filter.rvia.bitlen > 0) {
+	if (filter.rvia.bitlen>0) {
+		memset(&via, 0, sizeof(via));
+		via.family = r->rtm_family;
 		if (tb[RTA_GATEWAY])
 			memcpy(&via.data, RTA_DATA(tb[RTA_GATEWAY]), host_len/8);
 		if (tb[RTA_VIA]) {
 			size_t len = RTA_PAYLOAD(tb[RTA_VIA]) - 2;
 			struct rtvia *rtvia = RTA_DATA(tb[RTA_VIA]);
-
 			via.family = rtvia->rtvia_family;
 			memcpy(&via.data, rtvia->rtvia_addr, len);
 		}
 	}
-	if (filter.rprefsrc.bitlen > 0) {
+	if (filter.rprefsrc.bitlen>0) {
+		memset(&prefsrc, 0, sizeof(prefsrc));
+		prefsrc.family = r->rtm_family;
 		if (tb[RTA_PREFSRC])
 			memcpy(&prefsrc.data, RTA_DATA(tb[RTA_PREFSRC]), host_len/8);
 	}
 
-	if ((filter.rdst.family || filter.rdst.flags & PREFIXLEN_SPECIFIED) &&
-	    inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen))
+	if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen))
 		return 0;
-	if ((filter.mdst.family || filter.mdst.flags & PREFIXLEN_SPECIFIED) &&
+	if (filter.mdst.family && filter.mdst.bitlen >= 0 &&
 	    inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len))
 		return 0;
 
-	if ((filter.rsrc.family || filter.rsrc.flags & PREFIXLEN_SPECIFIED) &&
-	    inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen))
+	if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen))
 		return 0;
-	if ((filter.msrc.family || filter.msrc.flags & PREFIXLEN_SPECIFIED) &&
-	    filter.msrc.bitlen >= 0 &&
+	if (filter.msrc.family && filter.msrc.bitlen >= 0 &&
 	    inet_addr_match(&src, &filter.msrc, r->rtm_src_len))
 		return 0;
 
@@ -286,7 +255,6 @@
 		return 0;
 	if (filter.realmmask) {
 		__u32 realms = 0;
-
 		if (tb[RTA_FLOW])
 			realms = rta_getattr_u32(tb[RTA_FLOW]);
 		if ((realms^filter.realm)&filter.realmmask)
@@ -294,42 +262,31 @@
 	}
 	if (filter.iifmask) {
 		int iif = 0;
-
 		if (tb[RTA_IIF])
-			iif = rta_getattr_u32(tb[RTA_IIF]);
+			iif = *(int*)RTA_DATA(tb[RTA_IIF]);
 		if ((iif^filter.iif)&filter.iifmask)
 			return 0;
 	}
 	if (filter.oifmask) {
 		int oif = 0;
-
 		if (tb[RTA_OIF])
-			oif = rta_getattr_u32(tb[RTA_OIF]);
+			oif = *(int*)RTA_DATA(tb[RTA_OIF]);
 		if ((oif^filter.oif)&filter.oifmask)
 			return 0;
 	}
 	if (filter.markmask) {
 		int mark = 0;
-
 		if (tb[RTA_MARK])
-			mark = rta_getattr_u32(tb[RTA_MARK]);
+			mark = *(int *)RTA_DATA(tb[RTA_MARK]);
 		if ((mark ^ filter.mark) & filter.markmask)
 			return 0;
 	}
-	if (filter.metricmask) {
-		__u32 metric = 0;
-
-		if (tb[RTA_PRIORITY])
-			metric = rta_getattr_u32(tb[RTA_PRIORITY]);
-		if ((metric ^ filter.metric) & filter.metricmask)
-			return 0;
-	}
 	if (filter.flushb &&
 	    r->rtm_family == AF_INET6 &&
 	    r->rtm_dst_len == 0 &&
 	    r->rtm_type == RTN_UNREACHABLE &&
 	    tb[RTA_PRIORITY] &&
-	    rta_getattr_u32(tb[RTA_PRIORITY]) == -1)
+	    *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1)
 		return 0;
 
 	return 1;
@@ -340,399 +297,30 @@
 	unsigned int of = features;
 
 	if (features & RTAX_FEATURE_ECN) {
-		print_null(PRINT_ANY, "ecn", "ecn ", NULL);
+		fprintf(fp, " ecn");
 		features &= ~RTAX_FEATURE_ECN;
 	}
 
 	if (features)
-		print_0xhex(PRINT_ANY,
-			    "features", "%#llx ", of);
+		fprintf(fp, " 0x%x", of);
 }
 
-void print_rt_flags(FILE *fp, unsigned int flags)
+int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
-	open_json_array(PRINT_JSON,
-			is_json_context() ?  "flags" : "");
-
-	if (flags & RTNH_F_DEAD)
-		print_string(PRINT_ANY, NULL, "%s ", "dead");
-	if (flags & RTNH_F_ONLINK)
-		print_string(PRINT_ANY, NULL, "%s ", "onlink");
-	if (flags & RTNH_F_PERVASIVE)
-		print_string(PRINT_ANY, NULL, "%s ", "pervasive");
-	if (flags & RTNH_F_OFFLOAD)
-		print_string(PRINT_ANY, NULL, "%s ", "offload");
-	if (flags & RTM_F_NOTIFY)
-		print_string(PRINT_ANY, NULL, "%s ", "notify");
-	if (flags & RTNH_F_LINKDOWN)
-		print_string(PRINT_ANY, NULL, "%s ", "linkdown");
-	if (flags & RTNH_F_UNRESOLVED)
-		print_string(PRINT_ANY, NULL, "%s ", "unresolved");
-
-	close_json_array(PRINT_JSON, NULL);
-}
-
-static void print_rt_pref(FILE *fp, unsigned int pref)
-{
-
-	switch (pref) {
-	case ICMPV6_ROUTER_PREF_LOW:
-		print_string(PRINT_ANY,
-			     "pref", "pref %s", "low");
-		break;
-	case ICMPV6_ROUTER_PREF_MEDIUM:
-		print_string(PRINT_ANY,
-			     "pref", "pref %s", "medium");
-		break;
-	case ICMPV6_ROUTER_PREF_HIGH:
-		print_string(PRINT_ANY,
-			     "pref", "pref %s", "high");
-		break;
-	default:
-		print_uint(PRINT_ANY,
-			   "pref", "%u", pref);
-	}
-}
-
-void print_rta_if(FILE *fp, const struct rtattr *rta, const char *prefix)
-{
-	const char *ifname = ll_index_to_name(rta_getattr_u32(rta));
-
-	if (is_json_context())
-		print_string(PRINT_JSON, prefix, NULL, ifname);
-	else {
-		fprintf(fp, "%s ", prefix);
-		color_fprintf(fp, COLOR_IFNAME, "%s ", ifname);
-	}
-}
-
-static void print_cache_flags(FILE *fp, __u32 flags)
-{
-	json_writer_t *jw = get_json_writer();
-	flags &= ~0xFFFF;
-
-	if (jw) {
-		jsonw_name(jw, "cache");
-		jsonw_start_array(jw);
-	} else {
-		fprintf(fp, "%s    cache ", _SL_);
-		if (flags == 0)
-			return;
-		putc('<', fp);
-	}
-
-#define PRTFL(fl, flname)						\
-	if (flags & RTCF_##fl) {					\
-		flags &= ~RTCF_##fl;					\
-		if (jw)							\
-			jsonw_string(jw, flname);			\
-		else							\
-			fprintf(fp, "%s%s", flname, flags ? "," : "> "); \
-	}
-
-	PRTFL(LOCAL, "local");
-	PRTFL(REJECT, "reject");
-	PRTFL(MULTICAST, "mc");
-	PRTFL(BROADCAST, "brd");
-	PRTFL(DNAT, "dst-nat");
-	PRTFL(SNAT, "src-nat");
-	PRTFL(MASQ, "masq");
-	PRTFL(DIRECTDST, "dst-direct");
-	PRTFL(DIRECTSRC, "src-direct");
-	PRTFL(REDIRECTED, "redirected");
-	PRTFL(DOREDIRECT, "redirect");
-	PRTFL(FAST, "fastroute");
-	PRTFL(NOTIFY, "notify");
-	PRTFL(TPROXY, "proxy");
-#undef PRTFL
-
-	if (flags)
-		print_hex(PRINT_ANY, "flags", "%x>", flags);
-
-	if (jw)
-		jsonw_end_array(jw);
-}
-
-static void print_rta_cacheinfo(FILE *fp, const struct rta_cacheinfo *ci)
-{
-	static int hz;
-
-	if (!hz)
-		hz = get_user_hz();
-
-	if (ci->rta_expires != 0)
-		print_int(PRINT_ANY, "expires",
-			   "expires %dsec ", ci->rta_expires/hz);
-	if (ci->rta_error != 0)
-		print_uint(PRINT_ANY, "error",
-			   "error %u ", ci->rta_error);
-
-	if (show_stats) {
-		if (ci->rta_clntref)
-			print_uint(PRINT_ANY, "users",
-				   "users %u ", ci->rta_clntref);
-		if (ci->rta_used != 0)
-			print_uint(PRINT_ANY, "used",
-				   "used %u ", ci->rta_used);
-		if (ci->rta_lastuse != 0)
-			print_uint(PRINT_ANY, "age",
-				   "age %usec ", ci->rta_lastuse/hz);
-	}
-	if (ci->rta_id)
-		print_0xhex(PRINT_ANY, "ipid",
-			    "ipid 0x%04llx ", ci->rta_id);
-	if (ci->rta_ts || ci->rta_tsage) {
-		print_0xhex(PRINT_ANY, "ts",
-			    "ts 0x%llx", ci->rta_ts);
-		print_uint(PRINT_ANY, "tsage",
-			   "tsage %usec ", ci->rta_tsage);
-	}
-}
-
-static void print_rta_flow(FILE *fp, const struct rtattr *rta)
-{
-	__u32 to = rta_getattr_u32(rta);
-	__u32 from = to >> 16;
-	SPRINT_BUF(b1);
-
-	to &= 0xFFFF;
-	if (is_json_context()) {
-		open_json_object("flow");
-
-		if (from)
-			print_string(PRINT_JSON, "from", NULL,
-				     rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
-		print_string(PRINT_JSON, "to", NULL,
-			     rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
-		close_json_object();
-	} else {
-		fprintf(fp, "realm%s ", from ? "s" : "");
-
-		if (from)
-			print_string(PRINT_FP, NULL, "%s/",
-				     rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
-		print_string(PRINT_FP, NULL, "%s ",
-			     rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
-	}
-}
-
-static void print_rta_newdst(FILE *fp, const struct rtmsg *r,
-			     const struct rtattr *rta)
-{
-	const char *newdst = format_host_rta(r->rtm_family, rta);
-
-	if (is_json_context())
-		print_string(PRINT_JSON, "to", NULL, newdst);
-	else {
-		fprintf(fp, "as to ");
-		print_color_string(PRINT_FP,
-				   ifa_family_color(r->rtm_family),
-				   NULL, "%s ", newdst);
-	}
-}
-
-void print_rta_gateway(FILE *fp, unsigned char family, const struct rtattr *rta)
-{
-	const char *gateway = format_host_rta(family, rta);
-
-	if (is_json_context())
-		print_string(PRINT_JSON, "gateway", NULL, gateway);
-	else {
-		fprintf(fp, "via ");
-		print_color_string(PRINT_FP,
-				   ifa_family_color(family),
-				   NULL, "%s ", gateway);
-	}
-}
-
-static void print_rta_via(FILE *fp, const struct rtattr *rta)
-{
-	size_t len = RTA_PAYLOAD(rta) - 2;
-	const struct rtvia *via = RTA_DATA(rta);
-
-	if (is_json_context()) {
-		open_json_object("via");
-		print_string(PRINT_JSON, "family", NULL,
-			     family_name(via->rtvia_family));
-		print_string(PRINT_JSON, "host", NULL,
-			     format_host(via->rtvia_family, len,
-					 via->rtvia_addr));
-		close_json_object();
-	} else {
-		print_string(PRINT_FP, NULL, "via %s ",
-			     family_name(via->rtvia_family));
-		print_color_string(PRINT_FP,
-				   ifa_family_color(via->rtvia_family),
-				   NULL, "%s ",
-				   format_host(via->rtvia_family,
-					       len, via->rtvia_addr));
-	}
-}
-
-static void print_rta_metrics(FILE *fp, const struct rtattr *rta)
-{
-	struct rtattr *mxrta[RTAX_MAX+1];
-	unsigned int mxlock = 0;
-	int i;
-
-	open_json_array(PRINT_JSON, "metrics");
-	open_json_object(NULL);
-
-	parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta));
-
-	if (mxrta[RTAX_LOCK])
-		mxlock = rta_getattr_u32(mxrta[RTAX_LOCK]);
-
-	for (i = 2; i <= RTAX_MAX; i++) {
-		__u32 val = 0U;
-
-		if (mxrta[i] == NULL && !(mxlock & (1 << i)))
-			continue;
-
-		if (mxrta[i] != NULL && i != RTAX_CC_ALGO)
-			val = rta_getattr_u32(mxrta[i]);
-
-		if (i == RTAX_HOPLIMIT && (int)val == -1)
-			continue;
-
-		if (!is_json_context()) {
-			if (i < sizeof(mx_names)/sizeof(char *) && mx_names[i])
-				fprintf(fp, "%s ", mx_names[i]);
-			else
-				fprintf(fp, "metric %d ", i);
-
-			if (mxlock & (1<<i))
-				fprintf(fp, "lock ");
-		}
-
-		switch (i) {
-		case RTAX_FEATURES:
-			print_rtax_features(fp, val);
-			break;
-		default:
-			print_uint(PRINT_ANY, mx_names[i], "%u ", val);
-			break;
-
-		case RTAX_RTT:
-		case RTAX_RTTVAR:
-		case RTAX_RTO_MIN:
-			if (i == RTAX_RTT)
-				val /= 8;
-			else if (i == RTAX_RTTVAR)
-				val /= 4;
-
-			if (is_json_context())
-				print_uint(PRINT_JSON, mx_names[i],
-					   NULL, val);
-			else {
-				if (val >= 1000)
-					fprintf(fp, "%gs ", val/1e3);
-				else
-					fprintf(fp, "%ums ", val);
-			}
-			break;
-		case RTAX_CC_ALGO:
-			print_string(PRINT_ANY, "congestion",
-				     "%s ", rta_getattr_str(mxrta[i]));
-			break;
-		}
-	}
-
-	close_json_object();
-	close_json_array(PRINT_JSON, NULL);
-}
-
-static void print_rta_multipath(FILE *fp, const struct rtmsg *r,
-				struct rtattr *rta)
-{
-	const struct rtnexthop *nh = RTA_DATA(rta);
-	int len = RTA_PAYLOAD(rta);
-	int first = 1;
-
-	open_json_array(PRINT_JSON, "nexthops");
-
-	while (len >= sizeof(*nh)) {
-		struct rtattr *tb[RTA_MAX + 1];
-
-		if (nh->rtnh_len > len)
-			break;
-
-		open_json_object(NULL);
-
-		if ((r->rtm_flags & RTM_F_CLONED) &&
-		    r->rtm_type == RTN_MULTICAST) {
-			if (first) {
-				print_string(PRINT_FP, NULL, "Oifs: ", NULL);
-				first = 0;
-			} else {
-				print_string(PRINT_FP, NULL, " ", NULL);
-			}
-		} else
-			print_string(PRINT_FP, NULL, "%s\tnexthop ", _SL_);
-
-		if (nh->rtnh_len > sizeof(*nh)) {
-			parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh),
-				     nh->rtnh_len - sizeof(*nh));
-
-			if (tb[RTA_ENCAP])
-				lwt_print_encap(fp,
-						tb[RTA_ENCAP_TYPE],
-						tb[RTA_ENCAP]);
-			if (tb[RTA_NEWDST])
-				print_rta_newdst(fp, r, tb[RTA_NEWDST]);
-			if (tb[RTA_GATEWAY])
-				print_rta_gateway(fp, r->rtm_family,
-						  tb[RTA_GATEWAY]);
-			if (tb[RTA_VIA])
-				print_rta_via(fp, tb[RTA_VIA]);
-			if (tb[RTA_FLOW])
-				print_rta_flow(fp, tb[RTA_FLOW]);
-		}
-
-		if ((r->rtm_flags & RTM_F_CLONED) &&
-		    r->rtm_type == RTN_MULTICAST) {
-			print_string(PRINT_ANY, "dev",
-				     "%s", ll_index_to_name(nh->rtnh_ifindex));
-
-			if (nh->rtnh_hops != 1)
-				print_int(PRINT_ANY, "ttl", "(ttl>%d)", nh->rtnh_hops);
-
-			print_string(PRINT_FP, NULL, " ", NULL);
-		} else {
-			print_string(PRINT_ANY, "dev",
-				     "dev %s ", ll_index_to_name(nh->rtnh_ifindex));
-
-			if (r->rtm_family != AF_MPLS)
-				print_int(PRINT_ANY, "weight",
-					  "weight %d ", nh->rtnh_hops + 1);
-		}
-
-		print_rt_flags(fp, nh->rtnh_flags);
-
-		len -= NLMSG_ALIGN(nh->rtnh_len);
-		nh = RTNH_NEXT(nh);
-
-		close_json_object();
-	}
-	close_json_array(PRINT_JSON, NULL);
-}
-
-int print_route(struct nlmsghdr *n, void *arg)
-{
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 	struct rtmsg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
-	struct rtattr *tb[RTA_MAX+1];
-	int family, color, host_len;
+	struct rtattr * tb[RTA_MAX+1];
+	char abuf[256];
+	int host_len;
 	__u32 table;
-	int ret;
-
 	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 -1;
+		return 0;
 	}
 	if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
 		return 0;
@@ -752,209 +340,390 @@
 
 	if (filter.flushb) {
 		struct nlmsghdr *fn;
-
 		if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
-			ret = flush_update();
-			if (ret < 0)
-				return ret;
+			if (flush_update())
+				return -1;
 		}
-		fn = (struct nlmsghdr *)(filter.flushb + NLMSG_ALIGN(filter.flushp));
+		fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
 		memcpy(fn, n, n->nlmsg_len);
 		fn->nlmsg_type = RTM_DELROUTE;
 		fn->nlmsg_flags = NLM_F_REQUEST;
 		fn->nlmsg_seq = ++rth.seq;
-		filter.flushp = (((char *)fn) + n->nlmsg_len) - filter.flushb;
+		filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
 		filter.flushed++;
 		if (show_stats < 2)
 			return 0;
 	}
 
-	open_json_object(NULL);
 	if (n->nlmsg_type == RTM_DELROUTE)
-		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
+		fprintf(fp, "Deleted ");
+	if ((r->rtm_type != RTN_UNICAST || show_details > 0) && !filter.type)
+		fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
 
-	if ((r->rtm_type != RTN_UNICAST || show_details > 0) &&
-	    (!filter.typemask || (filter.typemask & (1 << r->rtm_type))))
-		print_string(PRINT_ANY, "type", "%s ",
-			     rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
-
-	color = COLOR_NONE;
 	if (tb[RTA_DST]) {
-		family = get_real_family(r->rtm_type, r->rtm_family);
-		color = ifa_family_color(family);
-
 		if (r->rtm_dst_len != host_len) {
-			snprintf(b1, sizeof(b1),
-				 "%s/%u", rt_addr_n2a_rta(family, tb[RTA_DST]),
-				 r->rtm_dst_len);
+			fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family,
+						       RTA_PAYLOAD(tb[RTA_DST]),
+						       RTA_DATA(tb[RTA_DST]),
+						       abuf, sizeof(abuf)),
+				r->rtm_dst_len
+				);
 		} else {
-			format_host_rta_r(family, tb[RTA_DST],
-					  b1, sizeof(b1));
-
+			fprintf(fp, "%s ", format_host(r->rtm_family,
+						       RTA_PAYLOAD(tb[RTA_DST]),
+						       RTA_DATA(tb[RTA_DST]),
+						       abuf, sizeof(abuf))
+				);
 		}
 	} else if (r->rtm_dst_len) {
-		snprintf(b1, sizeof(b1), "0/%d ", r->rtm_dst_len);
+		fprintf(fp, "0/%d ", r->rtm_dst_len);
 	} else {
-		strncpy(b1, "default", sizeof(b1));
+		fprintf(fp, "default ");
 	}
-	print_color_string(PRINT_ANY, color,
-			   "dst", "%s ", b1);
-
 	if (tb[RTA_SRC]) {
-		family = get_real_family(r->rtm_type, r->rtm_family);
-		color = ifa_family_color(family);
-
 		if (r->rtm_src_len != host_len) {
-			snprintf(b1, sizeof(b1),
-				 "%s/%u",
-				 rt_addr_n2a_rta(family, tb[RTA_SRC]),
-				 r->rtm_src_len);
+			fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family,
+						       RTA_PAYLOAD(tb[RTA_SRC]),
+						       RTA_DATA(tb[RTA_SRC]),
+						       abuf, sizeof(abuf)),
+				r->rtm_src_len
+				);
 		} else {
-			format_host_rta_r(family, tb[RTA_SRC],
-					  b1, sizeof(b1));
+			fprintf(fp, "from %s ", format_host(r->rtm_family,
+						       RTA_PAYLOAD(tb[RTA_SRC]),
+						       RTA_DATA(tb[RTA_SRC]),
+						       abuf, sizeof(abuf))
+				);
 		}
-		print_color_string(PRINT_ANY, color,
-				   "from", "from %s ", b1);
 	} else if (r->rtm_src_len) {
-		snprintf(b1, sizeof(b1), "0/%u", r->rtm_src_len);
-
-		print_string(PRINT_ANY, "src", "from %s ", b1);
+		fprintf(fp, "from 0/%u ", r->rtm_src_len);
 	}
-
-	if (tb[RTA_NH_ID])
-		print_uint(PRINT_ANY, "nhid", "nhid %u ",
-			   rta_getattr_u32(tb[RTA_NH_ID]));
-
-	if (tb[RTA_NEWDST])
-		print_rta_newdst(fp, r, tb[RTA_NEWDST]);
+	if (tb[RTA_NEWDST]) {
+		fprintf(fp, "as to %s ", format_host(r->rtm_family,
+						  RTA_PAYLOAD(tb[RTA_NEWDST]),
+						  RTA_DATA(tb[RTA_NEWDST]),
+						  abuf, sizeof(abuf))
+			);
+	}
 
 	if (tb[RTA_ENCAP])
 		lwt_print_encap(fp, tb[RTA_ENCAP_TYPE], tb[RTA_ENCAP]);
 
 	if (r->rtm_tos && filter.tosmask != -1) {
-		print_string(PRINT_ANY, "tos", "tos %s ",
-			     rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
+		SPRINT_BUF(b1);
+		fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
 	}
 
-	if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len)
-		print_rta_gateway(fp, r->rtm_family, tb[RTA_GATEWAY]);
-
-	if (tb[RTA_VIA])
-		print_rta_via(fp, tb[RTA_VIA]);
-
+	if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) {
+		fprintf(fp, "via %s ",
+			format_host(r->rtm_family,
+				    RTA_PAYLOAD(tb[RTA_GATEWAY]),
+				    RTA_DATA(tb[RTA_GATEWAY]),
+				    abuf, sizeof(abuf)));
+	}
+	if (tb[RTA_VIA]) {
+		size_t len = RTA_PAYLOAD(tb[RTA_VIA]) - 2;
+		struct rtvia *via = RTA_DATA(tb[RTA_VIA]);
+		fprintf(fp, "via %s %s ",
+			family_name(via->rtvia_family),
+			format_host(via->rtvia_family, len, via->rtvia_addr,
+				    abuf, sizeof(abuf)));
+	}
 	if (tb[RTA_OIF] && filter.oifmask != -1)
-		print_rta_if(fp, tb[RTA_OIF], "dev");
+		fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
 
 	if (table && (table != RT_TABLE_MAIN || show_details > 0) && !filter.tb)
-		print_string(PRINT_ANY,
-			     "table", "table %s ",
-			     rtnl_rttable_n2a(table, b1, sizeof(b1)));
-
-	if (!(r->rtm_flags & RTM_F_CLONED)) {
-		if ((r->rtm_protocol != RTPROT_BOOT || show_details > 0) &&
-		    filter.protocolmask != -1)
-			print_string(PRINT_ANY,
-				     "protocol", "proto %s ",
-				     rtnl_rtprot_n2a(r->rtm_protocol,
-						     b1, sizeof(b1)));
-
-		if ((r->rtm_scope != RT_SCOPE_UNIVERSE || show_details > 0) &&
-		    filter.scopemask != -1)
-			print_string(PRINT_ANY,
-				     "scope", "scope %s ",
-				     rtnl_rtscope_n2a(r->rtm_scope,
-						      b1, sizeof(b1)));
+		fprintf(fp, " table %s ", rtnl_rttable_n2a(table, b1, sizeof(b1)));
+	if (!(r->rtm_flags&RTM_F_CLONED)) {
+		if ((r->rtm_protocol != RTPROT_BOOT || show_details > 0) && filter.protocolmask != -1)
+			fprintf(fp, " proto %s ", rtnl_rtprot_n2a(r->rtm_protocol, b1, sizeof(b1)));
+		if ((r->rtm_scope != RT_SCOPE_UNIVERSE || show_details > 0) && filter.scopemask != -1)
+			fprintf(fp, " scope %s ", rtnl_rtscope_n2a(r->rtm_scope, b1, sizeof(b1)));
 	}
-
 	if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) {
-		const char *psrc
-			= rt_addr_n2a_rta(r->rtm_family, tb[RTA_PREFSRC]);
-
 		/* Do not use format_host(). It is our local addr
 		   and symbolic name will not be useful.
-		*/
-		if (is_json_context())
-			print_string(PRINT_JSON, "prefsrc", NULL, psrc);
-		else {
-			fprintf(fp, "src ");
-			print_color_string(PRINT_FP,
-					   ifa_family_color(r->rtm_family),
-					   NULL, "%s ", psrc);
-		}
-
+		 */
+		fprintf(fp, " src %s ",
+			rt_addr_n2a(r->rtm_family,
+				    RTA_PAYLOAD(tb[RTA_PREFSRC]),
+				    RTA_DATA(tb[RTA_PREFSRC]),
+				    abuf, sizeof(abuf)));
 	}
-
-	if (tb[RTA_PRIORITY] && filter.metricmask != -1)
-		print_uint(PRINT_ANY, "metric", "metric %u ",
-			   rta_getattr_u32(tb[RTA_PRIORITY]));
-
-	print_rt_flags(fp, r->rtm_flags);
-
+	if (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)
+		fprintf(fp, "onlink ");
+	if (r->rtm_flags & RTNH_F_PERVASIVE)
+		fprintf(fp, "pervasive ");
+	if (r->rtm_flags & RTNH_F_OFFLOAD)
+		fprintf(fp, "offload ");
+	if (r->rtm_flags & RTM_F_NOTIFY)
+		fprintf(fp, "notify ");
+	if (r->rtm_flags & RTNH_F_LINKDOWN)
+		fprintf(fp, "linkdown ");
 	if (tb[RTA_MARK]) {
-		unsigned int mark = rta_getattr_u32(tb[RTA_MARK]);
-
+		unsigned int mark = *(unsigned int*)RTA_DATA(tb[RTA_MARK]);
 		if (mark) {
-			if (is_json_context())
-				print_uint(PRINT_JSON, "mark", NULL, mark);
-			else if (mark >= 16)
-				print_0xhex(PRINT_FP, NULL,
-					    "mark 0x%llx ", mark);
+			if (mark >= 16)
+				fprintf(fp, " mark 0x%x", mark);
 			else
-				print_uint(PRINT_FP, NULL,
-					   "mark %u ", mark);
+				fprintf(fp, " mark %u", mark);
 		}
 	}
 
-	if (tb[RTA_FLOW] && filter.realmmask != ~0U)
-		print_rta_flow(fp, tb[RTA_FLOW]);
+	if (tb[RTA_FLOW] && filter.realmmask != ~0U) {
+		__u32 to = rta_getattr_u32(tb[RTA_FLOW]);
+		__u32 from = to>>16;
+		to &= 0xFFFF;
+		fprintf(fp, "realm%s ", from ? "s" : "");
+		if (from) {
+			fprintf(fp, "%s/",
+				rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
+		}
+		fprintf(fp, "%s ",
+			rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
+	}
 
 	if (tb[RTA_UID])
-		print_uint(PRINT_ANY, "uid", "uid %u ",
-			   rta_getattr_u32(tb[RTA_UID]));
+		fprintf(fp, "uid %u ", rta_getattr_u32(tb[RTA_UID]));
 
-	if (r->rtm_family == AF_INET) {
-		if (r->rtm_flags & RTM_F_CLONED)
-			print_cache_flags(fp, r->rtm_flags);
+	if ((r->rtm_flags&RTM_F_CLONED) && r->rtm_family == AF_INET) {
+		__u32 flags = r->rtm_flags&~0xFFFF;
+		int first = 1;
 
-		if (tb[RTA_CACHEINFO])
-			print_rta_cacheinfo(fp, RTA_DATA(tb[RTA_CACHEINFO]));
+		fprintf(fp, "%s    cache ", _SL_);
+
+#define PRTFL(fl,flname) if (flags&RTCF_##fl) { \
+  flags &= ~RTCF_##fl; \
+  fprintf(fp, "%s" flname "%s", first ? "<" : "", flags ? "," : "> "); \
+  first = 0; }
+		PRTFL(LOCAL, "local");
+		PRTFL(REJECT, "reject");
+		PRTFL(MULTICAST, "mc");
+		PRTFL(BROADCAST, "brd");
+		PRTFL(DNAT, "dst-nat");
+		PRTFL(SNAT, "src-nat");
+		PRTFL(MASQ, "masq");
+		PRTFL(DIRECTDST, "dst-direct");
+		PRTFL(DIRECTSRC, "src-direct");
+		PRTFL(REDIRECTED, "redirected");
+		PRTFL(DOREDIRECT, "redirect");
+		PRTFL(FAST, "fastroute");
+		PRTFL(NOTIFY, "notify");
+		PRTFL(TPROXY, "proxy");
+
+		if (flags)
+			fprintf(fp, "%s%x> ", first ? "<" : "", flags);
+		if (tb[RTA_CACHEINFO]) {
+			struct rta_cacheinfo *ci = RTA_DATA(tb[RTA_CACHEINFO]);
+			if (!hz)
+				hz = get_user_hz();
+			if (ci->rta_expires != 0)
+				fprintf(fp, " expires %dsec", ci->rta_expires/hz);
+			if (ci->rta_error != 0)
+				fprintf(fp, " error %d", ci->rta_error);
+			if (show_stats) {
+				if (ci->rta_clntref)
+					fprintf(fp, " users %d", ci->rta_clntref);
+				if (ci->rta_used != 0)
+					fprintf(fp, " used %d", ci->rta_used);
+				if (ci->rta_lastuse != 0)
+					fprintf(fp, " age %dsec", ci->rta_lastuse/hz);
+			}
+			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);
+		}
 	} else if (r->rtm_family == AF_INET6) {
+		struct rta_cacheinfo *ci = NULL;
 		if (tb[RTA_CACHEINFO])
-			print_rta_cacheinfo(fp, RTA_DATA(tb[RTA_CACHEINFO]));
+			ci = RTA_DATA(tb[RTA_CACHEINFO]);
+		if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
+			if (!hz)
+				hz = get_user_hz();
+			if (r->rtm_flags & RTM_F_CLONED)
+				fprintf(fp, "%s    cache ", _SL_);
+			if (ci->rta_expires)
+				fprintf(fp, " expires %dsec", ci->rta_expires/hz);
+			if (ci->rta_error != 0)
+				fprintf(fp, " error %d", ci->rta_error);
+			if (show_stats) {
+				if (ci->rta_clntref)
+					fprintf(fp, " users %d", ci->rta_clntref);
+				if (ci->rta_used != 0)
+					fprintf(fp, " used %d", ci->rta_used);
+				if (ci->rta_lastuse != 0)
+					fprintf(fp, " age %dsec", ci->rta_lastuse/hz);
+			}
+		} else if (ci) {
+			if (ci->rta_error != 0)
+				fprintf(fp, " error %d", ci->rta_error);
+		}
 	}
+	if (tb[RTA_METRICS]) {
+		int i;
+		unsigned mxlock = 0;
+		struct rtattr *mxrta[RTAX_MAX+1];
 
-	if (tb[RTA_METRICS])
-		print_rta_metrics(fp, tb[RTA_METRICS]);
+		parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]),
+			    RTA_PAYLOAD(tb[RTA_METRICS]));
+		if (mxrta[RTAX_LOCK])
+			mxlock = *(unsigned*)RTA_DATA(mxrta[RTAX_LOCK]);
 
-	if (tb[RTA_IIF] && filter.iifmask != -1)
-		print_rta_if(fp, tb[RTA_IIF], "iif");
+		for (i=2; i<= RTAX_MAX; i++) {
+			__u32 val;
 
-	if (tb[RTA_MULTIPATH])
-		print_rta_multipath(fp, r, tb[RTA_MULTIPATH]);
+			if (mxrta[i] == NULL)
+				continue;
 
-	if (tb[RTA_PREF])
-		print_rt_pref(fp, rta_getattr_u8(tb[RTA_PREF]));
+			if (i != RTAX_CC_ALGO)
+				val = rta_getattr_u32(mxrta[i]);
 
-	if (tb[RTA_TTL_PROPAGATE]) {
-		bool propagate = rta_getattr_u8(tb[RTA_TTL_PROPAGATE]);
+			if (i == RTAX_HOPLIMIT && (int)val == -1)
+				continue;
 
-		if (is_json_context())
-			print_bool(PRINT_JSON, "ttl-propogate", NULL,
-				   propagate);
-		else
-			print_string(PRINT_FP, NULL,
-				     "ttl-propogate %s",
-				     propagate ? "enabled" : "disabled");
+			if (i < sizeof(mx_names)/sizeof(char*) && mx_names[i])
+				fprintf(fp, " %s", mx_names[i]);
+			else
+				fprintf(fp, " metric %d", i);
+
+			if (mxlock & (1<<i))
+				fprintf(fp, " lock");
+
+			switch (i) {
+			case RTAX_FEATURES:
+				print_rtax_features(fp, val);
+				break;
+			default:
+				fprintf(fp, " %u", val);
+				break;
+
+			case RTAX_RTT:
+			case RTAX_RTTVAR:
+			case RTAX_RTO_MIN:
+				if (i == RTAX_RTT)
+					val /= 8;
+				else if (i == RTAX_RTTVAR)
+					val /= 4;
+
+				if (val >= 1000)
+					fprintf(fp, " %gs", val/1e3);
+				else
+					fprintf(fp, " %ums", val);
+				break;
+			case RTAX_CC_ALGO:
+				fprintf(fp, " %s", rta_getattr_str(mxrta[i]));
+				break;
+			}
+		}
 	}
+	if (tb[RTA_IIF] && filter.iifmask != -1) {
+		fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
+	}
+	if (tb[RTA_MULTIPATH]) {
+		struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]);
+		int first = 0;
 
-	print_string(PRINT_FP, NULL, "\n", NULL);
-	close_json_object();
+		len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
+
+		for (;;) {
+			if (len < sizeof(*nh))
+				break;
+			if (nh->rtnh_len > len)
+				break;
+			if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) {
+				if (first)
+					fprintf(fp, " Oifs:");
+				else
+					fprintf(fp, " ");
+			} else
+				fprintf(fp, "%s\tnexthop", _SL_);
+			if (nh->rtnh_len > sizeof(*nh)) {
+				parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh), nh->rtnh_len - sizeof(*nh));
+
+				if (tb[RTA_ENCAP])
+					lwt_print_encap(fp,
+							tb[RTA_ENCAP_TYPE],
+							tb[RTA_ENCAP]);
+
+				if (tb[RTA_GATEWAY]) {
+					fprintf(fp, " via %s ",
+						format_host(r->rtm_family,
+							    RTA_PAYLOAD(tb[RTA_GATEWAY]),
+							    RTA_DATA(tb[RTA_GATEWAY]),
+							    abuf, sizeof(abuf)));
+				}
+				if (tb[RTA_VIA]) {
+					size_t len = RTA_PAYLOAD(tb[RTA_VIA]) - 2;
+					struct rtvia *via = RTA_DATA(tb[RTA_VIA]);
+					fprintf(fp, "via %s %s ",
+						family_name(via->rtvia_family),
+						format_host(via->rtvia_family, len, via->rtvia_addr,
+							    abuf, sizeof(abuf)));
+				}
+				if (tb[RTA_FLOW]) {
+					__u32 to = rta_getattr_u32(tb[RTA_FLOW]);
+					__u32 from = to>>16;
+					to &= 0xFFFF;
+					fprintf(fp, " realm%s ", from ? "s" : "");
+					if (from) {
+						fprintf(fp, "%s/",
+							rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
+					}
+					fprintf(fp, "%s",
+						rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
+				}
+			}
+			if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) {
+				fprintf(fp, " %s", ll_index_to_name(nh->rtnh_ifindex));
+				if (nh->rtnh_hops != 1)
+					fprintf(fp, "(ttl>%d)", nh->rtnh_hops);
+			} else {
+				fprintf(fp, " dev %s", ll_index_to_name(nh->rtnh_ifindex));
+				fprintf(fp, " weight %d", nh->rtnh_hops+1);
+			}
+			if (nh->rtnh_flags & RTNH_F_DEAD)
+				fprintf(fp, " dead");
+			if (nh->rtnh_flags & RTNH_F_ONLINK)
+				fprintf(fp, " onlink");
+			if (nh->rtnh_flags & RTNH_F_PERVASIVE)
+				fprintf(fp, " pervasive");
+			if (nh->rtnh_flags & RTNH_F_LINKDOWN)
+				fprintf(fp, " linkdown");
+			len -= NLMSG_ALIGN(nh->rtnh_len);
+			nh = RTNH_NEXT(nh);
+		}
+	}
+	if (tb[RTA_PREF]) {
+		unsigned int pref = rta_getattr_u8(tb[RTA_PREF]);
+		fprintf(fp, " pref ");
+
+		switch (pref) {
+		case ICMPV6_ROUTER_PREF_LOW:
+			fprintf(fp, "low");
+			break;
+		case ICMPV6_ROUTER_PREF_MEDIUM:
+			fprintf(fp, "medium");
+			break;
+		case ICMPV6_ROUTER_PREF_HIGH:
+			fprintf(fp, "high");
+			break;
+		default:
+			fprintf(fp, "%u", pref);
+		}
+	}
+	fprintf(fp, "\n");
 	fflush(fp);
 	return 0;
 }
 
 static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r,
-			struct rtattr *rta, size_t len, struct rtnexthop *rtnh,
+			struct rtattr *rta, struct rtnexthop *rtnh,
 			int *argcp, char ***argvp)
 {
 	int argc = *argcp;
@@ -964,7 +733,6 @@
 		if (strcmp(*argv, "via") == 0) {
 			inet_prefix addr;
 			int family;
-
 			NEXT_ARG();
 			family = read_family(*argv);
 			if (family == AF_UNSPEC)
@@ -975,25 +743,20 @@
 			if (r->rtm_family == AF_UNSPEC)
 				r->rtm_family = addr.family;
 			if (addr.family == r->rtm_family) {
-				if (rta_addattr_l(rta, len, RTA_GATEWAY,
-						  &addr.data, addr.bytelen))
-					return -1;
-				rtnh->rtnh_len += sizeof(struct rtattr)
-						  + addr.bytelen;
+				rta_addattr_l(rta, 4096, RTA_GATEWAY, &addr.data, addr.bytelen);
+				rtnh->rtnh_len += sizeof(struct rtattr) + addr.bytelen;
 			} else {
-				if (rta_addattr_l(rta, len, RTA_VIA,
-						  &addr.family, addr.bytelen + 2))
-					return -1;
-				rtnh->rtnh_len += RTA_SPACE(addr.bytelen + 2);
+				rta_addattr_l(rta, 4096, RTA_VIA, &addr.family, addr.bytelen+2);
+				rtnh->rtnh_len += sizeof(struct rtattr) + addr.bytelen+2;
 			}
 		} else if (strcmp(*argv, "dev") == 0) {
 			NEXT_ARG();
-			rtnh->rtnh_ifindex = ll_name_to_index(*argv);
-			if (!rtnh->rtnh_ifindex)
-				return nodev(*argv);
+			if ((rtnh->rtnh_ifindex = ll_name_to_index(*argv)) == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n", *argv);
+				exit(1);
+			}
 		} else if (strcmp(*argv, "weight") == 0) {
-			unsigned int w;
-
+			unsigned w;
 			NEXT_ARG();
 			if (get_unsigned(&w, *argv, 0) || w == 0 || w > 256)
 				invarg("\"weight\" is invalid\n", *argv);
@@ -1002,31 +765,16 @@
 			rtnh->rtnh_flags |= RTNH_F_ONLINK;
 		} else if (matches(*argv, "realms") == 0) {
 			__u32 realm;
-
 			NEXT_ARG();
 			if (get_rt_realms_or_raw(&realm, *argv))
 				invarg("\"realm\" value is invalid\n", *argv);
-			if (rta_addattr32(rta, len, RTA_FLOW, realm))
-				return -1;
+			rta_addattr32(rta, 4096, RTA_FLOW, realm);
 			rtnh->rtnh_len += sizeof(struct rtattr) + 4;
 		} else if (strcmp(*argv, "encap") == 0) {
-			int old_len = rta->rta_len;
+			int len = rta->rta_len;
 
-			if (lwt_parse_encap(rta, len, &argc, &argv,
-					    RTA_ENCAP, RTA_ENCAP_TYPE))
-				return -1;
-			rtnh->rtnh_len += rta->rta_len - old_len;
-		} else if (strcmp(*argv, "as") == 0) {
-			inet_prefix addr;
-
-			NEXT_ARG();
-			if (strcmp(*argv, "to") == 0)
-				NEXT_ARG();
-			get_addr(&addr, *argv, r->rtm_family);
-			if (rta_addattr_l(rta, len, RTA_NEWDST,
-					  &addr.data, addr.bytelen))
-				return -1;
-			rtnh->rtnh_len += sizeof(struct rtattr) + addr.bytelen;
+			lwt_parse_encap(rta, 4096, &argc, &argv);
+			rtnh->rtnh_len += rta->rta_len - len;
 		} else
 			break;
 	}
@@ -1038,8 +786,8 @@
 static int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r,
 			  int argc, char **argv)
 {
-	char buf[4096];
-	struct rtattr *rta = (void *)buf;
+	char buf[1024];
+	struct rtattr *rta = (void*)buf;
 	struct rtnexthop *rtnh;
 
 	rta->rta_type = RTA_MULTIPATH;
@@ -1058,36 +806,25 @@
 		memset(rtnh, 0, sizeof(*rtnh));
 		rtnh->rtnh_len = sizeof(*rtnh);
 		rta->rta_len += rtnh->rtnh_len;
-		if (parse_one_nh(n, r, rta, 4096, rtnh, &argc, &argv)) {
-			fprintf(stderr, "Error: cannot parse nexthop\n");
-			exit(-1);
-		}
+		parse_one_nh(n, r, rta, rtnh, &argc, &argv);
 		rtnh = RTNH_NEXT(rtnh);
 	}
 
 	if (rta->rta_len > RTA_LENGTH(0))
-		return addattr_l(n, 4096, RTA_MULTIPATH,
-				 RTA_DATA(rta), RTA_PAYLOAD(rta));
+		addattr_l(n, 1024, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
 	return 0;
 }
 
-static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv)
+static int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
 {
 	struct {
 		struct nlmsghdr	n;
 		struct rtmsg		r;
-		char			buf[4096];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST | flags,
-		.n.nlmsg_type = cmd,
-		.r.rtm_family = preferred_family,
-		.r.rtm_table = RT_TABLE_MAIN,
-		.r.rtm_scope = RT_SCOPE_NOWHERE,
-	};
+		char  			buf[1024];
+	} req;
 	char  mxbuf[256];
-	struct rtattr *mxrta = (void *)mxbuf;
-	unsigned int mxlock = 0;
+	struct rtattr * mxrta = (void*)mxbuf;
+	unsigned mxlock = 0;
 	char  *d = NULL;
 	int gw_ok = 0;
 	int dst_ok = 0;
@@ -1096,7 +833,16 @@
 	int table_ok = 0;
 	int raw = 0;
 	int type_ok = 0;
-	__u32 nhid = 0;
+	static int hz;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+	req.n.nlmsg_type = cmd;
+	req.r.rtm_family = preferred_family;
+	req.r.rtm_table = RT_TABLE_MAIN;
+	req.r.rtm_scope = RT_SCOPE_NOWHERE;
 
 	if (cmd != RTM_DELROUTE) {
 		req.r.rtm_protocol = RTPROT_BOOT;
@@ -1110,16 +856,13 @@
 	while (argc > 0) {
 		if (strcmp(*argv, "src") == 0) {
 			inet_prefix addr;
-
 			NEXT_ARG();
 			get_addr(&addr, *argv, req.r.rtm_family);
 			if (req.r.rtm_family == AF_UNSPEC)
 				req.r.rtm_family = addr.family;
-			addattr_l(&req.n, sizeof(req),
-				  RTA_PREFSRC, &addr.data, addr.bytelen);
+			addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
 		} else if (strcmp(*argv, "as") == 0) {
 			inet_prefix addr;
-
 			NEXT_ARG();
 			if (strcmp(*argv, "to") == 0) {
 				NEXT_ARG();
@@ -1127,16 +870,10 @@
 			get_addr(&addr, *argv, req.r.rtm_family);
 			if (req.r.rtm_family == AF_UNSPEC)
 				req.r.rtm_family = addr.family;
-			addattr_l(&req.n, sizeof(req),
-				  RTA_NEWDST, &addr.data, addr.bytelen);
+			addattr_l(&req.n, sizeof(req), RTA_NEWDST, &addr.data, addr.bytelen);
 		} else if (strcmp(*argv, "via") == 0) {
 			inet_prefix addr;
 			int family;
-
-			if (gw_ok) {
-				invarg("use nexthop syntax to specify multiple via\n",
-				       *argv);
-			}
 			gw_ok = 1;
 			NEXT_ARG();
 			family = read_family(*argv);
@@ -1148,14 +885,11 @@
 			if (req.r.rtm_family == AF_UNSPEC)
 				req.r.rtm_family = addr.family;
 			if (addr.family == req.r.rtm_family)
-				addattr_l(&req.n, sizeof(req), RTA_GATEWAY,
-					  &addr.data, addr.bytelen);
+				addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
 			else
-				addattr_l(&req.n, sizeof(req), RTA_VIA,
-					  &addr.family, addr.bytelen+2);
+				addattr_l(&req.n, sizeof(req), RTA_VIA, &addr.family, addr.bytelen+2);
 		} else if (strcmp(*argv, "from") == 0) {
 			inet_prefix addr;
-
 			NEXT_ARG();
 			get_prefix(&addr, *argv, req.r.rtm_family);
 			if (req.r.rtm_family == AF_UNSPEC)
@@ -1166,38 +900,35 @@
 		} else if (strcmp(*argv, "tos") == 0 ||
 			   matches(*argv, "dsfield") == 0) {
 			__u32 tos;
-
 			NEXT_ARG();
 			if (rtnl_dsfield_a2n(&tos, *argv))
 				invarg("\"tos\" value is invalid\n", *argv);
 			req.r.rtm_tos = tos;
-		} else if (strcmp(*argv, "expires") == 0) {
+		} else if (strcmp(*argv, "expires") == 0 ) {
 			__u32 expires;
-
 			NEXT_ARG();
 			if (get_u32(&expires, *argv, 0))
 				invarg("\"expires\" value is invalid\n", *argv);
-			addattr32(&req.n, sizeof(req), RTA_EXPIRES, expires);
+			if (!hz)
+				hz = get_user_hz();
+			addattr32(&req.n, sizeof(req), RTA_EXPIRES, expires*hz);
 		} else if (matches(*argv, "metric") == 0 ||
 			   matches(*argv, "priority") == 0 ||
 			   strcmp(*argv, "preference") == 0) {
 			__u32 metric;
-
 			NEXT_ARG();
 			if (get_u32(&metric, *argv, 0))
 				invarg("\"metric\" value is invalid\n", *argv);
 			addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric);
 		} else if (strcmp(*argv, "scope") == 0) {
 			__u32 scope = 0;
-
 			NEXT_ARG();
 			if (rtnl_rtscope_a2n(&scope, *argv))
 				invarg("invalid \"scope\" value\n", *argv);
 			req.r.rtm_scope = scope;
 			scope_ok = 1;
 		} else if (strcmp(*argv, "mtu") == 0) {
-			unsigned int mtu;
-
+			unsigned mtu;
 			NEXT_ARG();
 			if (strcmp(*argv, "lock") == 0) {
 				mxlock |= (1<<RTAX_MTU);
@@ -1207,8 +938,7 @@
 				invarg("\"mtu\" value is invalid\n", *argv);
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
 		} else if (strcmp(*argv, "hoplimit") == 0) {
-			unsigned int hoplimit;
-
+			unsigned hoplimit;
 			NEXT_ARG();
 			if (strcmp(*argv, "lock") == 0) {
 				mxlock |= (1<<RTAX_HOPLIMIT);
@@ -1218,8 +948,7 @@
 				invarg("\"hoplimit\" value is invalid\n", *argv);
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_HOPLIMIT, hoplimit);
 		} else if (strcmp(*argv, "advmss") == 0) {
-			unsigned int mss;
-
+			unsigned mss;
 			NEXT_ARG();
 			if (strcmp(*argv, "lock") == 0) {
 				mxlock |= (1<<RTAX_ADVMSS);
@@ -1229,8 +958,7 @@
 				invarg("\"mss\" value is invalid\n", *argv);
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_ADVMSS, mss);
 		} else if (matches(*argv, "reordering") == 0) {
-			unsigned int reord;
-
+			unsigned reord;
 			NEXT_ARG();
 			if (strcmp(*argv, "lock") == 0) {
 				mxlock |= (1<<RTAX_REORDERING);
@@ -1240,8 +968,7 @@
 				invarg("\"reordering\" value is invalid\n", *argv);
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_REORDERING, reord);
 		} else if (strcmp(*argv, "rtt") == 0) {
-			unsigned int rtt;
-
+			unsigned rtt;
 			NEXT_ARG();
 			if (strcmp(*argv, "lock") == 0) {
 				mxlock |= (1<<RTAX_RTT);
@@ -1252,8 +979,7 @@
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT,
 				(raw) ? rtt : rtt * 8);
 		} else if (strcmp(*argv, "rto_min") == 0) {
-			unsigned int rto_min;
-
+			unsigned rto_min;
 			NEXT_ARG();
 			mxlock |= (1<<RTAX_RTO_MIN);
 			if (get_time_rtt(&rto_min, *argv, &raw))
@@ -1262,8 +988,7 @@
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTO_MIN,
 				      rto_min);
 		} else if (matches(*argv, "window") == 0) {
-			unsigned int win;
-
+			unsigned win;
 			NEXT_ARG();
 			if (strcmp(*argv, "lock") == 0) {
 				mxlock |= (1<<RTAX_WINDOW);
@@ -1273,8 +998,7 @@
 				invarg("\"window\" value is invalid\n", *argv);
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, win);
 		} else if (matches(*argv, "cwnd") == 0) {
-			unsigned int win;
-
+			unsigned win;
 			NEXT_ARG();
 			if (strcmp(*argv, "lock") == 0) {
 				mxlock |= (1<<RTAX_CWND);
@@ -1284,8 +1008,7 @@
 				invarg("\"cwnd\" value is invalid\n", *argv);
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_CWND, win);
 		} else if (matches(*argv, "initcwnd") == 0) {
-			unsigned int win;
-
+			unsigned win;
 			NEXT_ARG();
 			if (strcmp(*argv, "lock") == 0) {
 				mxlock |= (1<<RTAX_INITCWND);
@@ -1293,11 +1016,9 @@
 			}
 			if (get_unsigned(&win, *argv, 0))
 				invarg("\"initcwnd\" value is invalid\n", *argv);
-			rta_addattr32(mxrta, sizeof(mxbuf),
-				      RTAX_INITCWND, win);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITCWND, win);
 		} else if (matches(*argv, "initrwnd") == 0) {
-			unsigned int win;
-
+			unsigned win;
 			NEXT_ARG();
 			if (strcmp(*argv, "lock") == 0) {
 				mxlock |= (1<<RTAX_INITRWND);
@@ -1305,8 +1026,7 @@
 			}
 			if (get_unsigned(&win, *argv, 0))
 				invarg("\"initrwnd\" value is invalid\n", *argv);
-			rta_addattr32(mxrta, sizeof(mxbuf),
-				      RTAX_INITRWND, win);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITRWND, win);
 		} else if (matches(*argv, "features") == 0) {
 			unsigned int features = 0;
 
@@ -1320,18 +1040,15 @@
 				break;
 			}
 
-			rta_addattr32(mxrta, sizeof(mxbuf),
-				      RTAX_FEATURES, features);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_FEATURES, features);
 		} else if (matches(*argv, "quickack") == 0) {
-			unsigned int quickack;
-
+			unsigned quickack;
 			NEXT_ARG();
 			if (get_unsigned(&quickack, *argv, 0))
 				invarg("\"quickack\" value is invalid\n", *argv);
 			if (quickack != 1 && quickack != 0)
 				invarg("\"quickack\" value should be 0 or 1\n", *argv);
-			rta_addattr32(mxrta, sizeof(mxbuf),
-				      RTAX_QUICKACK, quickack);
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_QUICKACK, quickack);
 		} else if (matches(*argv, "congctl") == 0) {
 			NEXT_ARG();
 			if (strcmp(*argv, "lock") == 0) {
@@ -1341,8 +1058,7 @@
 			rta_addattr_l(mxrta, sizeof(mxbuf), RTAX_CC_ALGO, *argv,
 				      strlen(*argv));
 		} else if (matches(*argv, "rttvar") == 0) {
-			unsigned int win;
-
+			unsigned win;
 			NEXT_ARG();
 			if (strcmp(*argv, "lock") == 0) {
 				mxlock |= (1<<RTAX_RTTVAR);
@@ -1353,8 +1069,7 @@
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR,
 				(raw) ? win : win * 4);
 		} else if (matches(*argv, "ssthresh") == 0) {
-			unsigned int win;
-
+			unsigned win;
 			NEXT_ARG();
 			if (strcmp(*argv, "lock") == 0) {
 				mxlock |= (1<<RTAX_SSTHRESH);
@@ -1365,7 +1080,6 @@
 			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_SSTHRESH, win);
 		} else if (matches(*argv, "realms") == 0) {
 			__u32 realm;
-
 			NEXT_ARG();
 			if (get_rt_realms_or_raw(&realm, *argv))
 				invarg("\"realm\" value is invalid\n", *argv);
@@ -1375,21 +1089,14 @@
 		} else if (strcmp(*argv, "nexthop") == 0) {
 			nhs_ok = 1;
 			break;
-		} else if (!strcmp(*argv, "nhid")) {
-			NEXT_ARG();
-			if (get_u32(&nhid, *argv, 0))
-				invarg("\"id\" value is invalid\n", *argv);
-			addattr32(&req.n, sizeof(req), RTA_NH_ID, nhid);
 		} else if (matches(*argv, "protocol") == 0) {
 			__u32 prot;
-
 			NEXT_ARG();
 			if (rtnl_rtprot_a2n(&prot, *argv))
 				invarg("\"protocol\" value is invalid\n", *argv);
 			req.r.rtm_protocol = prot;
 		} else if (matches(*argv, "table") == 0) {
 			__u32 tid;
-
 			NEXT_ARG();
 			if (rtnl_rttable_a2n(&tid, *argv))
 				invarg("\"table\" value is invalid\n", *argv);
@@ -1400,27 +1107,12 @@
 				addattr32(&req.n, sizeof(req), RTA_TABLE, tid);
 			}
 			table_ok = 1;
-		} else if (matches(*argv, "vrf") == 0) {
-			__u32 tid;
-
-			NEXT_ARG();
-			tid = ipvrf_get_table(*argv);
-			if (tid == 0)
-				invarg("Invalid VRF\n", *argv);
-			if (tid < 256)
-				req.r.rtm_table = tid;
-			else {
-				req.r.rtm_table = RT_TABLE_UNSPEC;
-				addattr32(&req.n, sizeof(req), RTA_TABLE, tid);
-			}
-			table_ok = 1;
 		} else if (strcmp(*argv, "dev") == 0 ||
 			   strcmp(*argv, "oif") == 0) {
 			NEXT_ARG();
 			d = *argv;
 		} else if (matches(*argv, "pref") == 0) {
 			__u8 pref;
-
 			NEXT_ARG();
 			if (strcmp(*argv, "low") == 0)
 				pref = ICMPV6_ROUTER_PREF_LOW;
@@ -1433,40 +1125,15 @@
 			addattr8(&req.n, sizeof(req), RTA_PREF, pref);
 		} else if (strcmp(*argv, "encap") == 0) {
 			char buf[1024];
-			struct rtattr *rta = (void *)buf;
+			struct rtattr *rta = (void*)buf;
 
 			rta->rta_type = RTA_ENCAP;
 			rta->rta_len = RTA_LENGTH(0);
 
-			lwt_parse_encap(rta, sizeof(buf), &argc, &argv,
-					RTA_ENCAP, RTA_ENCAP_TYPE);
+			lwt_parse_encap(rta, sizeof(buf), &argc, &argv);
 
 			if (rta->rta_len > RTA_LENGTH(0))
-				addraw_l(&req.n, 1024
-					 , RTA_DATA(rta), RTA_PAYLOAD(rta));
-		} else if (strcmp(*argv, "ttl-propagate") == 0) {
-			__u8 ttl_prop;
-
-			NEXT_ARG();
-			if (matches(*argv, "enabled") == 0)
-				ttl_prop = 1;
-			else if (matches(*argv, "disabled") == 0)
-				ttl_prop = 0;
-			else
-				invarg("\"ttl-propagate\" value is invalid\n",
-				       *argv);
-
-			addattr8(&req.n, sizeof(req), RTA_TTL_PROPAGATE,
-				 ttl_prop);
-		} else if (matches(*argv, "fastopen_no_cookie") == 0) {
-			unsigned int fastopen_no_cookie;
-
-			NEXT_ARG();
-			if (get_unsigned(&fastopen_no_cookie, *argv, 0))
-				invarg("\"fastopen_no_cookie\" value is invalid\n", *argv);
-			if (fastopen_no_cookie != 1 && fastopen_no_cookie != 0)
-				invarg("\"fastopen_no_cookie\" value should be 0 or 1\n", *argv);
-			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_FASTOPEN_NO_COOKIE, fastopen_no_cookie);
+				addraw_l(&req.n, 1024, RTA_DATA(rta), RTA_PAYLOAD(rta));
 		} else {
 			int type;
 			inet_prefix dst;
@@ -1491,8 +1158,7 @@
 			req.r.rtm_dst_len = dst.bitlen;
 			dst_ok = 1;
 			if (dst.bytelen)
-				addattr_l(&req.n, sizeof(req),
-					  RTA_DST, &dst.data, dst.bytelen);
+				addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
 		}
 		argc--; argv++;
 	}
@@ -1500,12 +1166,16 @@
 	if (!dst_ok)
 		usage();
 
-	if (d) {
-		int idx = ll_name_to_index(d);
+	if (d || nhs_ok)  {
+		int idx;
 
-		if (!idx)
-			return nodev(d);
-		addattr32(&req.n, sizeof(req), RTA_OIF, idx);
+		if (d) {
+			if ((idx = ll_name_to_index(d)) == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n", d);
+				return -1;
+			}
+			addattr32(&req.n, sizeof(req), RTA_OIF, idx);
+		}
 	}
 
 	if (mxrta->rta_len > RTA_LENGTH(0)) {
@@ -1514,8 +1184,8 @@
 		addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
 	}
 
-	if (nhs_ok && parse_nexthops(&req.n, &req.r, argc, argv))
-		return -1;
+	if (nhs_ok)
+		parse_nexthops(&req.n, &req.r, argc, argv);
 
 	if (req.r.rtm_family == AF_UNSPEC)
 		req.r.rtm_family = AF_INET;
@@ -1542,7 +1212,7 @@
 			 req.r.rtm_type == RTN_UNSPEC) {
 			if (cmd == RTM_DELROUTE)
 				req.r.rtm_scope = RT_SCOPE_NOWHERE;
-			else if (!gw_ok && !nhs_ok && !nhid)
+			else if (!gw_ok && !nhs_ok)
 				req.r.rtm_scope = RT_SCOPE_LINK;
 		}
 	}
@@ -1550,30 +1220,53 @@
 	if (!type_ok && req.r.rtm_family == AF_MPLS)
 		req.r.rtm_type = RTN_UNICAST;
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		return -2;
 
 	return 0;
 }
 
+static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
+{
+	struct {
+		struct nlmsghdr nlh;
+		struct rtmsg rtm;
+	} req;
+	struct sockaddr_nl nladdr;
+
+	memset(&nladdr, 0, sizeof(nladdr));
+	memset(&req, 0, sizeof(req));
+	nladdr.nl_family = AF_NETLINK;
+
+	req.nlh.nlmsg_len = sizeof(req);
+	req.nlh.nlmsg_type = RTM_GETROUTE;
+	req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
+	req.nlh.nlmsg_pid = 0;
+	req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+	req.rtm.rtm_family = family;
+	req.rtm.rtm_flags |= RTM_F_CLONED;
+
+	return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
 static int iproute_flush_cache(void)
 {
 #define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush"
 
 	int len;
-	int flush_fd = open(ROUTE_FLUSH_PATH, O_WRONLY);
+	int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY);
 	char *buffer = "-1";
 
 	if (flush_fd < 0) {
-		fprintf(stderr, "Cannot open \"%s\": %s\n",
+		fprintf (stderr, "Cannot open \"%s\": %s\n",
 				ROUTE_FLUSH_PATH, strerror(errno));
 		return -1;
 	}
 
-	len = strlen(buffer);
+	len = strlen (buffer);
 
-	if ((write(flush_fd, (void *)buffer, len)) < len) {
-		fprintf(stderr, "Cannot flush routing cache\n");
+	if ((write (flush_fd, (void *)buffer, len)) < len) {
+		fprintf (stderr, "Cannot flush routing cache\n");
 		close(flush_fd);
 		return -1;
 	}
@@ -1583,7 +1276,8 @@
 
 static __u32 route_dump_magic = 0x45311224;
 
-static int save_route(struct nlmsghdr *n, void *arg)
+static int save_route(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		      void *arg)
 {
 	int ret;
 	int len = n->nlmsg_len;
@@ -1625,95 +1319,9 @@
 	return 0;
 }
 
-static int iproute_dump_filter(struct nlmsghdr *nlh, int reqlen)
-{
-	struct rtmsg *rtm = NLMSG_DATA(nlh);
-	int err;
-
-	rtm->rtm_protocol = filter.protocol;
-	if (filter.cloned)
-		rtm->rtm_flags |= RTM_F_CLONED;
-
-	if (filter.tb) {
-		err = addattr32(nlh, reqlen, RTA_TABLE, filter.tb);
-		if (err)
-			return err;
-	}
-
-	if (filter.oif) {
-		err = addattr32(nlh, reqlen, RTA_OIF, filter.oif);
-		if (err)
-			return err;
-	}
-
-	return 0;
-}
-
-static int iproute_flush(int family, rtnl_filter_t filter_fn)
-{
-	time_t start = time(0);
-	char flushb[4096-512];
-	int round = 0;
-	int ret;
-
-	if (filter.cloned) {
-		if (family != AF_INET6) {
-			iproute_flush_cache();
-			if (show_stats)
-				printf("*** IPv4 routing cache is flushed.\n");
-		}
-		if (family == AF_INET)
-			return 0;
-	}
-
-	filter.flushb = flushb;
-	filter.flushp = 0;
-	filter.flushe = sizeof(flushb);
-
-	for (;;) {
-		if (rtnl_routedump_req(&rth, family, iproute_dump_filter) < 0) {
-			perror("Cannot send dump request");
-			return -2;
-		}
-		filter.flushed = 0;
-		if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) {
-			fprintf(stderr, "Flush terminated\n");
-			return -2;
-		}
-		if (filter.flushed == 0) {
-			if (show_stats) {
-				if (round == 0 &&
-				    (!filter.cloned || family == AF_INET6))
-					printf("Nothing to flush.\n");
-				else
-					printf("*** Flush is complete after %d round%s ***\n",
-					       round, round > 1 ? "s" : "");
-			}
-			fflush(stdout);
-			return 0;
-		}
-		round++;
-		ret = flush_update();
-		if (ret < 0)
-			return ret;
-
-		if (time(0) - start > 30) {
-			printf("\n*** Flush not completed after %ld seconds, %d entries remain ***\n",
-			       (long)(time(0) - start), filter.flushed);
-			return -1;
-		}
-
-		if (show_stats) {
-			printf("\n*** Round %d, deleting %d entries ***\n",
-			       round, filter.flushed);
-			fflush(stdout);
-		}
-	}
-}
-
 static int iproute_list_flush_or_save(int argc, char **argv, int action)
 {
-	int dump_family = preferred_family;
+	int do_ipv6 = preferred_family;
 	char *id = NULL;
 	char *od = NULL;
 	unsigned int mark = 0;
@@ -1738,7 +1346,6 @@
 	while (argc > 0) {
 		if (matches(*argv, "table") == 0) {
 			__u32 tid;
-
 			NEXT_ARG();
 			if (rtnl_rttable_a2n(&tid, *argv)) {
 				if (strcmp(*argv, "all") == 0) {
@@ -1752,22 +1359,12 @@
 				}
 			} else
 				filter.tb = tid;
-		} else if (matches(*argv, "vrf") == 0) {
-			__u32 tid;
-
-			NEXT_ARG();
-			tid = ipvrf_get_table(*argv);
-			if (tid == 0)
-				invarg("Invalid VRF\n", *argv);
-			filter.tb = tid;
-			filter.typemask = ~(1 << RTN_LOCAL | 1<<RTN_BROADCAST);
 		} else if (matches(*argv, "cached") == 0 ||
 			   matches(*argv, "cloned") == 0) {
 			filter.cloned = 1;
 		} else if (strcmp(*argv, "tos") == 0 ||
 			   matches(*argv, "dsfield") == 0) {
 			__u32 tos;
-
 			NEXT_ARG();
 			if (rtnl_dsfield_a2n(&tos, *argv))
 				invarg("TOS value is invalid\n", *argv);
@@ -1775,7 +1372,6 @@
 			filter.tosmask = -1;
 		} else if (matches(*argv, "protocol") == 0) {
 			__u32 prot = 0;
-
 			NEXT_ARG();
 			filter.protocolmask = -1;
 			if (rtnl_rtprot_a2n(&prot, *argv)) {
@@ -1787,7 +1383,6 @@
 			filter.protocol = prot;
 		} else if (matches(*argv, "scope") == 0) {
 			__u32 scope = 0;
-
 			NEXT_ARG();
 			filter.scopemask = -1;
 			if (rtnl_rtscope_a2n(&scope, *argv)) {
@@ -1799,11 +1394,11 @@
 			filter.scope = scope;
 		} else if (matches(*argv, "type") == 0) {
 			int type;
-
 			NEXT_ARG();
+			filter.typemask = -1;
 			if (rtnl_rtntype_a2n(&type, *argv))
 				invarg("node type value is invalid\n", *argv);
-			filter.typemask = (1<<type);
+			filter.type = type;
 		} else if (strcmp(*argv, "dev") == 0 ||
 			   strcmp(*argv, "oif") == 0) {
 			NEXT_ARG();
@@ -1813,35 +1408,22 @@
 			id = *argv;
 		} else if (strcmp(*argv, "mark") == 0) {
 			NEXT_ARG();
-			if (get_unsigned(&mark, *argv, 0))
-				invarg("invalid mark value", *argv);
+			get_unsigned(&mark, *argv, 0);
 			filter.markmask = -1;
-		} else if (matches(*argv, "metric") == 0 ||
-			   matches(*argv, "priority") == 0 ||
-			   strcmp(*argv, "preference") == 0) {
-			__u32 metric;
-
-			NEXT_ARG();
-			if (get_u32(&metric, *argv, 0))
-				invarg("\"metric\" value is invalid\n", *argv);
-			filter.metric = metric;
-			filter.metricmask = -1;
 		} else if (strcmp(*argv, "via") == 0) {
 			int family;
-
 			NEXT_ARG();
 			family = read_family(*argv);
 			if (family == AF_UNSPEC)
-				family = dump_family;
+				family = do_ipv6;
 			else
 				NEXT_ARG();
 			get_prefix(&filter.rvia, *argv, family);
 		} else if (strcmp(*argv, "src") == 0) {
 			NEXT_ARG();
-			get_prefix(&filter.rprefsrc, *argv, dump_family);
+			get_prefix(&filter.rprefsrc, *argv, do_ipv6);
 		} else if (matches(*argv, "realms") == 0) {
 			__u32 realm;
-
 			NEXT_ARG();
 			if (get_rt_realms_or_raw(&realm, *argv))
 				invarg("invalid realms\n", *argv);
@@ -1858,15 +1440,15 @@
 			NEXT_ARG();
 			if (matches(*argv, "root") == 0) {
 				NEXT_ARG();
-				get_prefix(&filter.rsrc, *argv, dump_family);
+				get_prefix(&filter.rsrc, *argv, do_ipv6);
 			} else if (matches(*argv, "match") == 0) {
 				NEXT_ARG();
-				get_prefix(&filter.msrc, *argv, dump_family);
+				get_prefix(&filter.msrc, *argv, do_ipv6);
 			} else {
 				if (matches(*argv, "exact") == 0) {
 					NEXT_ARG();
 				}
-				get_prefix(&filter.msrc, *argv, dump_family);
+				get_prefix(&filter.msrc, *argv, do_ipv6);
 				filter.rsrc = filter.msrc;
 			}
 		} else {
@@ -1875,62 +1457,120 @@
 			}
 			if (matches(*argv, "root") == 0) {
 				NEXT_ARG();
-				get_prefix(&filter.rdst, *argv, dump_family);
+				get_prefix(&filter.rdst, *argv, do_ipv6);
 			} else if (matches(*argv, "match") == 0) {
 				NEXT_ARG();
-				get_prefix(&filter.mdst, *argv, dump_family);
+				get_prefix(&filter.mdst, *argv, do_ipv6);
 			} else {
 				if (matches(*argv, "exact") == 0) {
 					NEXT_ARG();
 				}
-				get_prefix(&filter.mdst, *argv, dump_family);
+				get_prefix(&filter.mdst, *argv, do_ipv6);
 				filter.rdst = filter.mdst;
 			}
 		}
 		argc--; argv++;
 	}
 
-	if (dump_family == AF_UNSPEC && filter.tb)
-		dump_family = AF_INET;
+	if (do_ipv6 == AF_UNSPEC && filter.tb)
+		do_ipv6 = AF_INET;
 
 	if (id || od)  {
 		int idx;
 
 		if (id) {
-			idx = ll_name_to_index(id);
-			if (!idx)
-				return nodev(id);
+			if ((idx = ll_name_to_index(id)) == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n", id);
+				return -1;
+			}
 			filter.iif = idx;
 			filter.iifmask = -1;
 		}
 		if (od) {
-			idx = ll_name_to_index(od);
-			if (!idx)
-				return nodev(od);
+			if ((idx = ll_name_to_index(od)) == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n", od);
+				return -1;
+			}
 			filter.oif = idx;
 			filter.oifmask = -1;
 		}
 	}
 	filter.mark = mark;
 
-	if (action == IPROUTE_FLUSH)
-		return iproute_flush(dump_family, filter_fn);
+	if (action == IPROUTE_FLUSH) {
+		int round = 0;
+		char flushb[4096-512];
+		time_t start = time(0);
 
-	if (rtnl_routedump_req(&rth, dump_family, iproute_dump_filter) < 0) {
-		perror("Cannot send dump request");
-		return -2;
+		if (filter.cloned) {
+			if (do_ipv6 != AF_INET6) {
+				iproute_flush_cache();
+				if (show_stats)
+					printf("*** IPv4 routing cache is flushed.\n");
+			}
+			if (do_ipv6 == AF_INET)
+				return 0;
+		}
+
+		filter.flushb = flushb;
+		filter.flushp = 0;
+		filter.flushe = sizeof(flushb);
+
+		for (;;) {
+			if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
+				perror("Cannot send dump request");
+				exit(1);
+			}
+			filter.flushed = 0;
+			if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) {
+				fprintf(stderr, "Flush terminated\n");
+				exit(1);
+			}
+			if (filter.flushed == 0) {
+				if (show_stats) {
+					if (round == 0 && (!filter.cloned || do_ipv6 == AF_INET6))
+						printf("Nothing to flush.\n");
+					else
+						printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
+				}
+				fflush(stdout);
+				return 0;
+			}
+			round++;
+			if (flush_update() < 0)
+				exit(1);
+
+			if (time(0) - start > 30) {
+				printf("\n*** Flush not completed after %ld seconds, %d entries remain ***\n",
+				       (long)(time(0) - start), filter.flushed);
+				exit(1);
+			}
+
+			if (show_stats) {
+				printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed);
+				fflush(stdout);
+			}
+		}
 	}
 
-	new_json_obj(json);
+	if (!filter.cloned) {
+		if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
+			perror("Cannot send dump request");
+			exit(1);
+		}
+	} else {
+		if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
+			perror("Cannot send dump request");
+			exit(1);
+		}
+	}
 
 	if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
-		return -2;
+		exit(1);
 	}
 
-	delete_json_obj();
-	fflush(stdout);
-	return 0;
+	exit(0);
 }
 
 
@@ -1939,37 +1579,41 @@
 	struct {
 		struct nlmsghdr	n;
 		struct rtmsg		r;
-		char			buf[1024];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_GETROUTE,
-		.r.rtm_family = preferred_family,
-	};
+		char  			buf[1024];
+	} req;
 	char  *idev = NULL;
 	char  *odev = NULL;
-	struct nlmsghdr *answer;
 	int connected = 0;
-	int fib_match = 0;
 	int from_ok = 0;
 	unsigned int mark = 0;
-	bool address_found = false;
+
+	memset(&req, 0, sizeof(req));
 
 	iproute_reset_filter(0);
 	filter.cloned = 2;
 
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = RTM_GETROUTE;
+	req.r.rtm_family = preferred_family;
+	req.r.rtm_table = 0;
+	req.r.rtm_protocol = 0;
+	req.r.rtm_scope = 0;
+	req.r.rtm_type = 0;
+	req.r.rtm_src_len = 0;
+	req.r.rtm_dst_len = 0;
+	req.r.rtm_tos = 0;
+
 	while (argc > 0) {
 		if (strcmp(*argv, "tos") == 0 ||
 		    matches(*argv, "dsfield") == 0) {
 			__u32 tos;
-
 			NEXT_ARG();
 			if (rtnl_dsfield_a2n(&tos, *argv))
 				invarg("TOS value is invalid\n", *argv);
 			req.r.rtm_tos = tos;
 		} else if (matches(*argv, "from") == 0) {
 			inet_prefix addr;
-
 			NEXT_ARG();
 			if (matches(*argv, "help") == 0)
 				usage();
@@ -1978,16 +1622,14 @@
 			if (req.r.rtm_family == AF_UNSPEC)
 				req.r.rtm_family = addr.family;
 			if (addr.bytelen)
-				addattr_l(&req.n, sizeof(req), RTA_SRC,
-					  &addr.data, addr.bytelen);
+				addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
 			req.r.rtm_src_len = addr.bitlen;
 		} else if (matches(*argv, "iif") == 0) {
 			NEXT_ARG();
 			idev = *argv;
 		} else if (matches(*argv, "mark") == 0) {
 			NEXT_ARG();
-			if (get_unsigned(&mark, *argv, 0))
-				invarg("invalid mark value", *argv);
+			get_unsigned(&mark, *argv, 0);
 		} else if (matches(*argv, "oif") == 0 ||
 			   strcmp(*argv, "dev") == 0) {
 			NEXT_ARG();
@@ -1996,11 +1638,6 @@
 			req.r.rtm_flags |= RTM_F_NOTIFY;
 		} else if (matches(*argv, "connected") == 0) {
 			connected = 1;
-		} else if (matches(*argv, "vrf") == 0) {
-			NEXT_ARG();
-			if (!name_is_vrf(*argv))
-				invarg("Invalid VRF\n", *argv);
-			odev = *argv;
 		} else if (matches(*argv, "uid") == 0) {
 			uid_t uid;
 
@@ -2008,45 +1645,8 @@
 			if (get_unsigned(&uid, *argv, 0))
 				invarg("invalid UID\n", *argv);
 			addattr32(&req.n, sizeof(req), RTA_UID, uid);
-		} else if (matches(*argv, "fibmatch") == 0) {
-			fib_match = 1;
-		} else if (strcmp(*argv, "as") == 0) {
-			inet_prefix addr;
-
-			NEXT_ARG();
-			if (strcmp(*argv, "to") == 0)
-				NEXT_ARG();
-			get_addr(&addr, *argv, req.r.rtm_family);
-			if (req.r.rtm_family == AF_UNSPEC)
-				req.r.rtm_family = addr.family;
-			addattr_l(&req.n, sizeof(req), RTA_NEWDST,
-				  &addr.data, addr.bytelen);
-		} else if (matches(*argv, "sport") == 0) {
-			__be16 sport;
-
-			NEXT_ARG();
-			if (get_be16(&sport, *argv, 0))
-				invarg("invalid sport\n", *argv);
-			addattr16(&req.n, sizeof(req), RTA_SPORT, sport);
-		} else if (matches(*argv, "dport") == 0) {
-			__be16 dport;
-
-			NEXT_ARG();
-			if (get_be16(&dport, *argv, 0))
-				invarg("invalid dport\n", *argv);
-			addattr16(&req.n, sizeof(req), RTA_DPORT, dport);
-		} else if (matches(*argv, "ipproto") == 0) {
-			int ipproto;
-
-			NEXT_ARG();
-			ipproto = inet_proto_a2n(*argv);
-			if (ipproto < 0)
-				invarg("Invalid \"ipproto\" value\n",
-				       *argv);
-			addattr8(&req.n, sizeof(req), RTA_IP_PROTO, ipproto);
 		} else {
 			inet_prefix addr;
-
 			if (strcmp(*argv, "to") == 0) {
 				NEXT_ARG();
 			}
@@ -2056,32 +1656,32 @@
 			if (req.r.rtm_family == AF_UNSPEC)
 				req.r.rtm_family = addr.family;
 			if (addr.bytelen)
-				addattr_l(&req.n, sizeof(req),
-					  RTA_DST, &addr.data, addr.bytelen);
+				addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
 			req.r.rtm_dst_len = addr.bitlen;
-			address_found = true;
 		}
 		argc--; argv++;
 	}
 
-	if (!address_found) {
+	if (req.r.rtm_dst_len == 0) {
 		fprintf(stderr, "need at least a destination address\n");
-		return -1;
+		exit(1);
 	}
 
 	if (idev || odev)  {
 		int idx;
 
 		if (idev) {
-			idx = ll_name_to_index(idev);
-			if (!idx)
-				return nodev(idev);
+			if ((idx = ll_name_to_index(idev)) == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n", idev);
+				return -1;
+			}
 			addattr32(&req.n, sizeof(req), RTA_IIF, idx);
 		}
 		if (odev) {
-			idx = ll_name_to_index(odev);
-			if (!idx)
-				return nodev(odev);
+			if ((idx = ll_name_to_index(odev)) == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n", odev);
+				return -1;
+			}
 			addattr32(&req.n, sizeof(req), RTA_OIF, idx);
 		}
 	}
@@ -2091,37 +1691,28 @@
 	if (req.r.rtm_family == AF_UNSPEC)
 		req.r.rtm_family = AF_INET;
 
-	/* Only IPv4 supports the RTM_F_LOOKUP_TABLE flag */
-	if (req.r.rtm_family == AF_INET)
-		req.r.rtm_flags |= RTM_F_LOOKUP_TABLE;
-	if (fib_match)
-		req.r.rtm_flags |= RTM_F_FIB_MATCH;
+	req.r.rtm_flags |= RTM_F_LOOKUP_TABLE;
 
-	if (rtnl_talk(&rth, &req.n, &answer) < 0)
+	if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0)
 		return -2;
 
-	new_json_obj(json);
-
 	if (connected && !from_ok) {
-		struct rtmsg *r = NLMSG_DATA(answer);
-		int len = answer->nlmsg_len;
-		struct rtattr *tb[RTA_MAX+1];
+		struct rtmsg *r = NLMSG_DATA(&req.n);
+		int len = req.n.nlmsg_len;
+		struct rtattr * tb[RTA_MAX+1];
 
-		if (print_route(answer, (void *)stdout) < 0) {
+		if (print_route(NULL, &req.n, (void*)stdout) < 0) {
 			fprintf(stderr, "An error :-)\n");
-			free(answer);
-			return -1;
+			exit(1);
 		}
 
-		if (answer->nlmsg_type != RTM_NEWROUTE) {
+		if (req.n.nlmsg_type != RTM_NEWROUTE) {
 			fprintf(stderr, "Not a route?\n");
-			free(answer);
 			return -1;
 		}
 		len -= NLMSG_LENGTH(sizeof(*r));
 		if (len < 0) {
 			fprintf(stderr, "Wrong len %d\n", len);
-			free(answer);
 			return -1;
 		}
 
@@ -2132,7 +1723,6 @@
 			r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
 		} else if (!tb[RTA_SRC]) {
 			fprintf(stderr, "Failed to connect the route\n");
-			free(answer);
 			return -1;
 		}
 		if (!odev && tb[RTA_OIF])
@@ -2146,63 +1736,29 @@
 		req.n.nlmsg_flags = NLM_F_REQUEST;
 		req.n.nlmsg_type = RTM_GETROUTE;
 
-		delete_json_obj();
-		free(answer);
-		if (rtnl_talk(&rth, &req.n, &answer) < 0)
+		if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0)
 			return -2;
 	}
 
-	if (print_route(answer, (void *)stdout) < 0) {
+	if (print_route(NULL, &req.n, (void*)stdout) < 0) {
 		fprintf(stderr, "An error :-)\n");
-		free(answer);
 		return -1;
 	}
 
-	delete_json_obj();
-	free(answer);
 	return 0;
 }
 
-static int rtattr_cmp(const struct rtattr *rta1, const struct rtattr *rta2)
-{
-	if (!rta1 || !rta2 || rta1->rta_len != rta2->rta_len)
-		return 1;
-
-	return memcmp(RTA_DATA(rta1), RTA_DATA(rta2), RTA_PAYLOAD(rta1));
-}
-
-static int restore_handler(struct rtnl_ctrl_data *ctrl,
+static int restore_handler(const struct sockaddr_nl *nl,
+			   struct rtnl_ctrl_data *ctrl,
 			   struct nlmsghdr *n, void *arg)
 {
-	struct rtmsg *r = NLMSG_DATA(n);
-	struct rtattr *tb[RTA_MAX+1];
-	int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
-	int ret, prio = *(int *)arg;
+	int ret;
 
-	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
-
-	/* Restore routes in correct order:
-	 * 0. ones for local addresses,
-	 * 1. ones for local networks,
-	 * 2. others (remote networks/hosts).
-	 */
-	if (!prio && !tb[RTA_GATEWAY] && (!tb[RTA_PREFSRC] ||
-	    !rtattr_cmp(tb[RTA_PREFSRC], tb[RTA_DST])))
-		goto restore;
-	else if (prio == 1 && !tb[RTA_GATEWAY] && tb[RTA_PREFSRC] &&
-		 rtattr_cmp(tb[RTA_PREFSRC], tb[RTA_DST]))
-		goto restore;
-	else if (prio == 2 && tb[RTA_GATEWAY])
-		goto restore;
-
-	return 0;
-
-restore:
 	n->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
 
 	ll_init_map(&rth);
 
-	ret = rtnl_talk(&rth, n, NULL);
+	ret = rtnl_talk(&rth, n, n, sizeof(*n));
 	if ((ret < 0) && (errno == EEXIST))
 		ret = 0;
 
@@ -2230,49 +1786,26 @@
 
 static int iproute_restore(void)
 {
-	int pos, prio;
-
 	if (route_dump_check_magic())
-		return -1;
+		exit(-1);
 
-	pos = ftell(stdin);
-	if (pos == -1) {
-		perror("Failed to restore: ftell");
-		return -1;
-	}
-
-	for (prio = 0; prio < 3; prio++) {
-		int err;
-
-		err = rtnl_from_file(stdin, &restore_handler, &prio);
-		if (err)
-			return -2;
-
-		if (fseek(stdin, pos, SEEK_SET) == -1) {
-			perror("Failed to restore: fseek");
-			return -1;
-		}
-	}
-
-	return 0;
+	exit(rtnl_from_file(stdin, &restore_handler, NULL));
 }
 
-static int show_handler(struct rtnl_ctrl_data *ctrl,
+static int show_handler(const struct sockaddr_nl *nl,
+			struct rtnl_ctrl_data *ctrl,
 			struct nlmsghdr *n, void *arg)
 {
-	print_route(n, stdout);
+	print_route(nl, n, stdout);
 	return 0;
 }
 
 static int iproute_showdump(void)
 {
 	if (route_dump_check_magic())
-		return -1;
+		exit(-1);
 
-	if (rtnl_from_file(stdin, &show_handler, NULL))
-		return -2;
-
-	return 0;
+	exit(rtnl_from_file(stdin, &show_handler, NULL));
 }
 
 void iproute_reset_filter(int ifindex)
@@ -2326,8 +1859,6 @@
 		return iproute_showdump();
 	if (matches(*argv, "help") == 0)
 		usage();
-
-	fprintf(stderr,
-		"Command \"%s\" is unknown, try \"ip route help\".\n", *argv);
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip route help\".\n", *argv);
 	exit(-1);
 }
diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c
index 60f34a3..7074906 100644
--- a/ip/iproute_lwtunnel.c
+++ b/ip/iproute_lwtunnel.c
@@ -7,7 +7,7 @@
  *		2 of the License, or (at your option) any later version.
  *
  * Authors:	Roopa Prabhu, <roopa@cumulusnetworks.com>
- *		Thomas Graf <tgraf@suug.ch>
+ * 		Thomas Graf <tgraf@suug.ch>
  *
  */
 
@@ -22,16 +22,22 @@
 #include <errno.h>
 
 #include "rt_names.h"
-#include "bpf_util.h"
 #include "utils.h"
-#include "ip_common.h"
-#include "ila_common.h"
+#include "iproute_lwtunnel.h"
 
-#include <linux/seg6.h>
-#include <linux/seg6_iptunnel.h>
-#include <linux/seg6_hmac.h>
-#include <linux/seg6_local.h>
-#include <linux/if_tunnel.h>
+static int read_encap_type(const char *name)
+{
+	if (strcmp(name, "mpls") == 0)
+		return LWTUNNEL_ENCAP_MPLS;
+	else if (strcmp(name, "ip") == 0)
+		return LWTUNNEL_ENCAP_IP;
+	else if (strcmp(name, "ip6") == 0)
+		return LWTUNNEL_ENCAP_IP6;
+	else if (strcmp(name, "ila") == 0)
+		return LWTUNNEL_ENCAP_ILA;
+	else
+		return LWTUNNEL_ENCAP_NONE;
+}
 
 static const char *format_encap_type(int type)
 {
@@ -44,291 +50,54 @@
 		return "ip6";
 	case LWTUNNEL_ENCAP_ILA:
 		return "ila";
-	case LWTUNNEL_ENCAP_BPF:
-		return "bpf";
-	case LWTUNNEL_ENCAP_SEG6:
-		return "seg6";
-	case LWTUNNEL_ENCAP_SEG6_LOCAL:
-		return "seg6local";
 	default:
 		return "unknown";
 	}
 }
 
-static void encap_type_usage(void)
-{
-	int i;
-
-	fprintf(stderr, "Usage: ip route ... encap TYPE [ OPTIONS ] [...]\n");
-
-	for (i = 1; i <= LWTUNNEL_ENCAP_MAX; i++)
-		fprintf(stderr, "%s %s\n", format_encap_type(i),
-			i == 1 ? "TYPE := " : "      ");
-
-	exit(-1);
-}
-
-static int read_encap_type(const char *name)
-{
-	if (strcmp(name, "mpls") == 0)
-		return LWTUNNEL_ENCAP_MPLS;
-	else if (strcmp(name, "ip") == 0)
-		return LWTUNNEL_ENCAP_IP;
-	else if (strcmp(name, "ip6") == 0)
-		return LWTUNNEL_ENCAP_IP6;
-	else if (strcmp(name, "ila") == 0)
-		return LWTUNNEL_ENCAP_ILA;
-	else if (strcmp(name, "bpf") == 0)
-		return LWTUNNEL_ENCAP_BPF;
-	else if (strcmp(name, "seg6") == 0)
-		return LWTUNNEL_ENCAP_SEG6;
-	else if (strcmp(name, "seg6local") == 0)
-		return LWTUNNEL_ENCAP_SEG6_LOCAL;
-	else if (strcmp(name, "help") == 0)
-		encap_type_usage();
-
-	return LWTUNNEL_ENCAP_NONE;
-}
-
-static void print_srh(FILE *fp, struct ipv6_sr_hdr *srh)
-{
-	int i;
-
-	if (is_json_context())
-		open_json_array(PRINT_JSON, "segs");
-	else
-		fprintf(fp, "segs %d [ ", srh->first_segment + 1);
-
-	for (i = srh->first_segment; i >= 0; i--)
-		print_color_string(PRINT_ANY, COLOR_INET6,
-				   NULL, "%s ",
-				   rt_addr_n2a(AF_INET6, 16, &srh->segments[i]));
-
-	if (is_json_context())
-		close_json_array(PRINT_JSON, NULL);
-	else
-		fprintf(fp, "] ");
-
-	if (sr_has_hmac(srh)) {
-		unsigned int offset = ((srh->hdrlen + 1) << 3) - 40;
-		struct sr6_tlv_hmac *tlv;
-
-		tlv = (struct sr6_tlv_hmac *)((char *)srh + offset);
-		print_0xhex(PRINT_ANY, "hmac",
-			    "hmac %llX ", ntohl(tlv->hmackeyid));
-	}
-}
-
-static const char *seg6_mode_types[] = {
-	[SEG6_IPTUN_MODE_INLINE]	= "inline",
-	[SEG6_IPTUN_MODE_ENCAP]		= "encap",
-	[SEG6_IPTUN_MODE_L2ENCAP]	= "l2encap",
-};
-
-static const char *format_seg6mode_type(int mode)
-{
-	if (mode < 0 || mode > ARRAY_SIZE(seg6_mode_types))
-		return "<unknown>";
-
-	return seg6_mode_types[mode];
-}
-
-static int read_seg6mode_type(const char *mode)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(seg6_mode_types); i++) {
-		if (strcmp(mode, seg6_mode_types[i]) == 0)
-			return i;
-	}
-
-	return -1;
-}
-
-static void print_encap_seg6(FILE *fp, struct rtattr *encap)
-{
-	struct rtattr *tb[SEG6_IPTUNNEL_MAX+1];
-	struct seg6_iptunnel_encap *tuninfo;
-
-	parse_rtattr_nested(tb, SEG6_IPTUNNEL_MAX, encap);
-
-	if (!tb[SEG6_IPTUNNEL_SRH])
-		return;
-
-	tuninfo = RTA_DATA(tb[SEG6_IPTUNNEL_SRH]);
-	print_string(PRINT_ANY, "mode",
-		     "mode %s ", format_seg6mode_type(tuninfo->mode));
-
-	print_srh(fp, tuninfo->srh);
-}
-
-static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = {
-	[SEG6_LOCAL_ACTION_END]			= "End",
-	[SEG6_LOCAL_ACTION_END_X]		= "End.X",
-	[SEG6_LOCAL_ACTION_END_T]		= "End.T",
-	[SEG6_LOCAL_ACTION_END_DX2]		= "End.DX2",
-	[SEG6_LOCAL_ACTION_END_DX6]		= "End.DX6",
-	[SEG6_LOCAL_ACTION_END_DX4]		= "End.DX4",
-	[SEG6_LOCAL_ACTION_END_DT6]		= "End.DT6",
-	[SEG6_LOCAL_ACTION_END_DT4]		= "End.DT4",
-	[SEG6_LOCAL_ACTION_END_B6]		= "End.B6",
-	[SEG6_LOCAL_ACTION_END_B6_ENCAP]	= "End.B6.Encaps",
-	[SEG6_LOCAL_ACTION_END_BM]		= "End.BM",
-	[SEG6_LOCAL_ACTION_END_S]		= "End.S",
-	[SEG6_LOCAL_ACTION_END_AS]		= "End.AS",
-	[SEG6_LOCAL_ACTION_END_AM]		= "End.AM",
-	[SEG6_LOCAL_ACTION_END_BPF]		= "End.BPF",
-};
-
-static const char *format_action_type(int action)
-{
-	if (action < 0 || action > SEG6_LOCAL_ACTION_MAX)
-		return "<invalid>";
-
-	return seg6_action_names[action] ?: "<unknown>";
-}
-
-static int read_action_type(const char *name)
-{
-	int i;
-
-	for (i = 0; i < SEG6_LOCAL_ACTION_MAX + 1; i++) {
-		if (!seg6_action_names[i])
-			continue;
-
-		if (strcmp(seg6_action_names[i], name) == 0)
-			return i;
-	}
-
-	return SEG6_LOCAL_ACTION_UNSPEC;
-}
-
-static void print_encap_bpf_prog(FILE *fp, struct rtattr *encap,
-				 const char *str)
-{
-	struct rtattr *tb[LWT_BPF_PROG_MAX+1];
-	const char *progname = NULL;
-
-	parse_rtattr_nested(tb, LWT_BPF_PROG_MAX, encap);
-
-	if (tb[LWT_BPF_PROG_NAME])
-		progname = rta_getattr_str(tb[LWT_BPF_PROG_NAME]);
-
-	if (is_json_context())
-		print_string(PRINT_JSON, str, NULL,
-			     progname ? : "<unknown>");
-	else {
-		fprintf(fp, "%s ", str);
-		if (progname)
-			fprintf(fp, "%s ", progname);
-	}
-}
-
-static void print_encap_seg6local(FILE *fp, struct rtattr *encap)
-{
-	struct rtattr *tb[SEG6_LOCAL_MAX + 1];
-	int action;
-
-	parse_rtattr_nested(tb, SEG6_LOCAL_MAX, encap);
-
-	if (!tb[SEG6_LOCAL_ACTION])
-		return;
-
-	action = rta_getattr_u32(tb[SEG6_LOCAL_ACTION]);
-
-	print_string(PRINT_ANY, "action",
-		     "action %s ", format_action_type(action));
-
-	if (tb[SEG6_LOCAL_SRH]) {
-		open_json_object("srh");
-		print_srh(fp, RTA_DATA(tb[SEG6_LOCAL_SRH]));
-		close_json_object();
-	}
-
-	if (tb[SEG6_LOCAL_TABLE])
-		print_uint(PRINT_ANY, "table",
-			   "table %u ", rta_getattr_u32(tb[SEG6_LOCAL_TABLE]));
-
-	if (tb[SEG6_LOCAL_NH4]) {
-		print_string(PRINT_ANY, "nh4",
-			     "nh4 %s ", rt_addr_n2a_rta(AF_INET, tb[SEG6_LOCAL_NH4]));
-	}
-
-	if (tb[SEG6_LOCAL_NH6]) {
-		print_string(PRINT_ANY, "nh6",
-			     "nh6 %s ", rt_addr_n2a_rta(AF_INET6, tb[SEG6_LOCAL_NH6]));
-	}
-
-	if (tb[SEG6_LOCAL_IIF]) {
-		int iif = rta_getattr_u32(tb[SEG6_LOCAL_IIF]);
-
-		print_string(PRINT_ANY, "iif",
-			     "iif %s ", ll_index_to_name(iif));
-	}
-
-	if (tb[SEG6_LOCAL_OIF]) {
-		int oif = rta_getattr_u32(tb[SEG6_LOCAL_OIF]);
-
-		print_string(PRINT_ANY, "oif",
-			     "oif %s ", ll_index_to_name(oif));
-	}
-
-	if (tb[SEG6_LOCAL_BPF])
-		print_encap_bpf_prog(fp, tb[SEG6_LOCAL_BPF], "endpoint");
-}
-
 static void print_encap_mpls(FILE *fp, struct rtattr *encap)
 {
 	struct rtattr *tb[MPLS_IPTUNNEL_MAX+1];
+	char abuf[256];
 
 	parse_rtattr_nested(tb, MPLS_IPTUNNEL_MAX, encap);
 
 	if (tb[MPLS_IPTUNNEL_DST])
-		print_string(PRINT_ANY, "dst", " %s ",
-			format_host_rta(AF_MPLS, tb[MPLS_IPTUNNEL_DST]));
-	if (tb[MPLS_IPTUNNEL_TTL])
-		print_uint(PRINT_ANY, "ttl", "ttl %u ",
-			rta_getattr_u8(tb[MPLS_IPTUNNEL_TTL]));
+		fprintf(fp, " %s ", format_host(AF_MPLS,
+			RTA_PAYLOAD(tb[MPLS_IPTUNNEL_DST]),
+			RTA_DATA(tb[MPLS_IPTUNNEL_DST]),
+			abuf, sizeof(abuf)));
 }
 
 static void print_encap_ip(FILE *fp, struct rtattr *encap)
 {
 	struct rtattr *tb[LWTUNNEL_IP_MAX+1];
-	__u16 flags;
+	char abuf[256];
 
 	parse_rtattr_nested(tb, LWTUNNEL_IP_MAX, encap);
 
 	if (tb[LWTUNNEL_IP_ID])
-		print_u64(PRINT_ANY, "id", "id %llu ",
-			   ntohll(rta_getattr_u64(tb[LWTUNNEL_IP_ID])));
+		fprintf(fp, "id %llu ", ntohll(rta_getattr_u64(tb[LWTUNNEL_IP_ID])));
 
 	if (tb[LWTUNNEL_IP_SRC])
-		print_color_string(PRINT_ANY, COLOR_INET,
-				   "src", "src %s ",
-				   rt_addr_n2a_rta(AF_INET, tb[LWTUNNEL_IP_SRC]));
+		fprintf(fp, "src %s ",
+			rt_addr_n2a(AF_INET,
+				    RTA_PAYLOAD(tb[LWTUNNEL_IP_SRC]),
+				    RTA_DATA(tb[LWTUNNEL_IP_SRC]),
+				    abuf, sizeof(abuf)));
 
 	if (tb[LWTUNNEL_IP_DST])
-		print_color_string(PRINT_ANY, COLOR_INET,
-				   "dst", "dst %s ",
-				   rt_addr_n2a_rta(AF_INET, tb[LWTUNNEL_IP_DST]));
+		fprintf(fp, "dst %s ",
+			rt_addr_n2a(AF_INET,
+				    RTA_PAYLOAD(tb[LWTUNNEL_IP_DST]),
+				    RTA_DATA(tb[LWTUNNEL_IP_DST]),
+				    abuf, sizeof(abuf)));
 
 	if (tb[LWTUNNEL_IP_TTL])
-		print_uint(PRINT_ANY, "ttl",
-			   "ttl %u ", rta_getattr_u8(tb[LWTUNNEL_IP_TTL]));
+		fprintf(fp, "ttl %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TTL]));
 
 	if (tb[LWTUNNEL_IP_TOS])
-		print_uint(PRINT_ANY, "tos",
-			   "tos %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TOS]));
-
-	if (tb[LWTUNNEL_IP_FLAGS]) {
-		flags = rta_getattr_u16(tb[LWTUNNEL_IP_FLAGS]);
-		if (flags & TUNNEL_KEY)
-			print_bool(PRINT_ANY, "key", "key ", true);
-		if (flags & TUNNEL_CSUM)
-			print_bool(PRINT_ANY, "csum", "csum ", true);
-		if (flags & TUNNEL_SEQ)
-			print_bool(PRINT_ANY, "seq", "seq ", true);
-	}
+		fprintf(fp, "tos %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TOS]));
 }
 
 static void print_encap_ila(FILE *fp, struct rtattr *encap)
@@ -340,84 +109,41 @@
 	if (tb[ILA_ATTR_LOCATOR]) {
 		char abuf[ADDR64_BUF_SIZE];
 
-		addr64_n2a(rta_getattr_u64(tb[ILA_ATTR_LOCATOR]),
+		addr64_n2a(*(__u64 *)RTA_DATA(tb[ILA_ATTR_LOCATOR]),
 			   abuf, sizeof(abuf));
-		print_string(PRINT_ANY, "locator",
-			     " %s ", abuf);
+		fprintf(fp, " %s ", abuf);
 	}
-
-	if (tb[ILA_ATTR_CSUM_MODE])
-		print_string(PRINT_ANY, "csum_mode",
-			     " csum-mode %s ",
-			     ila_csum_mode2name(rta_getattr_u8(tb[ILA_ATTR_CSUM_MODE])));
-
-	if (tb[ILA_ATTR_IDENT_TYPE])
-		print_string(PRINT_ANY, "ident_type",
-			     " ident-type %s ",
-			     ila_ident_type2name(rta_getattr_u8(tb[ILA_ATTR_IDENT_TYPE])));
-
-	if (tb[ILA_ATTR_HOOK_TYPE])
-		print_string(PRINT_ANY, "hook_type",
-			     " hook-type %s ",
-			     ila_hook_type2name(rta_getattr_u8(tb[ILA_ATTR_HOOK_TYPE])));
 }
 
 static void print_encap_ip6(FILE *fp, struct rtattr *encap)
 {
 	struct rtattr *tb[LWTUNNEL_IP6_MAX+1];
-	__u16 flags;
+	char abuf[256];
 
 	parse_rtattr_nested(tb, LWTUNNEL_IP6_MAX, encap);
 
 	if (tb[LWTUNNEL_IP6_ID])
-		print_u64(PRINT_ANY, "id", "id %llu ",
-			    ntohll(rta_getattr_u64(tb[LWTUNNEL_IP6_ID])));
+		fprintf(fp, "id %llu ", ntohll(rta_getattr_u64(tb[LWTUNNEL_IP6_ID])));
 
 	if (tb[LWTUNNEL_IP6_SRC])
-		print_color_string(PRINT_ANY, COLOR_INET6,
-				   "src", "src %s ",
-				   rt_addr_n2a_rta(AF_INET6, tb[LWTUNNEL_IP6_SRC]));
+		fprintf(fp, "src %s ",
+			rt_addr_n2a(AF_INET6,
+				    RTA_PAYLOAD(tb[LWTUNNEL_IP6_SRC]),
+				    RTA_DATA(tb[LWTUNNEL_IP6_SRC]),
+				    abuf, sizeof(abuf)));
 
 	if (tb[LWTUNNEL_IP6_DST])
-		print_color_string(PRINT_ANY, COLOR_INET6,
-				   "dst", "dst %s ",
-				   rt_addr_n2a_rta(AF_INET6, tb[LWTUNNEL_IP6_DST]));
+		fprintf(fp, "dst %s ",
+			rt_addr_n2a(AF_INET6,
+				    RTA_PAYLOAD(tb[LWTUNNEL_IP6_DST]),
+				    RTA_DATA(tb[LWTUNNEL_IP6_DST]),
+				    abuf, sizeof(abuf)));
 
 	if (tb[LWTUNNEL_IP6_HOPLIMIT])
-		print_u64(PRINT_ANY, "hoplimit",
-			   "hoplimit %u ",
-			   rta_getattr_u8(tb[LWTUNNEL_IP6_HOPLIMIT]));
+		fprintf(fp, "hoplimit %d ", rta_getattr_u8(tb[LWTUNNEL_IP6_HOPLIMIT]));
 
 	if (tb[LWTUNNEL_IP6_TC])
-		print_uint(PRINT_ANY, "tc",
-			   "tc %u ", rta_getattr_u8(tb[LWTUNNEL_IP6_TC]));
-
-	if (tb[LWTUNNEL_IP6_FLAGS]) {
-		flags = rta_getattr_u16(tb[LWTUNNEL_IP6_FLAGS]);
-		if (flags & TUNNEL_KEY)
-			print_bool(PRINT_ANY, "key", "key ", true);
-		if (flags & TUNNEL_CSUM)
-			print_bool(PRINT_ANY, "csum", "csum ", true);
-		if (flags & TUNNEL_SEQ)
-			print_bool(PRINT_ANY, "seq", "seq ", true);
-	}
-}
-
-static void print_encap_bpf(FILE *fp, struct rtattr *encap)
-{
-	struct rtattr *tb[LWT_BPF_MAX+1];
-
-	parse_rtattr_nested(tb, LWT_BPF_MAX, encap);
-
-	if (tb[LWT_BPF_IN])
-		print_encap_bpf_prog(fp, tb[LWT_BPF_IN], "in");
-	if (tb[LWT_BPF_OUT])
-		print_encap_bpf_prog(fp, tb[LWT_BPF_OUT], "out");
-	if (tb[LWT_BPF_XMIT])
-		print_encap_bpf_prog(fp, tb[LWT_BPF_XMIT], "xmit");
-	if (tb[LWT_BPF_XMIT_HEADROOM])
-		print_uint(PRINT_ANY, "headroom",
-			   " %u ", rta_getattr_u32(tb[LWT_BPF_XMIT_HEADROOM]));
+		fprintf(fp, "tc %d ", rta_getattr_u8(tb[LWTUNNEL_IP6_TC]));
 }
 
 void lwt_print_encap(FILE *fp, struct rtattr *encap_type,
@@ -430,7 +156,7 @@
 
 	et = rta_getattr_u16(encap_type);
 
-	print_string(PRINT_ANY, "encap", " encap %s ", format_encap_type(et));
+	fprintf(fp, " encap %s ", format_encap_type(et));
 
 	switch (et) {
 	case LWTUNNEL_ENCAP_MPLS:
@@ -445,443 +171,80 @@
 	case LWTUNNEL_ENCAP_IP6:
 		print_encap_ip6(fp, encap);
 		break;
-	case LWTUNNEL_ENCAP_BPF:
-		print_encap_bpf(fp, encap);
-		break;
-	case LWTUNNEL_ENCAP_SEG6:
-		print_encap_seg6(fp, encap);
-		break;
-	case LWTUNNEL_ENCAP_SEG6_LOCAL:
-		print_encap_seg6local(fp, encap);
-		break;
 	}
 }
 
-static struct ipv6_sr_hdr *parse_srh(char *segbuf, int hmac, bool encap)
-{
-	struct ipv6_sr_hdr *srh;
-	int nsegs = 0;
-	int srhlen;
-	char *s;
-	int i;
-
-	s = segbuf;
-	for (i = 0; *s; *s++ == ',' ? i++ : *s);
-	nsegs = i + 1;
-
-	if (!encap)
-		nsegs++;
-
-	srhlen = 8 + 16*nsegs;
-
-	if (hmac)
-		srhlen += 40;
-
-	srh = malloc(srhlen);
-	memset(srh, 0, srhlen);
-
-	srh->hdrlen = (srhlen >> 3) - 1;
-	srh->type = 4;
-	srh->segments_left = nsegs - 1;
-	srh->first_segment = nsegs - 1;
-
-	if (hmac)
-		srh->flags |= SR6_FLAG1_HMAC;
-
-	i = srh->first_segment;
-	for (s = strtok(segbuf, ","); s; s = strtok(NULL, ",")) {
-		inet_prefix addr;
-
-		get_addr(&addr, s, AF_INET6);
-		memcpy(&srh->segments[i], addr.data, sizeof(struct in6_addr));
-		i--;
-	}
-
-	if (hmac) {
-		struct sr6_tlv_hmac *tlv;
-
-		tlv = (struct sr6_tlv_hmac *)((char *)srh + srhlen - 40);
-		tlv->tlvhdr.type = SR6_TLV_HMAC;
-		tlv->tlvhdr.len = 38;
-		tlv->hmackeyid = htonl(hmac);
-	}
-
-	return srh;
-}
-
-static int parse_encap_seg6(struct rtattr *rta, size_t len, int *argcp,
-			    char ***argvp)
-{
-	int mode_ok = 0, segs_ok = 0, hmac_ok = 0;
-	struct seg6_iptunnel_encap *tuninfo;
-	struct ipv6_sr_hdr *srh;
-	char **argv = *argvp;
-	char segbuf[1024] = "";
-	int argc = *argcp;
-	int encap = -1;
-	__u32 hmac = 0;
-	int ret = 0;
-	int srhlen;
-
-	while (argc > 0) {
-		if (strcmp(*argv, "mode") == 0) {
-			NEXT_ARG();
-			if (mode_ok++)
-				duparg2("mode", *argv);
-			encap = read_seg6mode_type(*argv);
-			if (encap < 0)
-				invarg("\"mode\" value is invalid\n", *argv);
-		} else if (strcmp(*argv, "segs") == 0) {
-			NEXT_ARG();
-			if (segs_ok++)
-				duparg2("segs", *argv);
-			if (encap == -1)
-				invarg("\"segs\" provided before \"mode\"\n",
-				       *argv);
-
-			strlcpy(segbuf, *argv, 1024);
-		} else if (strcmp(*argv, "hmac") == 0) {
-			NEXT_ARG();
-			if (hmac_ok++)
-				duparg2("hmac", *argv);
-			get_u32(&hmac, *argv, 0);
-		} else {
-			break;
-		}
-		argc--; argv++;
-	}
-
-	srh = parse_srh(segbuf, hmac, encap);
-	srhlen = (srh->hdrlen + 1) << 3;
-
-	tuninfo = malloc(sizeof(*tuninfo) + srhlen);
-	memset(tuninfo, 0, sizeof(*tuninfo) + srhlen);
-
-	tuninfo->mode = encap;
-
-	memcpy(tuninfo->srh, srh, srhlen);
-
-	if (rta_addattr_l(rta, len, SEG6_IPTUNNEL_SRH, tuninfo,
-			  sizeof(*tuninfo) + srhlen)) {
-		ret = -1;
-		goto out;
-	}
-
-	*argcp = argc + 1;
-	*argvp = argv - 1;
-
-out:
-	free(tuninfo);
-	free(srh);
-
-	return ret;
-}
-
-struct lwt_x {
-	struct rtattr *rta;
-	size_t len;
-};
-
-static void bpf_lwt_cb(void *lwt_ptr, int fd, const char *annotation)
-{
-	struct lwt_x *x = lwt_ptr;
-
-	rta_addattr32(x->rta, x->len, LWT_BPF_PROG_FD, fd);
-	rta_addattr_l(x->rta, x->len, LWT_BPF_PROG_NAME, annotation,
-		      strlen(annotation) + 1);
-}
-
-static const struct bpf_cfg_ops bpf_cb_ops = {
-	.ebpf_cb = bpf_lwt_cb,
-};
-
-static int lwt_parse_bpf(struct rtattr *rta, size_t len,
-			 int *argcp, char ***argvp,
-			 int attr, const enum bpf_prog_type bpf_type)
-{
-	struct bpf_cfg_in cfg = {
-		.type = bpf_type,
-		.argc = *argcp,
-		.argv = *argvp,
-	};
-	struct lwt_x x = {
-		.rta = rta,
-		.len = len,
-	};
-	struct rtattr *nest;
-	int err;
-
-	nest = rta_nest(rta, len, attr);
-	err = bpf_parse_and_load_common(&cfg, &bpf_cb_ops, &x);
-	if (err < 0) {
-		fprintf(stderr, "Failed to parse eBPF program: %s\n",
-			strerror(-err));
-		return -1;
-	}
-	rta_nest_end(rta, nest);
-
-	*argcp = cfg.argc;
-	*argvp = cfg.argv;
-
-	return 0;
-}
-
-static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
-				 char ***argvp)
-{
-	int segs_ok = 0, hmac_ok = 0, table_ok = 0, nh4_ok = 0, nh6_ok = 0;
-	int iif_ok = 0, oif_ok = 0, action_ok = 0, srh_ok = 0, bpf_ok = 0;
-	__u32 action = 0, table, iif, oif;
-	struct ipv6_sr_hdr *srh;
-	char **argv = *argvp;
-	int argc = *argcp;
-	char segbuf[1024];
-	inet_prefix addr;
-	__u32 hmac = 0;
-	int ret = 0;
-
-	while (argc > 0) {
-		if (strcmp(*argv, "action") == 0) {
-			NEXT_ARG();
-			if (action_ok++)
-				duparg2("action", *argv);
-			action = read_action_type(*argv);
-			if (!action)
-				invarg("\"action\" value is invalid\n", *argv);
-			ret = rta_addattr32(rta, len, SEG6_LOCAL_ACTION,
-					    action);
-		} else if (strcmp(*argv, "table") == 0) {
-			NEXT_ARG();
-			if (table_ok++)
-				duparg2("table", *argv);
-			get_u32(&table, *argv, 0);
-			ret = rta_addattr32(rta, len, SEG6_LOCAL_TABLE, table);
-		} else if (strcmp(*argv, "nh4") == 0) {
-			NEXT_ARG();
-			if (nh4_ok++)
-				duparg2("nh4", *argv);
-			get_addr(&addr, *argv, AF_INET);
-			ret = rta_addattr_l(rta, len, SEG6_LOCAL_NH4,
-					    &addr.data, addr.bytelen);
-		} else if (strcmp(*argv, "nh6") == 0) {
-			NEXT_ARG();
-			if (nh6_ok++)
-				duparg2("nh6", *argv);
-			get_addr(&addr, *argv, AF_INET6);
-			ret = rta_addattr_l(rta, len, SEG6_LOCAL_NH6,
-					    &addr.data, addr.bytelen);
-		} else if (strcmp(*argv, "iif") == 0) {
-			NEXT_ARG();
-			if (iif_ok++)
-				duparg2("iif", *argv);
-			iif = ll_name_to_index(*argv);
-			if (!iif)
-				exit(nodev(*argv));
-			ret = rta_addattr32(rta, len, SEG6_LOCAL_IIF, iif);
-		} else if (strcmp(*argv, "oif") == 0) {
-			NEXT_ARG();
-			if (oif_ok++)
-				duparg2("oif", *argv);
-			oif = ll_name_to_index(*argv);
-			if (!oif)
-				exit(nodev(*argv));
-			ret = rta_addattr32(rta, len, SEG6_LOCAL_OIF, oif);
-		} else if (strcmp(*argv, "srh") == 0) {
-			NEXT_ARG();
-			if (srh_ok++)
-				duparg2("srh", *argv);
-			if (strcmp(*argv, "segs") != 0)
-				invarg("missing \"segs\" attribute for srh\n",
-					*argv);
-			NEXT_ARG();
-			if (segs_ok++)
-				duparg2("segs", *argv);
-			strncpy(segbuf, *argv, 1024);
-			segbuf[1023] = 0;
-			if (!NEXT_ARG_OK())
-				break;
-			NEXT_ARG();
-			if (strcmp(*argv, "hmac") == 0) {
-				NEXT_ARG();
-				if (hmac_ok++)
-					duparg2("hmac", *argv);
-				get_u32(&hmac, *argv, 0);
-			} else {
-				continue;
-			}
-		} else if (strcmp(*argv, "endpoint") == 0) {
-			NEXT_ARG();
-			if (bpf_ok++)
-				duparg2("endpoint", *argv);
-
-			if (lwt_parse_bpf(rta, len, &argc, &argv, SEG6_LOCAL_BPF,
-			    BPF_PROG_TYPE_LWT_SEG6LOCAL) < 0)
-				exit(-1);
-		} else {
-			break;
-		}
-		if (ret)
-			return ret;
-		argc--; argv++;
-	}
-
-	if (!action) {
-		fprintf(stderr, "Missing action type\n");
-		exit(-1);
-	}
-
-	if (srh_ok) {
-		int srhlen;
-
-		srh = parse_srh(segbuf, hmac,
-				action == SEG6_LOCAL_ACTION_END_B6_ENCAP);
-		srhlen = (srh->hdrlen + 1) << 3;
-		ret = rta_addattr_l(rta, len, SEG6_LOCAL_SRH, srh, srhlen);
-		free(srh);
-	}
-
-	*argcp = argc + 1;
-	*argvp = argv - 1;
-
-	return ret;
-}
-
-static int parse_encap_mpls(struct rtattr *rta, size_t len,
-			    int *argcp, char ***argvp)
+static int parse_encap_mpls(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
 {
 	inet_prefix addr;
 	int argc = *argcp;
 	char **argv = *argvp;
-	int ttl_ok = 0;
 
 	if (get_addr(&addr, *argv, AF_MPLS)) {
-		fprintf(stderr,
-			"Error: an inet address is expected rather than \"%s\".\n",
-			*argv);
+		fprintf(stderr, "Error: an inet address is expected rather than \"%s\".\n", *argv);
 		exit(1);
 	}
 
-	if (rta_addattr_l(rta, len, MPLS_IPTUNNEL_DST,
-			  &addr.data, addr.bytelen))
-		return -1;
+	rta_addattr_l(rta, len, MPLS_IPTUNNEL_DST, &addr.data,
+		      addr.bytelen);
 
-	argc--;
-	argv++;
-
-	while (argc > 0) {
-		if (strcmp(*argv, "ttl") == 0) {
-			__u8 ttl;
-
-			NEXT_ARG();
-			if (ttl_ok++)
-				duparg2("ttl", *argv);
-			if (get_u8(&ttl, *argv, 0))
-				invarg("\"ttl\" value is invalid\n", *argv);
-			if (rta_addattr8(rta, len, MPLS_IPTUNNEL_TTL, ttl))
-				return -1;
-		} else {
-			break;
-		}
-		argc--; argv++;
-	}
-
-	/* argv is currently the first unparsed argument,
-	 * but the lwt_parse_encap() caller will move to the next,
-	 * so step back
-	 */
-	*argcp = argc + 1;
-	*argvp = argv - 1;
+	*argcp = argc;
+	*argvp = argv;
 
 	return 0;
 }
 
-static int parse_encap_ip(struct rtattr *rta, size_t len,
-			  int *argcp, char ***argvp)
+static int parse_encap_ip(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
 {
-	int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0;
-	int key_ok = 0, csum_ok = 0, seq_ok = 0;
+	int id_ok = 0, dst_ok = 0, tos_ok = 0, ttl_ok = 0;
 	char **argv = *argvp;
 	int argc = *argcp;
-	int ret = 0;
-	__u16 flags = 0;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "id") == 0) {
 			__u64 id;
-
 			NEXT_ARG();
 			if (id_ok++)
 				duparg2("id", *argv);
-			if (get_be64(&id, *argv, 0))
+			if (get_u64(&id, *argv, 0))
 				invarg("\"id\" value is invalid\n", *argv);
-			ret = rta_addattr64(rta, len, LWTUNNEL_IP_ID, id);
+			rta_addattr64(rta, len, LWTUNNEL_IP_ID, htonll(id));
 		} else if (strcmp(*argv, "dst") == 0) {
 			inet_prefix addr;
-
 			NEXT_ARG();
 			if (dst_ok++)
 				duparg2("dst", *argv);
 			get_addr(&addr, *argv, AF_INET);
-			ret = rta_addattr_l(rta, len, LWTUNNEL_IP_DST,
-					    &addr.data, addr.bytelen);
-		} else if (strcmp(*argv, "src") == 0) {
-			inet_prefix addr;
-
-			NEXT_ARG();
-			if (src_ok++)
-				duparg2("src", *argv);
-			get_addr(&addr, *argv, AF_INET);
-			ret = rta_addattr_l(rta, len, LWTUNNEL_IP_SRC,
-					    &addr.data, addr.bytelen);
+			rta_addattr_l(rta, len, LWTUNNEL_IP_DST, &addr.data, addr.bytelen);
 		} else if (strcmp(*argv, "tos") == 0) {
 			__u32 tos;
-
 			NEXT_ARG();
 			if (tos_ok++)
 				duparg2("tos", *argv);
 			if (rtnl_dsfield_a2n(&tos, *argv))
 				invarg("\"tos\" value is invalid\n", *argv);
-			ret = rta_addattr8(rta, len, LWTUNNEL_IP_TOS, tos);
+			rta_addattr8(rta, len, LWTUNNEL_IP_TOS, tos);
 		} else if (strcmp(*argv, "ttl") == 0) {
 			__u8 ttl;
-
 			NEXT_ARG();
 			if (ttl_ok++)
 				duparg2("ttl", *argv);
 			if (get_u8(&ttl, *argv, 0))
 				invarg("\"ttl\" value is invalid\n", *argv);
-			ret = rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl);
-		} else if (strcmp(*argv, "key") == 0) {
-			if (key_ok++)
-				duparg2("key", *argv);
-			flags |= TUNNEL_KEY;
-		} else if (strcmp(*argv, "csum") == 0) {
-			if (csum_ok++)
-				duparg2("csum", *argv);
-			flags |= TUNNEL_CSUM;
-		} else if (strcmp(*argv, "seq") == 0) {
-			if (seq_ok++)
-				duparg2("seq", *argv);
-			flags |= TUNNEL_SEQ;
+			rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl);
 		} else {
 			break;
 		}
-		if (ret)
-			break;
 		argc--; argv++;
 	}
 
-	if (flags)
-		ret = rta_addattr16(rta, len,  LWTUNNEL_IP_FLAGS, flags);
-
 	/* argv is currently the first unparsed argument,
 	 * but the lwt_parse_encap() caller will move to the next,
-	 * so step back
-	 */
+	 * so step back */
 	*argcp = argc + 1;
 	*argvp = argv - 1;
 
-	return ret;
+	return 0;
 }
 
 static int parse_encap_ila(struct rtattr *rta, size_t len,
@@ -890,235 +253,79 @@
 	__u64 locator;
 	int argc = *argcp;
 	char **argv = *argvp;
-	int ret = 0;
 
 	if (get_addr64(&locator, *argv) < 0) {
 		fprintf(stderr, "Bad locator: %s\n", *argv);
 		exit(1);
 	}
 
-	argc--; argv++;
+	rta_addattr64(rta, 1024, ILA_ATTR_LOCATOR, locator);
 
-	if (rta_addattr64(rta, len, ILA_ATTR_LOCATOR, locator))
-		return -1;
+	*argcp = argc;
+	*argvp = argv;
 
-	while (argc > 0) {
-		if (strcmp(*argv, "csum-mode") == 0) {
-			int csum_mode;
-
-			NEXT_ARG();
-
-			csum_mode = ila_csum_name2mode(*argv);
-			if (csum_mode < 0)
-				invarg("\"csum-mode\" value is invalid\n",
-				       *argv);
-
-			ret = rta_addattr8(rta, len, ILA_ATTR_CSUM_MODE,
-					   (__u8)csum_mode);
-
-			argc--; argv++;
-		} else if (strcmp(*argv, "ident-type") == 0) {
-			int ident_type;
-
-			NEXT_ARG();
-
-			ident_type = ila_ident_name2type(*argv);
-			if (ident_type < 0)
-				invarg("\"ident-type\" value is invalid\n",
-				       *argv);
-
-			ret = rta_addattr8(rta, len, ILA_ATTR_IDENT_TYPE,
-					   (__u8)ident_type);
-
-			argc--; argv++;
-		} else if (strcmp(*argv, "hook-type") == 0) {
-			int hook_type;
-
-			NEXT_ARG();
-
-			hook_type = ila_hook_name2type(*argv);
-			if (hook_type < 0)
-				invarg("\"hook-type\" value is invalid\n",
-				       *argv);
-
-			ret = rta_addattr8(rta, len, ILA_ATTR_HOOK_TYPE,
-					   (__u8)hook_type);
-
-			argc--; argv++;
-		} else {
-			break;
-		}
-		if (ret)
-			break;
-	}
-
-	/* argv is currently the first unparsed argument,
-	 * but the lwt_parse_encap() caller will move to the next,
-	 * so step back
-	 */
-	*argcp = argc + 1;
-	*argvp = argv - 1;
-
-	return ret;
+	return 0;
 }
 
-static int parse_encap_ip6(struct rtattr *rta, size_t len,
-			   int *argcp, char ***argvp)
+static int parse_encap_ip6(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
 {
-	int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0;
-	int key_ok = 0, csum_ok = 0, seq_ok = 0;
+	int id_ok = 0, dst_ok = 0, tos_ok = 0, ttl_ok = 0;
 	char **argv = *argvp;
 	int argc = *argcp;
-	int ret = 0;
-	__u16 flags = 0;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "id") == 0) {
 			__u64 id;
-
 			NEXT_ARG();
 			if (id_ok++)
 				duparg2("id", *argv);
-			if (get_be64(&id, *argv, 0))
+			if (get_u64(&id, *argv, 0))
 				invarg("\"id\" value is invalid\n", *argv);
-			ret = rta_addattr64(rta, len, LWTUNNEL_IP6_ID, id);
+			rta_addattr64(rta, len, LWTUNNEL_IP6_ID, htonll(id));
 		} else if (strcmp(*argv, "dst") == 0) {
 			inet_prefix addr;
-
 			NEXT_ARG();
 			if (dst_ok++)
 				duparg2("dst", *argv);
 			get_addr(&addr, *argv, AF_INET6);
-			ret = rta_addattr_l(rta, len, LWTUNNEL_IP6_DST,
-					    &addr.data, addr.bytelen);
-		} else if (strcmp(*argv, "src") == 0) {
-			inet_prefix addr;
-
-			NEXT_ARG();
-			if (src_ok++)
-				duparg2("src", *argv);
-			get_addr(&addr, *argv, AF_INET6);
-			ret = rta_addattr_l(rta, len, LWTUNNEL_IP6_SRC,
-					    &addr.data, addr.bytelen);
+			rta_addattr_l(rta, len, LWTUNNEL_IP6_DST, &addr.data, addr.bytelen);
 		} else if (strcmp(*argv, "tc") == 0) {
 			__u32 tc;
-
 			NEXT_ARG();
 			if (tos_ok++)
 				duparg2("tc", *argv);
 			if (rtnl_dsfield_a2n(&tc, *argv))
 				invarg("\"tc\" value is invalid\n", *argv);
-			ret = rta_addattr8(rta, len, LWTUNNEL_IP6_TC, tc);
+			rta_addattr8(rta, len, LWTUNNEL_IP6_TC, tc);
 		} else if (strcmp(*argv, "hoplimit") == 0) {
 			__u8 hoplimit;
-
 			NEXT_ARG();
 			if (ttl_ok++)
 				duparg2("hoplimit", *argv);
 			if (get_u8(&hoplimit, *argv, 0))
-				invarg("\"hoplimit\" value is invalid\n",
-				       *argv);
-			ret = rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT,
-					   hoplimit);
-		} else if (strcmp(*argv, "key") == 0) {
-			if (key_ok++)
-				duparg2("key", *argv);
-			flags |= TUNNEL_KEY;
-		} else if (strcmp(*argv, "csum") == 0) {
-			if (csum_ok++)
-				duparg2("csum", *argv);
-			flags |= TUNNEL_CSUM;
-		} else if (strcmp(*argv, "seq") == 0) {
-			if (seq_ok++)
-				duparg2("seq", *argv);
-			flags |= TUNNEL_SEQ;
+				invarg("\"hoplimit\" value is invalid\n", *argv);
+			rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT, hoplimit);
 		} else {
 			break;
 		}
-		if (ret)
-			break;
 		argc--; argv++;
 	}
 
-	if (flags)
-		ret = rta_addattr16(rta, len,  LWTUNNEL_IP6_FLAGS, flags);
-
 	/* argv is currently the first unparsed argument,
 	 * but the lwt_parse_encap() caller will move to the next,
-	 * so step back
-	 */
-	*argcp = argc + 1;
-	*argvp = argv - 1;
-
-	return ret;
-}
-
-static void lwt_bpf_usage(void)
-{
-	fprintf(stderr, "Usage: ip route ... encap bpf [ in BPF ] [ out BPF ] [ xmit BPF ] [...]\n");
-	fprintf(stderr, "BPF := obj FILE [ section NAME ] [ verbose ]\n");
-	exit(-1);
-}
-
-static int parse_encap_bpf(struct rtattr *rta, size_t len, int *argcp,
-			   char ***argvp)
-{
-	char **argv = *argvp;
-	int argc = *argcp;
-	int headroom_set = 0;
-
-	while (argc > 0) {
-		if (strcmp(*argv, "in") == 0) {
-			NEXT_ARG();
-			if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_IN,
-					  BPF_PROG_TYPE_LWT_IN) < 0)
-				return -1;
-		} else if (strcmp(*argv, "out") == 0) {
-			NEXT_ARG();
-			if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_OUT,
-					  BPF_PROG_TYPE_LWT_OUT) < 0)
-				return -1;
-		} else if (strcmp(*argv, "xmit") == 0) {
-			NEXT_ARG();
-			if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_XMIT,
-					  BPF_PROG_TYPE_LWT_XMIT) < 0)
-				return -1;
-		} else if (strcmp(*argv, "headroom") == 0) {
-			unsigned int headroom;
-
-			NEXT_ARG();
-			if (get_unsigned(&headroom, *argv, 0) || headroom == 0)
-				invarg("headroom is invalid\n", *argv);
-			if (!headroom_set)
-				rta_addattr32(rta, len, LWT_BPF_XMIT_HEADROOM,
-					      headroom);
-			headroom_set = 1;
-		} else if (strcmp(*argv, "help") == 0) {
-			lwt_bpf_usage();
-		} else {
-			break;
-		}
-		NEXT_ARG_FWD();
-	}
-
-	/* argv is currently the first unparsed argument,
-	 * but the lwt_parse_encap() caller will move to the next,
-	 * so step back
-	 */
+	 * so step back */
 	*argcp = argc + 1;
 	*argvp = argv - 1;
 
 	return 0;
 }
 
-int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp,
-		    int encap_attr, int encap_type_attr)
+int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
 {
 	struct rtattr *nest;
 	int argc = *argcp;
 	char **argv = *argvp;
 	__u16 type;
-	int ret = 0;
 
 	NEXT_ARG();
 	type = read_encap_type(*argv);
@@ -1127,48 +334,34 @@
 
 	NEXT_ARG();
 	if (argc <= 1) {
-		fprintf(stderr,
-			"Error: unexpected end of line after \"encap\"\n");
+		fprintf(stderr, "Error: unexpected end of line after \"encap\"\n");
 		exit(-1);
 	}
 
-	nest = rta_nest(rta, len, encap_attr);
+	nest = rta_nest(rta, 1024, RTA_ENCAP);
 	switch (type) {
 	case LWTUNNEL_ENCAP_MPLS:
-		ret = parse_encap_mpls(rta, len, &argc, &argv);
+		parse_encap_mpls(rta, len, &argc, &argv);
 		break;
 	case LWTUNNEL_ENCAP_IP:
-		ret = parse_encap_ip(rta, len, &argc, &argv);
+		parse_encap_ip(rta, len, &argc, &argv);
 		break;
 	case LWTUNNEL_ENCAP_ILA:
-		ret = parse_encap_ila(rta, len, &argc, &argv);
+		parse_encap_ila(rta, len, &argc, &argv);
 		break;
 	case LWTUNNEL_ENCAP_IP6:
-		ret = parse_encap_ip6(rta, len, &argc, &argv);
-		break;
-	case LWTUNNEL_ENCAP_BPF:
-		if (parse_encap_bpf(rta, len, &argc, &argv) < 0)
-			exit(-1);
-		break;
-	case LWTUNNEL_ENCAP_SEG6:
-		ret = parse_encap_seg6(rta, len, &argc, &argv);
-		break;
-	case LWTUNNEL_ENCAP_SEG6_LOCAL:
-		ret = parse_encap_seg6local(rta, len, &argc, &argv);
+		parse_encap_ip6(rta, len, &argc, &argv);
 		break;
 	default:
 		fprintf(stderr, "Error: unsupported encap type\n");
 		break;
 	}
-	if (ret)
-		return ret;
-
 	rta_nest_end(rta, nest);
 
-	ret = rta_addattr16(rta, len, encap_type_attr, type);
+	rta_addattr16(rta, 1024, RTA_ENCAP_TYPE, type);
 
 	*argcp = argc;
 	*argvp = argv;
 
-	return ret;
+	return 0;
 }
diff --git a/ip/iproute_lwtunnel.h b/ip/iproute_lwtunnel.h
new file mode 100644
index 0000000..b82b58a
--- /dev/null
+++ b/ip/iproute_lwtunnel.h
@@ -0,0 +1,8 @@
+#ifndef __LWTUNNEL_H__
+#define __LETUNNEL_H__ 1
+
+int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp);
+void lwt_print_encap(FILE *fp, struct rtattr *encap_type,
+		     struct rtattr *encap);
+
+#endif
diff --git a/ip/iprule.c b/ip/iprule.c
index 9f5d998..ac835ab 100644
--- a/ip/iprule.c
+++ b/ip/iprule.c
@@ -13,26 +13,19 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
 #include <arpa/inet.h>
 #include <string.h>
-#include <linux/if.h>
 #include <linux/fib_rules.h>
 #include <errno.h>
 
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
-#include "json_print.h"
-
-enum list_action {
-	IPRULE_LIST,
-	IPRULE_FLUSH,
-	IPRULE_SAVE,
-};
 
 extern struct rtnl_handle rth;
 
@@ -40,264 +33,95 @@
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: ip rule { add | del } SELECTOR ACTION\n"
-		"       ip rule { flush | save | restore }\n"
-		"       ip rule [ list [ SELECTOR ]]\n"
-		"SELECTOR := [ not ] [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK[/MASK] ]\n"
-		"            [ iif STRING ] [ oif STRING ] [ pref NUMBER ] [ l3mdev ]\n"
-		"            [ uidrange NUMBER-NUMBER ]\n"
-		"            [ ipproto PROTOCOL ]\n"
-		"            [ sport [ NUMBER | NUMBER-NUMBER ]\n"
-		"            [ dport [ NUMBER | NUMBER-NUMBER ] ]\n"
-		"ACTION := [ table TABLE_ID ]\n"
-		"          [ protocol PROTO ]\n"
-		"          [ nat ADDRESS ]\n"
-		"          [ realms [SRCREALM/]DSTREALM ]\n"
-		"          [ goto NUMBER ]\n"
-		"          SUPPRESSOR\n"
-		"SUPPRESSOR := [ suppress_prefixlength NUMBER ]\n"
-		"              [ suppress_ifgroup DEVGROUP ]\n"
-		"TABLE_ID := [ local | main | default | NUMBER ]\n");
+	fprintf(stderr, "Usage: ip rule [ list | add | del | flush | save ] SELECTOR ACTION\n");
+	fprintf(stderr, "       ip rule restore\n");
+	fprintf(stderr, "SELECTOR := [ not ] [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK[/MASK] ]\n");
+	fprintf(stderr, "            [ iif STRING ] [ oif STRING ] [ pref NUMBER ] [ uidrange UID1-UID2 ]\n");
+	fprintf(stderr, "ACTION := [ table TABLE_ID ]\n");
+	fprintf(stderr, "          [ realms [SRCREALM/]DSTREALM ]\n");
+	fprintf(stderr, "          [ goto NUMBER ]\n");
+	fprintf(stderr, "          SUPPRESSOR\n");
+	fprintf(stderr, "SUPPRESSOR := [ suppress_prefixlength NUMBER ]\n");
+	fprintf(stderr, "              [ suppress_ifgroup DEVGROUP ]\n");
+	fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n");
 	exit(-1);
 }
 
-static struct
+int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
-	int not;
-	int l3mdev;
-	int iifmask, oifmask, uidrange;
-	unsigned int tb;
-	unsigned int tos, tosmask;
-	unsigned int pref, prefmask;
-	unsigned int fwmark, fwmask;
-	uint64_t tun_id;
-	char iif[IFNAMSIZ];
-	char oif[IFNAMSIZ];
-	struct fib_rule_uid_range range;
-	inet_prefix src;
-	inet_prefix dst;
-	int protocol;
-	int protocolmask;
-	struct fib_rule_port_range sport;
-	struct fib_rule_port_range dport;
-	__u8 ipproto;
-} filter;
-
-static inline int frh_get_table(struct fib_rule_hdr *frh, struct rtattr **tb)
-{
-	__u32 table = frh->table;
-	if (tb[RTA_TABLE])
-		table = rta_getattr_u32(tb[RTA_TABLE]);
-	return table;
-}
-
-static bool filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len)
-{
-	struct fib_rule_hdr *frh = NLMSG_DATA(n);
-	__u32 table;
-
-	if (preferred_family != AF_UNSPEC && frh->family != preferred_family)
-		return false;
-
-	if (filter.prefmask &&
-	    filter.pref ^ (tb[FRA_PRIORITY] ? rta_getattr_u32(tb[FRA_PRIORITY]) : 0))
-		return false;
-	if (filter.not && !(frh->flags & FIB_RULE_INVERT))
-		return false;
-
-	if (filter.src.family) {
-		inet_prefix *f_src = &filter.src;
-
-		if (f_src->family != frh->family ||
-		    f_src->bitlen > frh->src_len)
-			return false;
-
-		if (inet_addr_match_rta(f_src, tb[FRA_SRC]))
-			return false;
-	}
-
-	if (filter.dst.family) {
-		inet_prefix *f_dst = &filter.dst;
-
-		if (f_dst->family != frh->family ||
-		    f_dst->bitlen > frh->dst_len)
-			return false;
-
-		if (inet_addr_match_rta(f_dst, tb[FRA_DST]))
-			return false;
-	}
-
-	if (filter.tosmask && filter.tos ^ frh->tos)
-		return false;
-
-	if (filter.fwmark) {
-		__u32 mark = 0;
-
-		if (tb[FRA_FWMARK])
-			mark = rta_getattr_u32(tb[FRA_FWMARK]);
-		if (filter.fwmark ^ mark)
-			return false;
-	}
-	if (filter.fwmask) {
-		__u32 mask = 0;
-
-		if (tb[FRA_FWMASK])
-			mask = rta_getattr_u32(tb[FRA_FWMASK]);
-		if (filter.fwmask ^ mask)
-			return false;
-	}
-
-	if (filter.iifmask) {
-		if (tb[FRA_IFNAME]) {
-			if (strcmp(filter.iif, rta_getattr_str(tb[FRA_IFNAME])) != 0)
-				return false;
-		} else {
-			return false;
-		}
-	}
-
-	if (filter.oifmask) {
-		if (tb[FRA_OIFNAME]) {
-			if (strcmp(filter.oif, rta_getattr_str(tb[FRA_OIFNAME])) != 0)
-				return false;
-		} else {
-			return false;
-		}
-	}
-
-	if (filter.l3mdev && !(tb[FRA_L3MDEV] && rta_getattr_u8(tb[FRA_L3MDEV])))
-		return false;
-
-	if (filter.uidrange) {
-		struct fib_rule_uid_range *r = RTA_DATA(tb[FRA_UID_RANGE]);
-
-		if (!tb[FRA_UID_RANGE] ||
-		    r->start != filter.range.start ||
-		    r->end != filter.range.end)
-			return false;
-	}
-
-	if (filter.ipproto) {
-		__u8 ipproto = 0;
-
-		if (tb[FRA_IP_PROTO])
-			ipproto = rta_getattr_u8(tb[FRA_IP_PROTO]);
-		if (filter.ipproto != ipproto)
-			return false;
-	}
-
-	if (filter.sport.start) {
-		const struct fib_rule_port_range *r;
-
-		if (!tb[FRA_SPORT_RANGE])
-			return false;
-
-		r = RTA_DATA(tb[FRA_SPORT_RANGE]);
-		if (r->start != filter.sport.start ||
-		    r->end != filter.sport.end)
-			return false;
-	}
-
-	if (filter.dport.start) {
-		const struct fib_rule_port_range *r;
-
-		if (!tb[FRA_DPORT_RANGE])
-			return false;
-
-		r = RTA_DATA(tb[FRA_DPORT_RANGE]);
-		if (r->start != filter.dport.start ||
-		    r->end != filter.dport.end)
-			return false;
-	}
-
-	if (filter.tun_id) {
-		__u64 tun_id = 0;
-
-		if (tb[FRA_TUN_ID]) {
-			tun_id = ntohll(rta_getattr_u64(tb[FRA_TUN_ID]));
-			if (filter.tun_id != tun_id)
-				return false;
-		} else {
-			return false;
-		}
-	}
-
-	table = frh_get_table(frh, tb);
-	if (filter.tb > 0 && filter.tb ^ table)
-		return false;
-
-	return true;
-}
-
-int print_rule(struct nlmsghdr *n, void *arg)
-{
-	FILE *fp = arg;
-	struct fib_rule_hdr *frh = NLMSG_DATA(n);
+	FILE *fp = (FILE*)arg;
+	struct rtmsg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	int host_len = -1;
-	__u32 table, prio = 0;
-	struct rtattr *tb[FRA_MAX+1];
+	__u32 table;
+	struct rtattr * tb[FRA_MAX+1];
+	char abuf[256];
 	SPRINT_BUF(b1);
 
 	if (n->nlmsg_type != RTM_NEWRULE && n->nlmsg_type != RTM_DELRULE)
 		return 0;
 
-	len -= NLMSG_LENGTH(sizeof(*frh));
+	len -= NLMSG_LENGTH(sizeof(*r));
 	if (len < 0)
 		return -1;
 
-	parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len);
+	parse_rtattr(tb, FRA_MAX, RTM_RTA(r), len);
 
-	host_len = af_bit_len(frh->family);
+	host_len = af_bit_len(r->rtm_family);
 
-	if (!filter_nlmsg(n, tb, host_len))
-		return 0;
-
-	open_json_object(NULL);
 	if (n->nlmsg_type == RTM_DELRULE)
-		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
+		fprintf(fp, "Deleted ");
 
 	if (tb[FRA_PRIORITY])
-		prio = rta_getattr_u32(tb[FRA_PRIORITY]);
+		fprintf(fp, "%u:\t", *(unsigned*)RTA_DATA(tb[FRA_PRIORITY]));
+	else
+		fprintf(fp, "0:\t");
 
-	print_uint(PRINT_ANY, "priority", "%u:\t", prio);
-
-	if (frh->flags & FIB_RULE_INVERT)
-		print_null(PRINT_ANY, "not", "not ", NULL);
+	if (r->rtm_flags & FIB_RULE_INVERT)
+		fprintf(fp, "not ");
 
 	if (tb[FRA_SRC]) {
-		const char *src = rt_addr_n2a_rta(frh->family, tb[FRA_SRC]);
-
-		print_string(PRINT_FP, NULL, "from ", NULL);
-		print_color_string(PRINT_ANY, ifa_family_color(frh->family),
-				   "src", "%s", src);
-		if (frh->src_len != host_len)
-			print_uint(PRINT_ANY, "srclen", "/%u", frh->src_len);
-	} else if (frh->src_len) {
-		print_string(PRINT_ANY, "src", "from %s", "0");
-		print_uint(PRINT_ANY, "srclen", "/%u", frh->src_len);
+		if (r->rtm_src_len != host_len) {
+			fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family,
+						       RTA_PAYLOAD(tb[FRA_SRC]),
+						       RTA_DATA(tb[FRA_SRC]),
+						       abuf, sizeof(abuf)),
+				r->rtm_src_len
+				);
+		} else {
+			fprintf(fp, "from %s ", format_host(r->rtm_family,
+						       RTA_PAYLOAD(tb[FRA_SRC]),
+						       RTA_DATA(tb[FRA_SRC]),
+						       abuf, sizeof(abuf))
+				);
+		}
+	} else if (r->rtm_src_len) {
+		fprintf(fp, "from 0/%d ", r->rtm_src_len);
 	} else {
-		print_string(PRINT_ANY, "src", "from %s", "all");
+		fprintf(fp, "from all ");
 	}
 
 	if (tb[FRA_DST]) {
-		const char *dst = rt_addr_n2a_rta(frh->family, tb[FRA_DST]);
-
-		print_string(PRINT_FP, NULL, " to ", NULL);
-		print_color_string(PRINT_ANY, ifa_family_color(frh->family),
-				   "dst", "%s", dst);
-		if (frh->dst_len != host_len)
-			print_uint(PRINT_ANY, "dstlen", "/%u", frh->dst_len);
-	} else if (frh->dst_len) {
-		print_string(PRINT_ANY, "dst", " to %s", "0");
-		print_uint(PRINT_ANY, "dstlen", "/%u", frh->dst_len);
+		if (r->rtm_dst_len != host_len) {
+			fprintf(fp, "to %s/%u ", rt_addr_n2a(r->rtm_family,
+						       RTA_PAYLOAD(tb[FRA_DST]),
+						       RTA_DATA(tb[FRA_DST]),
+						       abuf, sizeof(abuf)),
+				r->rtm_dst_len
+				);
+		} else {
+			fprintf(fp, "to %s ", format_host(r->rtm_family,
+						       RTA_PAYLOAD(tb[FRA_DST]),
+						       RTA_DATA(tb[FRA_DST]),
+						       abuf, sizeof(abuf)));
+		}
+	} else if (r->rtm_dst_len) {
+		fprintf(fp, "to 0/%d ", r->rtm_dst_len);
 	}
 
-	if (frh->tos) {
-		print_string(PRINT_ANY, "tos",
-			     " tos %s",
-			     rtnl_dsfield_n2a(frh->tos, b1, sizeof(b1)));
+	if (r->rtm_tos) {
+		SPRINT_BUF(b1);
+		fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
 	}
 
 	if (tb[FRA_FWMARK] || tb[FRA_FWMASK]) {
@@ -307,113 +131,45 @@
 			mark = rta_getattr_u32(tb[FRA_FWMARK]);
 
 		if (tb[FRA_FWMASK] &&
-		    (mask = rta_getattr_u32(tb[FRA_FWMASK])) != 0xFFFFFFFF) {
-			print_0xhex(PRINT_ANY, "fwmark", " fwmark %#llx", mark);
-			print_0xhex(PRINT_ANY, "fwmask", "/%#llx", mask);
-		} else {
-			print_0xhex(PRINT_ANY, "fwmark", " fwmark %#llx", mark);
-		}
+		    (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]) {
-		if (!is_json_context())
-			fprintf(fp, " iif ");
-		print_color_string(PRINT_ANY, COLOR_IFNAME,
-				   "iif", "%s",
-				   rta_getattr_str(tb[FRA_IFNAME]));
-
-		if (frh->flags & FIB_RULE_IIF_DETACHED)
-			print_null(PRINT_ANY, "iif_detached", " [detached]",
-				   NULL);
+		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]) {
-		if (!is_json_context())
-			fprintf(fp, " oif ");
-
-		print_color_string(PRINT_ANY, COLOR_IFNAME, "oif", "%s",
-				   rta_getattr_str(tb[FRA_OIFNAME]));
-
-		if (frh->flags & FIB_RULE_OIF_DETACHED)
-			print_null(PRINT_ANY, "oif_detached", " [detached]",
-				   NULL);
-	}
-
-	if (tb[FRA_L3MDEV]) {
-		__u8 mdev = rta_getattr_u8(tb[FRA_L3MDEV]);
-
-		if (mdev)
-			print_null(PRINT_ANY, "l3mdev",
-				   " lookup [l3mdev-table]", NULL);
+		fprintf(fp, "oif %s ", rta_getattr_str(tb[FRA_OIFNAME]));
+		if (r->rtm_flags & FIB_RULE_OIF_DETACHED)
+			fprintf(fp, "[detached] ");
 	}
 
 	if (tb[FRA_UID_RANGE]) {
 		struct fib_rule_uid_range *r = RTA_DATA(tb[FRA_UID_RANGE]);
 
-		print_uint(PRINT_ANY, "uid_start", " uidrange %u", r->start);
-		print_uint(PRINT_ANY, "uid_end", "-%u", r->end);
+		fprintf(fp, "uidrange %u-%u ", r->start, r->end);
 	}
 
-	if (tb[FRA_IP_PROTO]) {
-		SPRINT_BUF(pbuf);
-		print_string(PRINT_ANY, "ipproto", " ipproto %s",
-			     inet_proto_n2a(rta_getattr_u8(tb[FRA_IP_PROTO]),
-					    pbuf, sizeof(pbuf)));
-	}
-
-	if (tb[FRA_SPORT_RANGE]) {
-		struct fib_rule_port_range *r = RTA_DATA(tb[FRA_SPORT_RANGE]);
-
-		if (r->start == r->end) {
-			print_uint(PRINT_ANY, "sport", " sport %u", r->start);
-		} else {
-			print_uint(PRINT_ANY, "sport_start", " sport %u",
-				   r->start);
-			print_uint(PRINT_ANY, "sport_end", "-%u", r->end);
-		}
-	}
-
-	if (tb[FRA_DPORT_RANGE]) {
-		struct fib_rule_port_range *r = RTA_DATA(tb[FRA_DPORT_RANGE]);
-
-		if (r->start == r->end) {
-			print_uint(PRINT_ANY, "dport", " dport %u", r->start);
-		} else {
-			print_uint(PRINT_ANY, "dport_start", " dport %u",
-				   r->start);
-			print_uint(PRINT_ANY, "dport_end", "-%u", r->end);
-		}
-	}
-
-	if (tb[FRA_TUN_ID]) {
-		__u64 tun_id = ntohll(rta_getattr_u64(tb[FRA_TUN_ID]));
-
-		print_u64(PRINT_ANY, "tun_id", " tun_id %llu", tun_id);
-	}
-
-	table = frh_get_table(frh, tb);
+	table = rtm_get_table(r, tb);
 	if (table) {
-		print_string(PRINT_ANY, "table",
-			     " lookup %s",
-			     rtnl_rttable_n2a(table, b1, sizeof(b1)));
+		fprintf(fp, "lookup %s ", rtnl_rttable_n2a(table, b1, sizeof(b1)));
 
 		if (tb[FRA_SUPPRESS_PREFIXLEN]) {
 			int pl = rta_getattr_u32(tb[FRA_SUPPRESS_PREFIXLEN]);
-
-			if (pl != -1)
-				print_int(PRINT_ANY, "suppress_prefixlen",
-					  " suppress_prefixlength %d", pl);
+			if (pl != -1) {
+				fprintf(fp, "suppress_prefixlength %d ", pl);
+			}
 		}
-
 		if (tb[FRA_SUPPRESS_IFGROUP]) {
 			int group = rta_getattr_u32(tb[FRA_SUPPRESS_IFGROUP]);
-
 			if (group != -1) {
-				const char *grname
-					= rtnl_group_n2a(group, b1, sizeof(b1));
-
-				print_string(PRINT_ANY, "suppress_ifgroup",
-					     " suppress_ifgroup %s", grname);
+				SPRINT_BUF(b1);
+				fprintf(fp, "suppress_ifgroup %s ", rtnl_group_n2a(group, b1, sizeof(b1)));
 			}
 		}
 	}
@@ -421,57 +177,38 @@
 	if (tb[FRA_FLOW]) {
 		__u32 to = rta_getattr_u32(tb[FRA_FLOW]);
 		__u32 from = to>>16;
-
 		to &= 0xFFFF;
-		if (from)
-			print_string(PRINT_ANY,
-				     "flow_from", " realms %s/",
-				     rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
-		else
-			print_string(PRINT_FP, NULL, " realms ", NULL);
-
-		print_string(PRINT_ANY, "flow_to", "%s",
-			     rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
+		if (from) {
+			fprintf(fp, "realms %s/",
+				rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
+		}
+		fprintf(fp, "%s ",
+			rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
 	}
 
-	if (frh->action == RTN_NAT) {
+	if (r->rtm_type == RTN_NAT) {
 		if (tb[RTA_GATEWAY]) {
-			const char *gateway;
-
-			gateway = format_host_rta(frh->family, tb[RTA_GATEWAY]);
-
-			print_string(PRINT_ANY, "nat_gateway",
-				     " map-to %s", gateway);
-		} else {
-			print_null(PRINT_ANY, "masquerade", " masquerade", NULL);
-		}
-	} else if (frh->action == FR_ACT_GOTO) {
+			fprintf(fp, "map-to %s ",
+				format_host(r->rtm_family,
+					    RTA_PAYLOAD(tb[RTA_GATEWAY]),
+					    RTA_DATA(tb[RTA_GATEWAY]),
+					    abuf, sizeof(abuf)));
+		} else
+			fprintf(fp, "masquerade");
+	} else if (r->rtm_type == FR_ACT_GOTO) {
+		fprintf(fp, "goto ");
 		if (tb[FRA_GOTO])
-			print_uint(PRINT_ANY, "goto", " goto %u",
-				   rta_getattr_u32(tb[FRA_GOTO]));
+			fprintf(fp, "%u", rta_getattr_u32(tb[FRA_GOTO]));
 		else
-			print_string(PRINT_ANY, "goto", " goto %s", "none");
+			fprintf(fp, "none");
+		if (r->rtm_flags & FIB_RULE_UNRESOLVED)
+			fprintf(fp, " [unresolved]");
+	} else if (r->rtm_type == FR_ACT_NOP)
+		fprintf(fp, "nop");
+	else if (r->rtm_type != RTN_UNICAST)
+		fprintf(fp, "%s", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
 
-		if (frh->flags & FIB_RULE_UNRESOLVED)
-			print_null(PRINT_ANY, "unresolved",
-				   " [unresolved]", NULL);
-	} else if (frh->action == FR_ACT_NOP) {
-		print_null(PRINT_ANY, "nop", " nop", NULL);
-	} else if (frh->action != FR_ACT_TO_TBL) {
-		print_string(PRINT_ANY, "action", " %s",
-			     rtnl_rtntype_n2a(frh->action, b1, sizeof(b1)));
-	}
-
-	if (tb[FRA_PROTOCOL]) {
-		__u8 protocol = rta_getattr_u8(tb[FRA_PROTOCOL]);
-
-		if ((protocol && protocol != RTPROT_KERNEL) || show_details > 0) {
-			print_string(PRINT_ANY, "protocol", " proto %s",
-				     rtnl_rtprot_n2a(protocol, b1, sizeof(b1)));
-		}
-	}
-	print_string(PRINT_FP, NULL, "\n", "");
-	close_json_object();
+	fprintf(fp, "\n");
 	fflush(fp);
 	return 0;
 }
@@ -496,7 +233,7 @@
 	return 0;
 }
 
-static int save_rule(struct nlmsghdr *n, void *arg)
+static int save_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
 	int ret;
 
@@ -509,217 +246,35 @@
 	return ret == n->nlmsg_len ? 0 : ret;
 }
 
-static int flush_rule(struct nlmsghdr *n, void *arg)
+static int iprule_list_or_save(int argc, char **argv, int save)
 {
-	struct rtnl_handle rth2;
-	struct fib_rule_hdr *frh = NLMSG_DATA(n);
-	int len = n->nlmsg_len;
-	struct rtattr *tb[FRA_MAX+1];
-	int host_len = -1;
-
-	len -= NLMSG_LENGTH(sizeof(*frh));
-	if (len < 0)
-		return -1;
-
-	parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len);
-
-	host_len = af_bit_len(frh->family);
-	if (!filter_nlmsg(n, tb, host_len))
-		return 0;
-
-	if (tb[FRA_PROTOCOL]) {
-		__u8 protocol = rta_getattr_u8(tb[FRA_PROTOCOL]);
-
-		if ((filter.protocol ^ protocol) & filter.protocolmask)
-			return 0;
-	}
-
-	if (tb[FRA_PRIORITY]) {
-		n->nlmsg_type = RTM_DELRULE;
-		n->nlmsg_flags = NLM_F_REQUEST;
-
-		if (rtnl_open(&rth2, 0) < 0)
-			return -1;
-
-		if (rtnl_talk(&rth2, n, NULL) < 0)
-			return -2;
-
-		rtnl_close(&rth2);
-	}
-
-	return 0;
-}
-
-static int iprule_list_flush_or_save(int argc, char **argv, int action)
-{
-	rtnl_filter_t filter_fn;
+	rtnl_filter_t filter = print_rule;
 	int af = preferred_family;
 
 	if (af == AF_UNSPEC)
 		af = AF_INET;
 
-	if (action == IPRULE_SAVE && argc > 0) {
-		fprintf(stderr, "\"ip rule save\" does not take any arguments.\n");
+	if (argc > 0) {
+		fprintf(stderr, "\"ip rule %s\" does not take any arguments.\n",
+				save ? "save" : "show");
 		return -1;
 	}
 
-	switch (action) {
-	case IPRULE_SAVE:
+	if (save) {
 		if (save_rule_prep())
 			return -1;
-		filter_fn = save_rule;
-		break;
-	case IPRULE_FLUSH:
-		filter_fn = flush_rule;
-		break;
-	default:
-		filter_fn = print_rule;
+		filter = save_rule;
 	}
 
-	memset(&filter, 0, sizeof(filter));
-
-	while (argc > 0) {
-		if (matches(*argv, "preference") == 0 ||
-		    matches(*argv, "order") == 0 ||
-		    matches(*argv, "priority") == 0) {
-			__u32 pref;
-
-			NEXT_ARG();
-			if (get_u32(&pref, *argv, 0))
-				invarg("preference value is invalid\n", *argv);
-			filter.pref = pref;
-			filter.prefmask = 1;
-		} else if (strcmp(*argv, "not") == 0) {
-			filter.not = 1;
-		} else if (strcmp(*argv, "tos") == 0) {
-			__u32 tos;
-
-			NEXT_ARG();
-			if (rtnl_dsfield_a2n(&tos, *argv))
-				invarg("TOS value is invalid\n", *argv);
-			filter.tos = tos;
-			filter.tosmask = 1;
-		} else if (strcmp(*argv, "fwmark") == 0) {
-			char *slash;
-			__u32 fwmark, fwmask;
-
-			NEXT_ARG();
-			slash = strchr(*argv, '/');
-			if (slash != NULL)
-				*slash = '\0';
-			if (get_u32(&fwmark, *argv, 0))
-				invarg("fwmark value is invalid\n", *argv);
-			filter.fwmark = fwmark;
-			if (slash) {
-				if (get_u32(&fwmask, slash+1, 0))
-					invarg("fwmask value is invalid\n",
-					       slash+1);
-				filter.fwmask = fwmask;
-			}
-		} else if (strcmp(*argv, "dev") == 0 ||
-			   strcmp(*argv, "iif") == 0) {
-			NEXT_ARG();
-			if (get_ifname(filter.iif, *argv))
-				invarg("\"iif\"/\"dev\" not a valid ifname", *argv);
-			filter.iifmask = 1;
-		} else if (strcmp(*argv, "oif") == 0) {
-			NEXT_ARG();
-			if (get_ifname(filter.oif, *argv))
-				invarg("\"oif\" not a valid ifname", *argv);
-			filter.oifmask = 1;
-		} else if (strcmp(*argv, "l3mdev") == 0) {
-			filter.l3mdev = 1;
-		} else if (strcmp(*argv, "uidrange") == 0) {
-			NEXT_ARG();
-			filter.uidrange = 1;
-			if (sscanf(*argv, "%u-%u",
-				   &filter.range.start,
-				   &filter.range.end) != 2)
-				invarg("invalid UID range\n", *argv);
-
-		} else if (matches(*argv, "tun_id") == 0) {
-			__u64 tun_id;
-
-			NEXT_ARG();
-			if (get_u64(&tun_id, *argv, 0))
-				invarg("\"tun_id\" value is invalid\n", *argv);
-			filter.tun_id = tun_id;
-		} else if (matches(*argv, "lookup") == 0 ||
-			   matches(*argv, "table") == 0) {
-			__u32 tid;
-
-			NEXT_ARG();
-			if (rtnl_rttable_a2n(&tid, *argv))
-				invarg("table id value is invalid\n", *argv);
-			filter.tb = tid;
-		} else if (matches(*argv, "from") == 0 ||
-			   matches(*argv, "src") == 0) {
-			NEXT_ARG();
-			if (get_prefix(&filter.src, *argv, af))
-				invarg("from value is invalid\n", *argv);
-		} else if (matches(*argv, "protocol") == 0) {
-			__u32 prot;
-			NEXT_ARG();
-			filter.protocolmask = -1;
-			if (rtnl_rtprot_a2n(&prot, *argv)) {
-				if (strcmp(*argv, "all") != 0)
-					invarg("invalid \"protocol\"\n", *argv);
-				prot = 0;
-				filter.protocolmask = 0;
-			}
-			filter.protocol = prot;
-		} else if (strcmp(*argv, "ipproto") == 0) {
-			int ipproto;
-
-			NEXT_ARG();
-			ipproto = inet_proto_a2n(*argv);
-			if (ipproto < 0)
-				invarg("Invalid \"ipproto\" value\n", *argv);
-			filter.ipproto = ipproto;
-		} else if (strcmp(*argv, "sport") == 0) {
-			struct fib_rule_port_range r;
-			int ret;
-
-			NEXT_ARG();
-			ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
-			if (ret == 1)
-				r.end = r.start;
-			else if (ret != 2)
-				invarg("invalid port range\n", *argv);
-			filter.sport = r;
-		} else if (strcmp(*argv, "dport") == 0) {
-			struct fib_rule_port_range r;
-			int ret;
-
-			NEXT_ARG();
-			ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
-			if (ret == 1)
-				r.end = r.start;
-			else if (ret != 2)
-				invarg("invalid dport range\n", *argv);
-			filter.dport = r;
-		} else{
-			if (matches(*argv, "dst") == 0 ||
-			    matches(*argv, "to") == 0) {
-				NEXT_ARG();
-			}
-			if (get_prefix(&filter.dst, *argv, af))
-				invarg("to value is invalid\n", *argv);
-		}
-		argc--; argv++;
-	}
-
-	if (rtnl_ruledump_req(&rth, af) < 0) {
+	if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) {
 		perror("Cannot send dump request");
 		return 1;
 	}
 
-	new_json_obj(json);
-	if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) {
+	if (rtnl_dump_filter(&rth, filter, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		return 1;
 	}
-	delete_json_obj();
 
 	return 0;
 }
@@ -736,15 +291,15 @@
 
 	ret = fread(&magic, sizeof(magic), 1, stdin);
 	if (magic != rule_dump_magic) {
-		fprintf(stderr, "Magic mismatch (%d elems, %x magic)\n",
-			ret, magic);
+		fprintf(stderr, "Magic mismatch (%d elems, %x magic)\n", ret, magic);
 		return -1;
 	}
 
 	return 0;
 }
 
-static int restore_handler(struct rtnl_ctrl_data *ctrl,
+static int restore_handler(const struct sockaddr_nl *nl,
+			   struct rtnl_ctrl_data *ctrl,
 			   struct nlmsghdr *n, void *arg)
 {
 	int ret;
@@ -753,7 +308,7 @@
 
 	ll_init_map(&rth);
 
-	ret = rtnl_talk(&rth, n, NULL);
+	ret = rtnl_talk(&rth, n, n, sizeof(*n));
 	if ((ret < 0) && (errno == EEXIST))
 		ret = 0;
 
@@ -771,60 +326,49 @@
 
 static int iprule_modify(int cmd, int argc, char **argv)
 {
-	int l3mdev_rule = 0;
 	int table_ok = 0;
-	__u32 tid = 0;
 	struct {
 		struct nlmsghdr	n;
-		struct fib_rule_hdr	frh;
-		char			buf[1024];
-	} req = {
-		.n.nlmsg_type = cmd,
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct fib_rule_hdr)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.frh.family = preferred_family,
-		.frh.action = FR_ACT_UNSPEC,
-	};
+		struct rtmsg		r;
+		char  			buf[1024];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_type = cmd;
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.r.rtm_family = preferred_family;
+	req.r.rtm_protocol = RTPROT_BOOT;
+	req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+	req.r.rtm_table = 0;
+	req.r.rtm_type = RTN_UNSPEC;
+	req.r.rtm_flags = 0;
 
 	if (cmd == RTM_NEWRULE) {
-		if (argc == 0) {
-			fprintf(stderr,
-				"\"ip rule add\" requires arguments.\n");
-			return -1;
-		}
 		req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
-		req.frh.action = FR_ACT_TO_TBL;
-	}
-
-	if (cmd == RTM_DELRULE && argc == 0) {
-		fprintf(stderr, "\"ip rule del\" requires arguments.\n");
-		return -1;
+		req.r.rtm_type = RTN_UNICAST;
 	}
 
 	while (argc > 0) {
 		if (strcmp(*argv, "not") == 0) {
-			req.frh.flags |= FIB_RULE_INVERT;
+			req.r.rtm_flags |= FIB_RULE_INVERT;
 		} else if (strcmp(*argv, "from") == 0) {
 			inet_prefix dst;
-
 			NEXT_ARG();
-			get_prefix(&dst, *argv, req.frh.family);
-			req.frh.src_len = dst.bitlen;
-			addattr_l(&req.n, sizeof(req), FRA_SRC,
-				  &dst.data, dst.bytelen);
+			get_prefix(&dst, *argv, req.r.rtm_family);
+			req.r.rtm_src_len = dst.bitlen;
+			addattr_l(&req.n, sizeof(req), FRA_SRC, &dst.data, dst.bytelen);
 		} else if (strcmp(*argv, "to") == 0) {
 			inet_prefix dst;
-
 			NEXT_ARG();
-			get_prefix(&dst, *argv, req.frh.family);
-			req.frh.dst_len = dst.bitlen;
-			addattr_l(&req.n, sizeof(req), FRA_DST,
-				  &dst.data, dst.bytelen);
+			get_prefix(&dst, *argv, req.r.rtm_family);
+			req.r.rtm_dst_len = dst.bitlen;
+			addattr_l(&req.n, sizeof(req), FRA_DST, &dst.data, dst.bytelen);
 		} else if (matches(*argv, "preference") == 0 ||
 			   matches(*argv, "order") == 0 ||
 			   matches(*argv, "priority") == 0) {
 			__u32 pref;
-
 			NEXT_ARG();
 			if (get_u32(&pref, *argv, 0))
 				invarg("preference value is invalid\n", *argv);
@@ -832,100 +376,64 @@
 		} else if (strcmp(*argv, "tos") == 0 ||
 			   matches(*argv, "dsfield") == 0) {
 			__u32 tos;
-
 			NEXT_ARG();
 			if (rtnl_dsfield_a2n(&tos, *argv))
 				invarg("TOS value is invalid\n", *argv);
-			req.frh.tos = tos;
+			req.r.rtm_tos = tos;
 		} else if (strcmp(*argv, "fwmark") == 0) {
 			char *slash;
 			__u32 fwmark, fwmask;
-
 			NEXT_ARG();
-
-			slash = strchr(*argv, '/');
-			if (slash != NULL)
+			if ((slash = strchr(*argv, '/')) != NULL)
 				*slash = '\0';
 			if (get_u32(&fwmark, *argv, 0))
 				invarg("fwmark value is invalid\n", *argv);
 			addattr32(&req.n, sizeof(req), FRA_FWMARK, fwmark);
 			if (slash) {
 				if (get_u32(&fwmask, slash+1, 0))
-					invarg("fwmask value is invalid\n",
-					       slash+1);
-				addattr32(&req.n, sizeof(req),
-					  FRA_FWMASK, fwmask);
+					invarg("fwmask value is invalid\n", slash+1);
+				addattr32(&req.n, sizeof(req), FRA_FWMASK, fwmask);
 			}
 		} else if (matches(*argv, "realms") == 0) {
 			__u32 realm;
-
 			NEXT_ARG();
 			if (get_rt_realms_or_raw(&realm, *argv))
 				invarg("invalid realms\n", *argv);
 			addattr32(&req.n, sizeof(req), FRA_FLOW, realm);
-		} else if (matches(*argv, "protocol") == 0) {
-			__u32 proto;
-
-			NEXT_ARG();
-			if (rtnl_rtprot_a2n(&proto, *argv))
-				invarg("\"protocol\" value is invalid\n", *argv);
-			addattr8(&req.n, sizeof(req), FRA_PROTOCOL, proto);
-		} else if (matches(*argv, "tun_id") == 0) {
-			__u64 tun_id;
-
-			NEXT_ARG();
-			if (get_be64(&tun_id, *argv, 0))
-				invarg("\"tun_id\" value is invalid\n", *argv);
-			addattr64(&req.n, sizeof(req), FRA_TUN_ID, tun_id);
 		} else if (matches(*argv, "table") == 0 ||
 			   strcmp(*argv, "lookup") == 0) {
+			__u32 tid;
 			NEXT_ARG();
 			if (rtnl_rttable_a2n(&tid, *argv))
 				invarg("invalid table ID\n", *argv);
 			if (tid < 256)
-				req.frh.table = tid;
+				req.r.rtm_table = tid;
 			else {
-				req.frh.table = RT_TABLE_UNSPEC;
+				req.r.rtm_table = RT_TABLE_UNSPEC;
 				addattr32(&req.n, sizeof(req), FRA_TABLE, tid);
 			}
 			table_ok = 1;
 		} else if (matches(*argv, "suppress_prefixlength") == 0 ||
 			   strcmp(*argv, "sup_pl") == 0) {
 			int pl;
-
 			NEXT_ARG();
 			if (get_s32(&pl, *argv, 0) || pl < 0)
-				invarg("suppress_prefixlength value is invalid\n",
-				       *argv);
-			addattr32(&req.n, sizeof(req),
-				  FRA_SUPPRESS_PREFIXLEN, pl);
+				invarg("suppress_prefixlength value is invalid\n", *argv);
+			addattr32(&req.n, sizeof(req), FRA_SUPPRESS_PREFIXLEN, pl);
 		} else if (matches(*argv, "suppress_ifgroup") == 0 ||
 			   strcmp(*argv, "sup_group") == 0) {
 			NEXT_ARG();
 			int group;
-
 			if (rtnl_group_a2n(&group, *argv))
-				invarg("Invalid \"suppress_ifgroup\" value\n",
-				       *argv);
-			addattr32(&req.n, sizeof(req),
-				  FRA_SUPPRESS_IFGROUP, group);
+				invarg("Invalid \"suppress_ifgroup\" value\n", *argv);
+			addattr32(&req.n, sizeof(req), FRA_SUPPRESS_IFGROUP, group);
 		} else if (strcmp(*argv, "dev") == 0 ||
 			   strcmp(*argv, "iif") == 0) {
 			NEXT_ARG();
-			if (check_ifname(*argv))
-				invarg("\"iif\"/\"dev\" not a valid ifname", *argv);
-			addattr_l(&req.n, sizeof(req), FRA_IFNAME,
-				  *argv, strlen(*argv)+1);
+			addattr_l(&req.n, sizeof(req), FRA_IFNAME, *argv, strlen(*argv)+1);
 		} else if (strcmp(*argv, "oif") == 0) {
 			NEXT_ARG();
-			if (check_ifname(*argv))
-				invarg("\"oif\" not a valid ifname", *argv);
-			addattr_l(&req.n, sizeof(req), FRA_OIFNAME,
-				  *argv, strlen(*argv)+1);
-		} else if (strcmp(*argv, "l3mdev") == 0) {
-			addattr8(&req.n, sizeof(req), FRA_L3MDEV, 1);
-			table_ok = 1;
-			l3mdev_rule = 1;
+			addattr_l(&req.n, sizeof(req), FRA_OIFNAME, *argv, strlen(*argv)+1);
 		} else if (strcmp(*argv, "uidrange") == 0) {
 			struct fib_rule_uid_range r;
 
@@ -938,98 +446,111 @@
 			   matches(*argv, "map-to") == 0) {
 			NEXT_ARG();
 			fprintf(stderr, "Warning: route NAT is deprecated\n");
-			addattr32(&req.n, sizeof(req), RTA_GATEWAY,
-				  get_addr32(*argv));
-			req.frh.action = RTN_NAT;
-		} else if (strcmp(*argv, "ipproto") == 0) {
-			int ipproto;
-
-			NEXT_ARG();
-			ipproto = inet_proto_a2n(*argv);
-			if (ipproto < 0)
-				invarg("Invalid \"ipproto\" value\n",
-				       *argv);
-			addattr8(&req.n, sizeof(req), FRA_IP_PROTO, ipproto);
-		} else if (strcmp(*argv, "sport") == 0) {
-			struct fib_rule_port_range r;
-			int ret = 0;
-
-			NEXT_ARG();
-			ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
-			if (ret == 1)
-				r.end = r.start;
-			else if (ret != 2)
-				invarg("invalid port range\n", *argv);
-			addattr_l(&req.n, sizeof(req), FRA_SPORT_RANGE, &r,
-				  sizeof(r));
-		} else if (strcmp(*argv, "dport") == 0) {
-			struct fib_rule_port_range r;
-			int ret = 0;
-
-			NEXT_ARG();
-			ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
-			if (ret == 1)
-				r.end = r.start;
-			else if (ret != 2)
-				invarg("invalid dport range\n", *argv);
-			addattr_l(&req.n, sizeof(req), FRA_DPORT_RANGE, &r,
-				  sizeof(r));
+			addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv));
+			req.r.rtm_type = RTN_NAT;
 		} else {
 			int type;
 
-			if (strcmp(*argv, "type") == 0)
+			if (strcmp(*argv, "type") == 0) {
 				NEXT_ARG();
-
+			}
 			if (matches(*argv, "help") == 0)
 				usage();
 			else if (matches(*argv, "goto") == 0) {
 				__u32 target;
-
 				type = FR_ACT_GOTO;
 				NEXT_ARG();
 				if (get_u32(&target, *argv, 0))
 					invarg("invalid target\n", *argv);
-				addattr32(&req.n, sizeof(req),
-					  FRA_GOTO, target);
+				addattr32(&req.n, sizeof(req), FRA_GOTO, target);
 			} else if (matches(*argv, "nop") == 0)
 				type = FR_ACT_NOP;
 			else if (rtnl_rtntype_a2n(&type, *argv))
 				invarg("Failed to parse rule type", *argv);
-			req.frh.action = type;
+			req.r.rtm_type = type;
 			table_ok = 1;
 		}
 		argc--;
 		argv++;
 	}
 
-	if (l3mdev_rule && tid != 0) {
-		fprintf(stderr,
-			"table can not be specified for l3mdev rules\n");
-		return -EINVAL;
-	}
-
-	if (req.frh.family == AF_UNSPEC)
-		req.frh.family = AF_INET;
+	if (req.r.rtm_family == AF_UNSPEC)
+		req.r.rtm_family = AF_INET;
 
 	if (!table_ok && cmd == RTM_NEWRULE)
-		req.frh.table = RT_TABLE_MAIN;
+		req.r.rtm_table = RT_TABLE_MAIN;
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		return -2;
 
 	return 0;
 }
 
+
+static int flush_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	struct rtnl_handle rth2;
+	struct rtmsg *r = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr * tb[FRA_MAX+1];
+
+	len -= NLMSG_LENGTH(sizeof(*r));
+	if (len < 0)
+		return -1;
+
+	parse_rtattr(tb, FRA_MAX, RTM_RTA(r), len);
+
+	if (tb[FRA_PRIORITY]) {
+		n->nlmsg_type = RTM_DELRULE;
+		n->nlmsg_flags = NLM_F_REQUEST;
+
+		if (rtnl_open(&rth2, 0) < 0)
+			return -1;
+
+		if (rtnl_talk(&rth2, n, NULL, 0) < 0)
+			return -2;
+
+		rtnl_close(&rth2);
+	}
+
+	return 0;
+}
+
+static int iprule_flush(int argc, char **argv)
+{
+	int af = preferred_family;
+
+	if (af == AF_UNSPEC)
+		af = AF_INET;
+
+	if (argc > 0) {
+		fprintf(stderr, "\"ip rule flush\" does not allow arguments\n");
+		return -1;
+	}
+
+	if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) {
+		perror("Cannot send dump request");
+		return 1;
+	}
+
+	if (rtnl_dump_filter(&rth, flush_rule, NULL) < 0) {
+		fprintf(stderr, "Flush terminated\n");
+		return 1;
+	}
+
+	return 0;
+}
+
 int do_iprule(int argc, char **argv)
 {
 	if (argc < 1) {
-		return iprule_list_flush_or_save(0, NULL, IPRULE_LIST);
+		return iprule_list_or_save(0, NULL, 0);
 	} else if (matches(argv[0], "list") == 0 ||
 		   matches(argv[0], "lst") == 0 ||
 		   matches(argv[0], "show") == 0) {
-		return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_LIST);
+		return iprule_list_or_save(argc-1, argv+1, 0);
 	} else if (matches(argv[0], "save") == 0) {
-		return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_SAVE);
+		return iprule_list_or_save(argc-1, argv+1, 1);
 	} else if (matches(argv[0], "restore") == 0) {
 		return iprule_restore();
 	} else if (matches(argv[0], "add") == 0) {
@@ -1037,12 +558,11 @@
 	} else if (matches(argv[0], "delete") == 0) {
 		return iprule_modify(RTM_DELRULE, argc-1, argv+1);
 	} else if (matches(argv[0], "flush") == 0) {
-		return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_FLUSH);
+		return iprule_flush(argc-1, argv+1);
 	} else if (matches(argv[0], "help") == 0)
 		usage();
 
-	fprintf(stderr,
-		"Command \"%s\" is unknown, try \"ip rule help\".\n", *argv);
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip rule help\".\n", *argv);
 	exit(-1);
 }
 
@@ -1060,8 +580,7 @@
 	case RTNL_FAMILY_IP6MR:
 		break;
 	default:
-		fprintf(stderr,
-			"Multicast rules are only supported for IPv4/IPv6, was: %i\n",
+		fprintf(stderr, "Multicast rules are only supported for IPv4/IPv6, was: %i\n",
 			preferred_family);
 		exit(-1);
 	}
diff --git a/ip/ipseg6.c b/ip/ipseg6.c
deleted file mode 100644
index 56a7699..0000000
--- a/ip/ipseg6.c
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * seg6.c "ip sr/seg6"
- *
- *	  This program is free software; you can redistribute it and/or
- *	  modify it under the terms of the GNU General Public License
- *	  version 2 as published by the Free Software Foundation;
- *
- * Author: David Lebrun <david.lebrun@uclouvain.be>
- */
-
-#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/genetlink.h>
-#include <linux/seg6_genl.h>
-#include <linux/seg6_hmac.h>
-
-#include "utils.h"
-#include "ip_common.h"
-#include "libgenl.h"
-#include "json_print.h"
-
-#define HMAC_KEY_PROMPT "Enter secret for HMAC key ID (blank to delete): "
-
-static void usage(void)
-{
-	fprintf(stderr,
-		"Usage: ip sr { COMMAND | help }\n"
-		"	   ip sr hmac show\n"
-		"	   ip sr hmac set KEYID ALGO\n"
-		"	   ip sr tunsrc show\n"
-		"	   ip sr tunsrc set ADDRESS\n"
-		"where  ALGO := { sha1 | sha256 }\n");
-	exit(-1);
-}
-
-static struct rtnl_handle grth = { .fd = -1 };
-static int genl_family = -1;
-
-#define SEG6_REQUEST(_req, _bufsiz, _cmd, _flags) \
-	GENL_REQUEST(_req, _bufsiz, genl_family, 0, \
-				SEG6_GENL_VERSION, _cmd, _flags)
-
-static struct {
-	unsigned int cmd;
-	inet_prefix addr;
-	__u32 keyid;
-	const char *pass;
-	__u8 alg_id;
-} opts;
-
-static void print_dumphmac(struct rtattr *attrs[])
-{
-	char secret[64];
-	char *algstr;
-	__u8 slen = rta_getattr_u8(attrs[SEG6_ATTR_SECRETLEN]);
-	__u8 alg_id = rta_getattr_u8(attrs[SEG6_ATTR_ALGID]);
-
-	memset(secret, 0, 64);
-
-	if (slen > 63) {
-		fprintf(stderr, "HMAC secret length %d > 63, truncated\n", slen);
-		slen = 63;
-	}
-
-	memcpy(secret, RTA_DATA(attrs[SEG6_ATTR_SECRET]), slen);
-
-	switch (alg_id) {
-	case SEG6_HMAC_ALGO_SHA1:
-		algstr = "sha1";
-		break;
-	case SEG6_HMAC_ALGO_SHA256:
-		algstr = "sha256";
-		break;
-	default:
-		algstr = "<unknown>";
-	}
-
-	print_uint(PRINT_ANY, "hmac", "hmac %u ",
-		   rta_getattr_u32(attrs[SEG6_ATTR_HMACKEYID]));
-	print_string(PRINT_ANY, "algo", "algo %s ", algstr);
-	print_string(PRINT_ANY, "secret", "secret \"%s\"\n", secret);
-}
-
-static void print_tunsrc(struct rtattr *attrs[])
-{
-	const char *dst
-		= rt_addr_n2a(AF_INET6, 16,
-			      RTA_DATA(attrs[SEG6_ATTR_DST]));
-
-	print_string(PRINT_ANY, "tunsrc",
-		     "tunsrc addr %s\n", dst);
-}
-
-static int process_msg(struct nlmsghdr *n, void *arg)
-{
-	struct rtattr *attrs[SEG6_ATTR_MAX + 1];
-	struct genlmsghdr *ghdr;
-	int len = n->nlmsg_len;
-
-	if (n->nlmsg_type != genl_family)
-		return -1;
-
-	len -= NLMSG_LENGTH(GENL_HDRLEN);
-	if (len < 0)
-		return -1;
-
-	ghdr = NLMSG_DATA(n);
-
-	parse_rtattr(attrs, SEG6_ATTR_MAX, (void *)ghdr + GENL_HDRLEN, len);
-
-	open_json_object(NULL);
-	switch (ghdr->cmd) {
-	case SEG6_CMD_DUMPHMAC:
-		print_dumphmac(attrs);
-		break;
-
-	case SEG6_CMD_GET_TUNSRC:
-		print_tunsrc(attrs);
-		break;
-	}
-	close_json_object();
-
-	return 0;
-}
-
-static int seg6_do_cmd(void)
-{
-	SEG6_REQUEST(req, 1024, opts.cmd, NLM_F_REQUEST);
-	struct nlmsghdr *answer;
-	int repl = 0, dump = 0;
-
-	if (genl_family < 0) {
-		if (rtnl_open_byproto(&grth, 0, NETLINK_GENERIC) < 0) {
-			fprintf(stderr, "Cannot open generic netlink socket\n");
-			exit(1);
-		}
-		genl_family = genl_resolve_family(&grth, SEG6_GENL_NAME);
-		if (genl_family < 0)
-			exit(1);
-		req.n.nlmsg_type = genl_family;
-	}
-
-	switch (opts.cmd) {
-	case SEG6_CMD_SETHMAC:
-	{
-		addattr32(&req.n, sizeof(req), SEG6_ATTR_HMACKEYID, opts.keyid);
-		addattr8(&req.n, sizeof(req), SEG6_ATTR_SECRETLEN,
-			 strlen(opts.pass));
-		addattr8(&req.n, sizeof(req), SEG6_ATTR_ALGID, opts.alg_id);
-		if (strlen(opts.pass))
-			addattr_l(&req.n, sizeof(req), SEG6_ATTR_SECRET,
-				  opts.pass, strlen(opts.pass));
-		break;
-	}
-	case SEG6_CMD_SET_TUNSRC:
-		addattr_l(&req.n, sizeof(req), SEG6_ATTR_DST, opts.addr.data,
-			  sizeof(struct in6_addr));
-		break;
-	case SEG6_CMD_DUMPHMAC:
-		dump = 1;
-		break;
-	case SEG6_CMD_GET_TUNSRC:
-		repl = 1;
-		break;
-	}
-
-	if (!repl && !dump) {
-		if (rtnl_talk(&grth, &req.n, NULL) < 0)
-			return -1;
-	} else if (repl) {
-		if (rtnl_talk(&grth, &req.n, &answer) < 0)
-			return -2;
-		new_json_obj(json);
-		if (process_msg(answer, stdout) < 0) {
-			fprintf(stderr, "Error parsing reply\n");
-			exit(1);
-		}
-		delete_json_obj();
-		free(answer);
-	} else {
-		req.n.nlmsg_flags |= NLM_F_DUMP;
-		req.n.nlmsg_seq = grth.dump = ++grth.seq;
-		if (rtnl_send(&grth, &req, req.n.nlmsg_len) < 0) {
-			perror("Failed to send dump request");
-			exit(1);
-		}
-
-		new_json_obj(json);
-		if (rtnl_dump_filter(&grth, process_msg, stdout) < 0) {
-			fprintf(stderr, "Dump terminated\n");
-			exit(1);
-		}
-		delete_json_obj();
-		fflush(stdout);
-	}
-
-	return 0;
-}
-
-int do_seg6(int argc, char **argv)
-{
-	if (argc < 1 || matches(*argv, "help") == 0)
-		usage();
-
-	memset(&opts, 0, sizeof(opts));
-
-	if (matches(*argv, "hmac") == 0) {
-		NEXT_ARG();
-		if (matches(*argv, "show") == 0) {
-			opts.cmd = SEG6_CMD_DUMPHMAC;
-		} else if (matches(*argv, "set") == 0) {
-			NEXT_ARG();
-			if (get_u32(&opts.keyid, *argv, 0) || opts.keyid == 0)
-				invarg("hmac KEYID value is invalid", *argv);
-			NEXT_ARG();
-			if (strcmp(*argv, "sha1") == 0) {
-				opts.alg_id = SEG6_HMAC_ALGO_SHA1;
-			} else if (strcmp(*argv, "sha256") == 0) {
-				opts.alg_id = SEG6_HMAC_ALGO_SHA256;
-			} else {
-				invarg("hmac ALGO value is invalid", *argv);
-			}
-			opts.cmd = SEG6_CMD_SETHMAC;
-			opts.pass = getpass(HMAC_KEY_PROMPT);
-		} else {
-			invarg("unknown", *argv);
-		}
-	} else if (matches(*argv, "tunsrc") == 0) {
-		NEXT_ARG();
-		if (matches(*argv, "show") == 0) {
-			opts.cmd = SEG6_CMD_GET_TUNSRC;
-		} else if (matches(*argv, "set") == 0) {
-			NEXT_ARG();
-			opts.cmd = SEG6_CMD_SET_TUNSRC;
-			get_addr(&opts.addr, *argv, AF_INET6);
-		} else {
-			invarg("unknown", *argv);
-		}
-	} else {
-		invarg("unknown", *argv);
-	}
-
-	return seg6_do_cmd();
-}
diff --git a/ip/iptoken.c b/ip/iptoken.c
index 9f35689..428f133 100644
--- a/ip/iptoken.c
+++ b/ip/iptoken.c
@@ -13,6 +13,7 @@
 #include <stdlib.h>
 #include <stdbool.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -25,7 +26,6 @@
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
-#include "json_print.h"
 
 extern struct rtnl_handle rth;
 
@@ -38,11 +38,11 @@
 
 static void usage(void)
 {
-	fprintf(stderr, "Usage: ip token [ list | set | del | get ] [ TOKEN ] [ dev DEV ]\n");
+	fprintf(stderr, "Usage: ip token [ list | set | get ] [ TOKEN ] [ dev DEV ]\n");
 	exit(-1);
 }
 
-static int print_token(struct nlmsghdr *n, void *arg)
+static int print_token(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
 	struct rtnl_dump_args *args = arg;
 	FILE *fp = args->fp;
@@ -51,6 +51,7 @@
 	int len = n->nlmsg_len;
 	struct rtattr *tb[IFLA_MAX + 1];
 	struct rtattr *ltb[IFLA_INET6_MAX + 1];
+	char abuf[256];
 
 	if (n->nlmsg_type != RTM_NEWLINK)
 		return -1;
@@ -60,9 +61,9 @@
 		return -1;
 
 	if (ifi->ifi_family != AF_INET6)
-		return 0;
+		return -1;
 	if (ifi->ifi_index == 0)
-		return 0;
+		return -1;
 	if (ifindex > 0 && ifi->ifi_index != ifindex)
 		return 0;
 	if (ifi->ifi_flags & (IFF_LOOPBACK | IFF_NOARP))
@@ -78,17 +79,13 @@
 		return -1;
 	}
 
-	open_json_object(NULL);
-	print_string(PRINT_FP, NULL, "token ", NULL);
-	print_color_string(PRINT_ANY,
-			   ifa_family_color(ifi->ifi_family),
-			   "token", "%s",
-			   format_host_rta(ifi->ifi_family, ltb[IFLA_INET6_TOKEN]));
-	print_string(PRINT_FP, NULL, " dev ", NULL);
-	print_color_string(PRINT_ANY, COLOR_IFNAME,
-			   "ifname", "%s\n",
-			   ll_index_to_name(ifi->ifi_index));
-	close_json_object();
+	fprintf(fp, "token %s ",
+		format_host(ifi->ifi_family,
+			    RTA_PAYLOAD(ltb[IFLA_INET6_TOKEN]),
+			    RTA_DATA(ltb[IFLA_INET6_TOKEN]),
+			    abuf, sizeof(abuf)));
+	fprintf(fp, "dev %s ", ll_index_to_name(ifi->ifi_index));
+	fprintf(fp, "\n");
 	fflush(fp);
 
 	return 0;
@@ -97,7 +94,10 @@
 static int iptoken_list(int argc, char **argv)
 {
 	int af = AF_INET6;
-	struct rtnl_dump_args da = { .fp = stdout };
+	struct rtnl_dump_args da;
+
+	memset(&da, 0, sizeof(da));
+	da.fp = stdout;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "dev") == 0) {
@@ -109,37 +109,37 @@
 		argc--; argv++;
 	}
 
-	if (rtnl_linkdump_req(&rth, af) < 0) {
+	if (rtnl_wilddump_request(&rth, af, RTM_GETLINK) < 0) {
 		perror("Cannot send dump request");
 		return -1;
 	}
 
-	new_json_obj(json);
 	if (rtnl_dump_filter(&rth, print_token, &da) < 0) {
-		delete_json_obj();
 		fprintf(stderr, "Dump terminated\n");
 		return -1;
 	}
-	delete_json_obj();
 
 	return 0;
 }
 
-static int iptoken_set(int argc, char **argv, bool delete)
+static int iptoken_set(int argc, char **argv)
 {
 	struct {
 		struct nlmsghdr n;
 		struct ifinfomsg ifi;
 		char buf[512];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_SETLINK,
-		.ifi.ifi_family = AF_INET6,
-	};
+	} req;
 	struct rtattr *afs, *afs6;
-	bool have_token = delete, have_dev = false;
-	inet_prefix addr = { .bytelen = 16, };
+	bool have_token = false, have_dev = false;
+	inet_prefix addr;
+
+	memset(&addr, 0, sizeof(addr));
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = RTM_SETLINK;
+	req.ifi.ifi_family = AF_INET6;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "dev") == 0) {
@@ -154,7 +154,13 @@
 			if (matches(*argv, "help") == 0)
 				usage();
 			if (!have_token) {
+				afs = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC);
+				afs6 = addattr_nest(&req.n, sizeof(req), AF_INET6);
 				get_prefix(&addr, *argv, req.ifi.ifi_family);
+				addattr_l(&req.n, sizeof(req), IFLA_INET6_TOKEN,
+					  &addr.data, addr.bytelen);
+				addattr_nest_end(&req.n, afs6);
+				addattr_nest_end(&req.n, afs);
 				have_token = true;
 			}
 		}
@@ -162,22 +168,17 @@
 	}
 
 	if (!have_token) {
-		fprintf(stderr, "Not enough information: token is required.\n");
+		fprintf(stderr, "Not enough information: token "
+			"is required.\n");
 		return -1;
 	}
 	if (!have_dev) {
-		fprintf(stderr, "Not enough information: \"dev\" argument is required.\n");
+		fprintf(stderr, "Not enough information: \"dev\" "
+			"argument is required.\n");
 		return -1;
 	}
 
-	afs = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC);
-	afs6 = addattr_nest(&req.n, sizeof(req), AF_INET6);
-	addattr_l(&req.n, sizeof(req), IFLA_INET6_TOKEN,
-		  &addr.data, addr.bytelen);
-	addattr_nest_end(&req.n, afs6);
-	addattr_nest_end(&req.n, afs);
-
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		return -2;
 
 	return 0;
@@ -195,9 +196,7 @@
 		return iptoken_list(argc - 1, argv + 1);
 	} else if (matches(argv[0], "set") == 0 ||
 		   matches(argv[0], "add") == 0) {
-		return iptoken_set(argc - 1, argv + 1, false);
-	} else if (matches(argv[0], "delete") == 0) {
-		return iptoken_set(argc - 1, argv + 1, true);
+		return iptoken_set(argc - 1, argv + 1);
 	} else if (matches(argv[0], "get") == 0) {
 		return iptoken_list(argc - 1, argv + 1);
 	} else if (matches(argv[0], "help") == 0)
diff --git a/ip/iptunnel.c b/ip/iptunnel.c
index 696f3b9..65a4e6e 100644
--- a/ip/iptunnel.c
+++ b/ip/iptunnel.c
@@ -32,19 +32,18 @@
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: ip tunnel { add | change | del | show | prl | 6rd } [ NAME ]\n"
-		"	 [ mode { ipip | gre | sit | isatap | vti } ] [ remote ADDR ] [ local ADDR ]\n"
-		"	 [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n"
-		"	 [ prl-default ADDR ] [ prl-nodefault ADDR ] [ prl-delete ADDR ]\n"
-		"	 [ 6rd-prefix ADDR ] [ 6rd-relay_prefix ADDR ] [ 6rd-reset ]\n"
-		"	 [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n"
-		"\n"
-		"Where:	NAME := STRING\n"
-		"	ADDR := { IP_ADDRESS | any }\n"
-		"	TOS  := { STRING | 00..ff | inherit | inherit/STRING | inherit/00..ff }\n"
-		"	TTL  := { 1..255 | inherit }\n"
-		"	KEY  := { DOTTED_QUAD | NUMBER }\n");
+	fprintf(stderr, "Usage: ip tunnel { add | change | del | show | prl | 6rd } [ NAME ]\n");
+	fprintf(stderr, "          [ mode { ipip | gre | sit | isatap | vti } ] [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(stderr, "          [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
+	fprintf(stderr, "          [ prl-default ADDR ] [ prl-nodefault ADDR ] [ prl-delete ADDR ]\n");
+	fprintf(stderr, "          [ 6rd-prefix ADDR ] [ 6rd-relay_prefix ADDR ] [ 6rd-reset ]\n");
+	fprintf(stderr, "          [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME := STRING\n");
+	fprintf(stderr, "       ADDR := { IP_ADDRESS | any }\n");
+	fprintf(stderr, "       TOS  := { STRING | 00..ff | inherit | inherit/STRING | inherit/00..ff }\n");
+	fprintf(stderr, "       TTL  := { 1..255 | inherit }\n");
+	fprintf(stderr, "       KEY  := { DOTTED_QUAD | NUMBER }\n");
 	exit(-1);
 }
 
@@ -61,10 +60,12 @@
 static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
 {
 	int count = 0;
-	const char *medium = NULL;
+	char medium[IFNAMSIZ];
 	int isatap = 0;
 
 	memset(p, 0, sizeof(*p));
+	memset(&medium, 0, sizeof(medium));
+
 	p->iph.version = 4;
 	p->iph.ihl = 5;
 #ifndef IP_DF
@@ -128,13 +129,19 @@
 			p->iph.frag_off = htons(IP_DF);
 		} else if (strcmp(*argv, "remote") == 0) {
 			NEXT_ARG();
-			p->iph.daddr = get_addr32(*argv);
+			if (strcmp(*argv, "any"))
+				p->iph.daddr = get_addr32(*argv);
+			else
+				p->iph.daddr = htonl(INADDR_ANY);
 		} else if (strcmp(*argv, "local") == 0) {
 			NEXT_ARG();
-			p->iph.saddr = get_addr32(*argv);
+			if (strcmp(*argv, "any"))
+				p->iph.saddr = get_addr32(*argv);
+			else
+				p->iph.saddr = htonl(INADDR_ANY);
 		} else if (strcmp(*argv, "dev") == 0) {
 			NEXT_ARG();
-			medium = *argv;
+			strncpy(medium, *argv, IFNAMSIZ - 1);
 		} else if (strcmp(*argv, "ttl") == 0 ||
 			   strcmp(*argv, "hoplimit") == 0 ||
 			   strcmp(*argv, "hlim") == 0) {
@@ -173,11 +180,11 @@
 
 			if (p->name[0])
 				duparg2("name", *argv);
-			if (get_ifname(p->name, *argv))
-				invarg("\"name\" not a valid ifname", *argv);
+			strncpy(p->name, *argv, IFNAMSIZ - 1);
 			if (cmd == SIOCCHGTUNNEL && count == 0) {
-				struct ip_tunnel_parm old_p = {};
+				struct ip_tunnel_parm old_p;
 
+				memset(&old_p, 0, sizeof(old_p));
 				if (tnl_get_ioctl(*argv, &old_p))
 					return -1;
 				*p = old_p;
@@ -212,10 +219,12 @@
 		}
 	}
 
-	if (medium) {
+	if (medium[0]) {
 		p->link = ll_name_to_index(medium);
-		if (!p->link)
-			return nodev(medium);
+		if (p->link == 0) {
+			fprintf(stderr, "Cannot find device \"%s\"\n", medium);
+			return -1;
+		}
 	}
 
 	if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
@@ -285,158 +294,190 @@
 	return tnl_del_ioctl(tnl_defname(&p) ? : p.name, p.name, &p);
 }
 
-static void print_tunnel(const void *t)
+static void print_tunnel(struct ip_tunnel_parm *p)
 {
-	const struct ip_tunnel_parm *p = t;
-	struct ip_tunnel_6rd ip6rd = {};
-	SPRINT_BUF(b1);
+	struct ip_tunnel_6rd ip6rd;
+	char s1[1024];
+	char s2[1024];
+
+	memset(&ip6rd, 0, sizeof(ip6rd));
 
 	/* Do not use format_host() for local addr,
 	 * symbolic name will not be useful.
 	 */
-	open_json_object(NULL);
-	print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", "%s: ", p->name);
-	snprintf(b1, sizeof(b1), "%s/ip", tnl_strproto(p->iph.protocol));
-	print_string(PRINT_ANY, "mode", "%s ", b1);
-	print_null(PRINT_FP, NULL, "remote ", NULL);
-	print_color_string(PRINT_ANY, COLOR_INET, "remote", "%s ",
-			   p->iph.daddr || is_json_context()
-				? format_host_r(AF_INET, 4, &p->iph.daddr, b1, sizeof(b1))
-				: "any");
-	print_null(PRINT_FP, NULL, "local ", NULL);
-	print_color_string(PRINT_ANY, COLOR_INET, "local", "%s",
-			   p->iph.saddr || is_json_context()
-				? rt_addr_n2a_r(AF_INET, 4, &p->iph.saddr, b1, sizeof(b1))
-				: "any");
+	printf("%s: %s/ip remote %s local %s",
+	       p->name,
+	       tnl_strproto(p->iph.protocol),
+	       p->iph.daddr ? format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1)) : "any",
+	       p->iph.saddr ? rt_addr_n2a(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2)) : "any");
 
 	if (p->iph.protocol == IPPROTO_IPV6 && (p->i_flags & SIT_ISATAP)) {
-		struct ip_tunnel_prl prl[16] = {};
+		struct ip_tunnel_prl prl[16];
 		int i;
 
+		memset(prl, 0, sizeof(prl));
 		prl[0].datalen = sizeof(prl) - sizeof(prl[0]);
 		prl[0].addr = htonl(INADDR_ANY);
 
-		if (!tnl_prl_ioctl(SIOCGETPRL, p->name, prl)) {
+		if (!tnl_prl_ioctl(SIOCGETPRL, p->name, prl))
 			for (i = 1; i < ARRAY_SIZE(prl); i++) {
-				if (prl[i].addr == htonl(INADDR_ANY))
-					continue;
-				if (prl[i].flags & PRL_DEFAULT)
-					print_string(PRINT_ANY, "pdr",
-						     " pdr %s",
-						     format_host(AF_INET, 4, &prl[i].addr));
-				else
-					print_string(PRINT_ANY, "pr", " pr %s",
-						     format_host(AF_INET, 4, &prl[i].addr));
+				if (prl[i].addr != htonl(INADDR_ANY)) {
+					printf(" %s %s ",
+					       (prl[i].flags & PRL_DEFAULT) ? "pdr" : "pr",
+					       format_host(AF_INET, 4, &prl[i].addr, s1, sizeof(s1)));
+				}
 			}
-		}
 	}
 
 	if (p->link) {
 		const char *n = ll_index_to_name(p->link);
 
 		if (n)
-			print_string(PRINT_ANY, "dev", " dev %s", n);
+			printf(" dev %s", n);
 	}
 
 	if (p->iph.ttl)
-		print_uint(PRINT_ANY, "ttl", " ttl %u", p->iph.ttl);
+		printf(" ttl %d", p->iph.ttl);
 	else
-		print_string(PRINT_FP, "ttl", " ttl %s", "inherit");
+		printf(" ttl inherit");
 
 	if (p->iph.tos) {
-		SPRINT_BUF(b2);
-
-		if (p->iph.tos != 1) {
-			if (!is_json_context() && p->iph.tos & 1)
-				snprintf(b2, sizeof(b2), "%s%s",
-					 p->iph.tos & 1 ? "inherit/" : "",
-					 rtnl_dsfield_n2a(p->iph.tos & ~1, b1, sizeof(b1)));
-			else
-				snprintf(b2, sizeof(b2), "%s",
-					 rtnl_dsfield_n2a(p->iph.tos, b1, sizeof(b1)));
-			print_string(PRINT_ANY, "tos", " tos %s", b2);
-		} else {
-			print_string(PRINT_FP, NULL, " tos %s", "inherit");
-		}
+		SPRINT_BUF(b1);
+		printf(" tos");
+		if (p->iph.tos & 1)
+			printf(" inherit");
+		if (p->iph.tos & ~1)
+			printf("%c%s ", p->iph.tos & 1 ? '/' : ' ',
+			       rtnl_dsfield_n2a(p->iph.tos & ~1, b1, sizeof(b1)));
 	}
 
 	if (!(p->iph.frag_off & htons(IP_DF)))
-		print_null(PRINT_ANY, "nopmtudisc", " nopmtudisc", NULL);
+		printf(" nopmtudisc");
 
 	if (p->iph.protocol == IPPROTO_IPV6 && !tnl_ioctl_get_6rd(p->name, &ip6rd) && ip6rd.prefixlen) {
-		print_string(PRINT_ANY, "6rd-prefix", " 6rd-prefix %s",
-			     inet_ntop(AF_INET6, &ip6rd.prefix, b1, sizeof(b1)));
-		print_uint(PRINT_ANY, "6rd-prefixlen", "/%u", ip6rd.prefixlen);
+		printf(" 6rd-prefix %s/%u",
+		       inet_ntop(AF_INET6, &ip6rd.prefix, s1, sizeof(s1)),
+		       ip6rd.prefixlen);
 		if (ip6rd.relay_prefix) {
-			print_string(PRINT_ANY, "6rd-relay_prefix",
-				     " 6rd-relay_prefix %s",
-				     format_host(AF_INET, 4, &ip6rd.relay_prefix));
-			print_uint(PRINT_ANY, "6rd-relay_prefixlen", "/%u",
-				   ip6rd.relay_prefixlen);
+			printf(" 6rd-relay_prefix %s/%u",
+			       format_host(AF_INET, 4, &ip6rd.relay_prefix, s1, sizeof(s1)),
+			       ip6rd.relay_prefixlen);
 		}
 	}
 
-	tnl_print_gre_flags(p->iph.protocol, p->i_flags, p->o_flags,
-			    p->i_key, p->o_key);
+	if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key)
+		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 %u", ntohl(p->i_key));
+		if (p->o_flags & GRE_KEY)
+			printf(" okey %u", ntohl(p->o_key));
+	}
 
-	close_json_object();
+	if (p->i_flags & GRE_SEQ)
+		printf("%s  Drop packets out of sequence.", _SL_);
+	if (p->i_flags & GRE_CSUM)
+		printf("%s  Checksum in received packet is required.", _SL_);
+	if (p->o_flags & GRE_SEQ)
+		printf("%s  Sequence packets on output.", _SL_);
+	if (p->o_flags & GRE_CSUM)
+		printf("%s  Checksum output packets.", _SL_);
 }
 
-
-static void ip_tunnel_parm_initialize(const struct tnl_print_nlmsg_info *info)
+static int do_tunnels_list(struct ip_tunnel_parm *p)
 {
-	struct ip_tunnel_parm *p2 = info->p2;
+	char buf[512];
+	int err = -1;
+	FILE *fp = fopen("/proc/net/dev", "r");
 
-	memset(p2, 0, sizeof(*p2));
-}
+	if (fp == NULL) {
+		perror("fopen");
+		return -1;
+	}
 
-static bool ip_tunnel_parm_match(const struct tnl_print_nlmsg_info *info)
-{
-	const struct ip_tunnel_parm *p1 = info->p1;
-	const struct ip_tunnel_parm *p2 = info->p2;
+	/* skip header lines */
+	if (!fgets(buf, sizeof(buf), fp) ||
+	    !fgets(buf, sizeof(buf), fp)) {
+		fprintf(stderr, "/proc/net/dev read error\n");
+		goto end;
+	}
 
-	return ((!p1->link || p1->link == p2->link) &&
-		(!p1->name[0] || strcmp(p1->name, p2->name) == 0) &&
-		(!p1->iph.daddr || p1->iph.daddr == p2->iph.daddr) &&
-		(!p1->iph.saddr || p1->iph.saddr == p2->iph.saddr) &&
-		(!p1->i_key || p1->i_key == p2->i_key));
+	while (fgets(buf, sizeof(buf), fp) != NULL) {
+		char name[IFNAMSIZ];
+		int index, type;
+		struct ip_tunnel_parm p1;
+		char *ptr;
+
+		buf[sizeof(buf) - 1] = 0;
+		ptr = strchr(buf, ':');
+		if (ptr == NULL ||
+		    (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
+			fprintf(stderr, "Wrong format for /proc/net/dev. Giving up.\n");
+			goto end;
+		}
+		if (p->name[0] && strcmp(p->name, name))
+			continue;
+		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;
+		}
+		if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
+			continue;
+		memset(&p1, 0, sizeof(p1));
+		if (tnl_get_ioctl(name, &p1))
+			continue;
+		if ((p->link && p1.link != p->link) ||
+		    (p->name[0] && strcmp(p1.name, p->name)) ||
+		    (p->iph.daddr && p1.iph.daddr != p->iph.daddr) ||
+		    (p->iph.saddr && p1.iph.saddr != p->iph.saddr) ||
+		    (p->i_key && p1.i_key != p->i_key))
+			continue;
+		print_tunnel(&p1);
+		if (show_stats)
+			tnl_print_stats(ptr);
+		printf("\n");
+	}
+	err = 0;
+ end:
+	fclose(fp);
+	return err;
 }
 
 static int do_show(int argc, char **argv)
 {
-	struct ip_tunnel_parm p, p1;
+	struct ip_tunnel_parm p;
 	const char *basedev;
 
+	ll_init_map(&rth);
 	if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
 		return -1;
 
 	basedev = tnl_defname(&p);
-	if (!basedev) {
-		struct tnl_print_nlmsg_info info = {
-			.p1    = &p,
-			.p2    = &p1,
-			.init  = ip_tunnel_parm_initialize,
-			.match = ip_tunnel_parm_match,
-			.print = print_tunnel,
-		};
-
-		return do_tunnels_list(&info);
-	}
+	if (!basedev)
+		return do_tunnels_list(&p);
 
 	if (tnl_get_ioctl(p.name[0] ? p.name : basedev, &p))
 		return -1;
 
 	print_tunnel(&p);
-	fputc('\n', stdout);
+	printf("\n");
 	return 0;
 }
 
 static int do_prl(int argc, char **argv)
 {
-	struct ip_tunnel_prl p = {};
+	struct ip_tunnel_prl p;
 	int count = 0;
+	int devname = 0;
 	int cmd = 0;
-	const char *medium = NULL;
+	char medium[IFNAMSIZ];
+
+	memset(&p, 0, sizeof(p));
+	memset(&medium, 0, sizeof(medium));
 
 	while (argc > 0) {
 		if (strcmp(*argv, "prl-default") == 0) {
@@ -457,9 +498,8 @@
 			count++;
 		} else if (strcmp(*argv, "dev") == 0) {
 			NEXT_ARG();
-			if (check_ifname(*argv))
-				invarg("\"dev\" not a valid ifname", *argv);
-			medium = *argv;
+			strncpy(medium, *argv, IFNAMSIZ-1);
+			devname++;
 		} else {
 			fprintf(stderr,
 				"Invalid PRL parameter \"%s\"\n", *argv);
@@ -472,7 +512,7 @@
 		}
 		argc--; argv++;
 	}
-	if (!medium) {
+	if (devname == 0) {
 		fprintf(stderr, "Must specify device\n");
 		exit(-1);
 	}
@@ -482,11 +522,15 @@
 
 static int do_6rd(int argc, char **argv)
 {
-	struct ip_tunnel_6rd ip6rd = {};
+	struct ip_tunnel_6rd ip6rd;
+	int devname = 0;
 	int cmd = 0;
-	const char *medium = NULL;
+	char medium[IFNAMSIZ];
 	inet_prefix prefix;
 
+	memset(&ip6rd, 0, sizeof(ip6rd));
+	memset(&medium, 0, sizeof(medium));
+
 	while (argc > 0) {
 		if (strcmp(*argv, "6rd-prefix") == 0) {
 			NEXT_ARG();
@@ -506,9 +550,8 @@
 			cmd = SIOCDEL6RD;
 		} else if (strcmp(*argv, "dev") == 0) {
 			NEXT_ARG();
-			if (check_ifname(*argv))
-				invarg("\"dev\" not a valid ifname", *argv);
-			medium = *argv;
+			strncpy(medium, *argv, IFNAMSIZ-1);
+			devname++;
 		} else {
 			fprintf(stderr,
 				"Invalid 6RD parameter \"%s\"\n", *argv);
@@ -516,7 +559,7 @@
 		}
 		argc--; argv++;
 	}
-	if (!medium) {
+	if (devname == 0) {
 		fprintf(stderr, "Must specify device\n");
 		exit(-1);
 	}
diff --git a/ip/iptuntap.c b/ip/iptuntap.c
index 82e3849..b9b28a1 100644
--- a/ip/iptuntap.c
+++ b/ip/iptuntap.c
@@ -20,33 +20,28 @@
 #include <sys/ioctl.h>
 #include <linux/if.h>
 #include <linux/if_tun.h>
-#include <linux/if_arp.h>
 #include <pwd.h>
 #include <grp.h>
 #include <fcntl.h>
 #include <dirent.h>
 #include <errno.h>
-#include <glob.h>
 
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
 
-static const char drv_name[] = "tun";
-
 #define TUNDEV "/dev/net/tun"
 
 static void usage(void) __attribute__((noreturn));
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: ip tuntap { add | del | show | list | lst | help } [ dev PHYS_DEV ]\n"
-		"	[ mode { tun | tap } ] [ user USER ] [ group GROUP ]\n"
-		"	[ one_queue ] [ pi ] [ vnet_hdr ] [ multi_queue ] [ name NAME ]\n"
-		"\n"
-		"Where:	USER  := { STRING | NUMBER }\n"
-		"	GROUP := { STRING | NUMBER }\n");
+	fprintf(stderr, "Usage: ip tuntap { add | del | show | list | lst | help } [ dev PHYS_DEV ] \n");
+	fprintf(stderr, "          [ mode { tun | tap } ] [ user USER ] [ group GROUP ]\n");
+	fprintf(stderr, "          [ one_queue ] [ pi ] [ vnet_hdr ] [ multi_queue ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: USER  := { STRING | NUMBER }\n");
+	fprintf(stderr, "       GROUP := { STRING | NUMBER }\n");
 	exit(-1);
 }
 
@@ -109,8 +104,7 @@
 	return ret;
 
 }
-static int parse_args(int argc, char **argv,
-		      struct ifreq *ifr, uid_t *uid, gid_t *gid)
+static int parse_args(int argc, char **argv, struct ifreq *ifr, uid_t *uid, gid_t *gid)
 {
 	int count = 0;
 
@@ -123,18 +117,18 @@
 			NEXT_ARG();
 			if (matches(*argv, "tun") == 0) {
 				if (ifr->ifr_flags & IFF_TAP) {
-					fprintf(stderr, "You managed to ask for more than one tunnel mode.\n");
+					fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
 					exit(-1);
 				}
 				ifr->ifr_flags |= IFF_TUN;
 			} else if (matches(*argv, "tap") == 0) {
 				if (ifr->ifr_flags & IFF_TUN) {
-					fprintf(stderr, "You managed to ask for more than one tunnel mode.\n");
+					fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
 					exit(-1);
 				}
 				ifr->ifr_flags |= IFF_TAP;
 			} else {
-				fprintf(stderr, "Unknown tunnel mode \"%s\"\n", *argv);
+				fprintf(stderr,"Unknown tunnel mode \"%s\"\n", *argv);
 				exit(-1);
 			}
 		} else if (uid && matches(*argv, "user") == 0) {
@@ -146,7 +140,6 @@
 				*uid = user;
 			else {
 				struct passwd *pw = getpwnam(*argv);
-
 				if (!pw) {
 					fprintf(stderr, "invalid user \"%s\"\n", *argv);
 					exit(-1);
@@ -163,7 +156,6 @@
 				*gid = group;
 			else {
 				struct group *gr = getgrnam(*argv);
-
 				if (!gr) {
 					fprintf(stderr, "invalid group \"%s\"\n", *argv);
 					exit(-1);
@@ -180,8 +172,7 @@
 			ifr->ifr_flags |= IFF_MULTI_QUEUE;
 		} else if (matches(*argv, "dev") == 0) {
 			NEXT_ARG();
-			if (get_ifname(ifr->ifr_name, *argv))
-				invarg("\"dev\" not a valid ifname", *argv);
+			strncpy(ifr->ifr_name, *argv, IFNAMSIZ-1);
 		} else {
 			if (matches(*argv, "name") == 0) {
 				NEXT_ARG();
@@ -189,8 +180,7 @@
 				usage();
 			if (ifr->ifr_name[0])
 				duparg2("name", *argv);
-			if (get_ifname(ifr->ifr_name, *argv))
-				invarg("\"name\" not a valid ifname", *argv);
+			strncpy(ifr->ifr_name, *argv, IFNAMSIZ);
 		}
 		count++;
 		argc--; argv++;
@@ -227,254 +217,91 @@
 	return tap_del_ioctl(&ifr);
 }
 
+static int read_prop(char *dev, char *prop, long *value)
+{
+	char fname[IFNAMSIZ+25], buf[80], *endp;
+	ssize_t len;
+	int fd;
+	long result;
+
+	sprintf(fname, "/sys/class/net/%s/%s", dev, prop);
+	fd = open(fname, O_RDONLY);
+	if (fd < 0) {
+		if (strcmp(prop, "tun_flags"))
+			fprintf(stderr, "open %s: %s\n", fname,
+				strerror(errno));
+		return -1;
+	}
+	len = read(fd, buf, sizeof(buf)-1);
+	close(fd);
+	if (len < 0) {
+		fprintf(stderr, "read %s: %s", fname, strerror(errno));
+		return -1;
+	}
+
+	buf[len] = 0;
+	result = strtol(buf, &endp, 0);
+	if (*endp != '\n') {
+		fprintf(stderr, "Failed to parse %s\n", fname);
+		return -1;
+	}
+	*value = result;
+	return 0;
+}
+
 static void print_flags(long flags)
 {
-	open_json_array(PRINT_JSON, "flags");
-
 	if (flags & IFF_TUN)
-		print_string(PRINT_ANY, NULL, " %s", "tun");
+		printf(" tun");
 
 	if (flags & IFF_TAP)
-		print_string(PRINT_ANY, NULL, " %s", "tap");
+		printf(" tap");
 
 	if (!(flags & IFF_NO_PI))
-		print_string(PRINT_ANY, NULL, " %s", "pi");
+		printf(" pi");
 
 	if (flags & IFF_ONE_QUEUE)
-		print_string(PRINT_ANY, NULL, " %s", "one_queue");
+		printf(" one_queue");
 
 	if (flags & IFF_VNET_HDR)
-		print_string(PRINT_ANY, NULL, " %s", "vnet_hdr");
+		printf(" vnet_hdr");
 
-	if (flags & IFF_PERSIST)
-		print_string(PRINT_ANY, NULL, " %s", "persist");
-
-	if (!(flags & IFF_NOFILTER))
-		print_string(PRINT_ANY, NULL, " %s", "filter");
-
-	flags &= ~(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE |
-		   IFF_VNET_HDR | IFF_PERSIST | IFF_NOFILTER);
+	flags &= ~(IFF_TUN|IFF_TAP|IFF_NO_PI|IFF_ONE_QUEUE|IFF_VNET_HDR);
 	if (flags)
-		print_0xhex(PRINT_ANY, NULL, "%#llx", flags);
-
-	close_json_array(PRINT_JSON, NULL);
-}
-
-static char *pid_name(pid_t pid)
-{
-	char *comm;
-	FILE *f;
-	int err;
-
-	err = asprintf(&comm, "/proc/%d/comm", pid);
-	if (err < 0)
-		return NULL;
-
-	f = fopen(comm, "r");
-	free(comm);
-	if (!f) {
-		perror("fopen");
-		return NULL;
-	}
-
-	if (fscanf(f, "%ms\n", &comm) != 1) {
-		perror("fscanf");
-		comm = NULL;
-	}
-
-
-	if (fclose(f))
-		perror("fclose");
-
-	return comm;
-}
-
-static void show_processes(const char *name)
-{
-	glob_t globbuf = { };
-	char **fd_path;
-	int err;
-
-	err = glob("/proc/[0-9]*/fd/[0-9]*", GLOB_NOSORT,
-		   NULL, &globbuf);
-	if (err)
-		return;
-
-	open_json_array(PRINT_JSON, "processes");
-
-	fd_path = globbuf.gl_pathv;
-	while (*fd_path) {
-		const char *dev_net_tun = "/dev/net/tun";
-		const size_t linkbuf_len = strlen(dev_net_tun) + 2;
-		char linkbuf[linkbuf_len], *fdinfo;
-		int pid, fd;
-		FILE *f;
-
-		if (sscanf(*fd_path, "/proc/%d/fd/%d", &pid, &fd) != 2)
-			goto next;
-
-		if (pid == getpid())
-			goto next;
-
-		err = readlink(*fd_path, linkbuf, linkbuf_len - 1);
-		if (err < 0) {
-			perror("readlink");
-			goto next;
-		}
-		linkbuf[err] = '\0';
-		if (strcmp(dev_net_tun, linkbuf))
-			goto next;
-
-		if (asprintf(&fdinfo, "/proc/%d/fdinfo/%d", pid, fd) < 0)
-			goto next;
-
-		f = fopen(fdinfo, "r");
-		free(fdinfo);
-		if (!f) {
-			perror("fopen");
-			goto next;
-		}
-
-		while (!feof(f)) {
-			char *key = NULL, *value = NULL;
-
-			err = fscanf(f, "%m[^:]: %ms\n", &key, &value);
-			if (err == EOF) {
-				if (ferror(f))
-					perror("fscanf");
-				break;
-			} else if (err == 2 &&
-				   !strcmp("iff", key) &&
-				   !strcmp(name, value)) {
-				char *pname = pid_name(pid);
-
-				print_string(PRINT_ANY, "name",
-					     "%s", pname ? : "<NULL>");
-
-				print_uint(PRINT_ANY, "pid",
-					   "(%d)", pid);
-				free(pname);
-			}
-
-			free(key);
-			free(value);
-		}
-		if (fclose(f))
-			perror("fclose");
-
-next:
-		++fd_path;
-	}
-	close_json_array(PRINT_JSON, NULL);
-
-	globfree(&globbuf);
-}
-
-static int tuntap_filter_req(struct nlmsghdr *nlh, int reqlen)
-{
-	struct rtattr *linkinfo;
-	int err;
-
-	linkinfo = addattr_nest(nlh, reqlen, IFLA_LINKINFO);
-
-	err = addattr_l(nlh, reqlen, IFLA_INFO_KIND,
-			drv_name, sizeof(drv_name) - 1);
-	if (err)
-		return err;
-
-	addattr_nest_end(nlh, linkinfo);
-
-	return 0;
-}
-
-static int print_tuntap(struct nlmsghdr *n, void *arg)
-{
-	struct ifinfomsg *ifi = NLMSG_DATA(n);
-	struct rtattr *tb[IFLA_MAX+1];
-	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
-	const char *name, *kind;
-	long flags, owner = -1, group = -1;
-
-	if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
-		return 0;
-
-	if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi)))
-		return -1;
-
-	switch (ifi->ifi_type) {
-	case ARPHRD_NONE:
-	case ARPHRD_ETHER:
-		break;
-	default:
-		return 0;
-	}
-
-	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
-
-	if (!tb[IFLA_IFNAME])
-		return 0;
-
-	if (!tb[IFLA_LINKINFO])
-		return 0;
-
-	parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
-
-	if (!linkinfo[IFLA_INFO_KIND])
-		return 0;
-
-	kind = rta_getattr_str(linkinfo[IFLA_INFO_KIND]);
-	if (strcmp(kind, drv_name))
-		return 0;
-
-	name = rta_getattr_str(tb[IFLA_IFNAME]);
-
-	if (read_prop(name, "tun_flags", &flags))
-		return 0;
-	if (read_prop(name, "owner", &owner))
-		return 0;
-	if (read_prop(name, "group", &group))
-		return 0;
-
-	open_json_object(NULL);
-	print_color_string(PRINT_ANY, COLOR_IFNAME,
-			   "ifname", "%s:", name);
-	print_flags(flags);
-	if (owner != -1)
-		print_u64(PRINT_ANY, "user",
-			   " user %ld", owner);
-	if (group != -1)
-		print_u64(PRINT_ANY, "group",
-			   " group %ld", group);
-
-	if (show_details) {
-		print_string(PRINT_FP, NULL,
-			     "%s\tAttached to processes:", _SL_);
-		show_processes(name);
-	}
-	close_json_object();
-	print_string(PRINT_FP, NULL, "%s", "\n");
-
-	return 0;
+		printf(" UNKNOWN_FLAGS:%lx", flags);
 }
 
 static int do_show(int argc, char **argv)
 {
-	if (rtnl_linkdump_req_filter_fn(&rth, AF_UNSPEC,
-					tuntap_filter_req) < 0) {
-		perror("Cannot send dump request\n");
+	DIR *dir;
+	struct dirent *d;
+	long flags, owner = -1, group = -1;
+
+	dir = opendir("/sys/class/net");
+	if (!dir) {
+		perror("opendir");
 		return -1;
 	}
+	while ((d = readdir(dir))) {
+		if (d->d_name[0] == '.' &&
+		    (d->d_name[1] == 0 || d->d_name[1] == '.'))
+			continue;
 
-	new_json_obj(json);
+		if (read_prop(d->d_name, "tun_flags", &flags))
+			continue;
 
-	if (rtnl_dump_filter(&rth, print_tuntap, NULL) < 0) {
-		fprintf(stderr, "Dump terminated\n");
-		return -1;
+		read_prop(d->d_name, "owner", &owner);
+		read_prop(d->d_name, "group", &group);
+
+		printf("%s:", d->d_name);
+		print_flags(flags);
+		if (owner != -1)
+			printf(" user %ld", owner);
+		if (group != -1)
+			printf(" group %ld", group);
+		printf("\n");
 	}
-
-	delete_json_obj();
-	fflush(stdout);
-
+	closedir(dir);
 	return 0;
 }
 
@@ -486,9 +313,9 @@
 		if (matches(*argv, "delete") == 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);
+                    matches(*argv, "lst") == 0 ||
+                    matches(*argv, "list") == 0)
+                        return do_show(argc-1, argv+1);
 		if (matches(*argv, "help") == 0)
 			usage();
 	} else
@@ -498,102 +325,3 @@
 		*argv);
 	exit(-1);
 }
-
-static void print_owner(FILE *f, uid_t uid)
-{
-	struct passwd *pw = getpwuid(uid);
-
-	if (pw)
-		print_string(PRINT_ANY, "user", "user %s ", pw->pw_name);
-	else
-		print_uint(PRINT_ANY, "user", "user %u ", uid);
-}
-
-static void print_group(FILE *f, gid_t gid)
-{
-	struct group *group = getgrgid(gid);
-
-	if (group)
-		print_string(PRINT_ANY, "group", "group %s ", group->gr_name);
-	else
-		print_uint(PRINT_ANY, "group", "group %u ", gid);
-}
-
-static void print_mq(FILE *f, struct rtattr *tb[])
-{
-	if (!tb[IFLA_TUN_MULTI_QUEUE] ||
-	    !rta_getattr_u8(tb[IFLA_TUN_MULTI_QUEUE])) {
-		if (is_json_context())
-			print_bool(PRINT_JSON, "multi_queue", NULL, false);
-		return;
-	}
-
-	print_bool(PRINT_ANY, "multi_queue", "multi_queue ", true);
-
-	if (tb[IFLA_TUN_NUM_QUEUES]) {
-		print_uint(PRINT_ANY, "numqueues", "numqueues %u ",
-			   rta_getattr_u32(tb[IFLA_TUN_NUM_QUEUES]));
-	}
-
-	if (tb[IFLA_TUN_NUM_DISABLED_QUEUES]) {
-		print_uint(PRINT_ANY, "numdisabled", "numdisabled %u ",
-			   rta_getattr_u32(tb[IFLA_TUN_NUM_DISABLED_QUEUES]));
-	}
-}
-
-static void print_onoff(FILE *f, const char *flag, __u8 val)
-{
-	if (is_json_context())
-		print_bool(PRINT_JSON, flag, NULL, !!val);
-	else
-		fprintf(f, "%s %s ", flag, val ? "on" : "off");
-}
-
-static void print_type(FILE *f, __u8 type)
-{
-	SPRINT_BUF(buf);
-	const char *str = buf;
-
-	if (type == IFF_TUN)
-		str = "tun";
-	else if (type == IFF_TAP)
-		str = "tap";
-	else
-		snprintf(buf, sizeof(buf), "UNKNOWN:%hhu", type);
-
-	print_string(PRINT_ANY, "type", "type %s ", str);
-}
-
-static void tun_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
-{
-	if (!tb)
-		return;
-
-	if (tb[IFLA_TUN_TYPE])
-		print_type(f, rta_getattr_u8(tb[IFLA_TUN_TYPE]));
-
-	if (tb[IFLA_TUN_PI])
-		print_onoff(f, "pi", rta_getattr_u8(tb[IFLA_TUN_PI]));
-
-	if (tb[IFLA_TUN_VNET_HDR]) {
-		print_onoff(f, "vnet_hdr",
-			    rta_getattr_u8(tb[IFLA_TUN_VNET_HDR]));
-	}
-
-	print_mq(f, tb);
-
-	if (tb[IFLA_TUN_PERSIST])
-		print_onoff(f, "persist", rta_getattr_u8(tb[IFLA_TUN_PERSIST]));
-
-	if (tb[IFLA_TUN_OWNER])
-		print_owner(f, rta_getattr_u32(tb[IFLA_TUN_OWNER]));
-
-	if (tb[IFLA_TUN_GROUP])
-		print_group(f, rta_getattr_u32(tb[IFLA_TUN_GROUP]));
-}
-
-struct link_util tun_link_util = {
-	.id = "tun",
-	.maxattr = IFLA_TUN_MAX,
-	.print_opt = tun_print_opt,
-};
diff --git a/ip/ipvrf.c b/ip/ipvrf.c
deleted file mode 100644
index 43366f6..0000000
--- a/ip/ipvrf.c
+++ /dev/null
@@ -1,643 +0,0 @@
-/*
- * ipvrf.c	"ip vrf"
- *
- *		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:	David Ahern <dsa@cumulusnetworks.com>
- *
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/mount.h>
-#include <linux/bpf.h>
-#include <linux/if.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <dirent.h>
-#include <errno.h>
-#include <limits.h>
-
-#include "rt_names.h"
-#include "utils.h"
-#include "ip_common.h"
-#include "bpf_util.h"
-
-#define CGRP_PROC_FILE  "/cgroup.procs"
-
-static struct link_filter vrf_filter;
-
-static void usage(void)
-{
-	fprintf(stderr,
-		"Usage:	ip vrf show [NAME] ...\n"
-		"	ip vrf exec [NAME] cmd ...\n"
-		"	ip vrf identify [PID]\n"
-		"	ip vrf pids [NAME]\n");
-
-	exit(-1);
-}
-
-/*
- * parse process based cgroup file looking for PATH/vrf/NAME where
- * NAME is the name of the vrf the process is associated with
- */
-static int vrf_identify(pid_t pid, char *name, size_t len)
-{
-	char path[PATH_MAX];
-	char buf[4096];
-	char *vrf, *end;
-	FILE *fp;
-
-	snprintf(path, sizeof(path), "/proc/%d/cgroup", pid);
-	fp = fopen(path, "r");
-	if (!fp)
-		return -1;
-
-	memset(name, 0, len);
-
-	while (fgets(buf, sizeof(buf), fp)) {
-		/* want the controller-less cgroup */
-		if (strstr(buf, "::/") == NULL)
-			continue;
-
-		vrf = strstr(buf, "/vrf/");
-		if (vrf) {
-			vrf += 5;  /* skip past "/vrf/" */
-			end = strchr(vrf, '\n');
-			if (end)
-				*end = '\0';
-
-			strlcpy(name, vrf, len);
-			break;
-		}
-	}
-
-	fclose(fp);
-
-	return 0;
-}
-
-static int ipvrf_identify(int argc, char **argv)
-{
-	char vrf[32];
-	int rc;
-	unsigned int pid;
-
-	if (argc < 1)
-		pid = getpid();
-	else if (argc > 1)
-		invarg("Extra arguments specified\n", argv[1]);
-	else if (get_unsigned(&pid, argv[0], 10))
-		invarg("Invalid pid\n", argv[0]);
-
-	rc = vrf_identify(pid, vrf, sizeof(vrf));
-	if (!rc) {
-		if (vrf[0] != '\0')
-			printf("%s\n", vrf);
-	} else {
-		fprintf(stderr, "Failed to lookup vrf association: %s\n",
-			strerror(errno));
-	}
-
-	return rc;
-}
-
-/* read PATH/vrf/NAME/cgroup.procs file */
-static void read_cgroup_pids(const char *base_path, char *name)
-{
-	char path[PATH_MAX];
-	char buf[4096];
-	FILE *fp;
-
-	if (snprintf(path, sizeof(path), "%s/vrf/%s%s",
-		     base_path, name, CGRP_PROC_FILE) >= sizeof(path))
-		return;
-
-	fp = fopen(path, "r");
-	if (!fp)
-		return; /* no cgroup file, nothing to show */
-
-	/* dump contents (pids) of cgroup.procs */
-	while (fgets(buf, sizeof(buf), fp)) {
-		char *nl, comm[32];
-
-		nl = strchr(buf, '\n');
-		if (nl)
-			*nl = '\0';
-
-		if (get_command_name(buf, comm, sizeof(comm)))
-			strcpy(comm, "<terminated?>");
-
-		printf("%5s  %s\n", buf, comm);
-	}
-
-	fclose(fp);
-}
-
-/* recurse path looking for PATH[/NETNS]/vrf/NAME */
-static int recurse_dir(char *base_path, char *name, const char *netns)
-{
-	char path[PATH_MAX];
-	struct dirent *de;
-	struct stat fstat;
-	int rc;
-	DIR *d;
-
-	d = opendir(base_path);
-	if (!d)
-		return -1;
-
-	while ((de = readdir(d)) != NULL) {
-		if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-			continue;
-
-		if (!strcmp(de->d_name, "vrf")) {
-			const char *pdir = strrchr(base_path, '/');
-
-			/* found a 'vrf' directory. if it is for the given
-			 * namespace then dump the cgroup pids
-			 */
-			if (*netns == '\0' ||
-			    (pdir && !strcmp(pdir+1, netns)))
-				read_cgroup_pids(base_path, name);
-
-			continue;
-		}
-
-		/* is this a subdir that needs to be walked */
-		if (snprintf(path, sizeof(path), "%s/%s",
-			     base_path, de->d_name) >= sizeof(path))
-			continue;
-
-		if (lstat(path, &fstat) < 0)
-			continue;
-
-		if (S_ISDIR(fstat.st_mode)) {
-			rc = recurse_dir(path, name, netns);
-			if (rc != 0)
-				goto out;
-		}
-	}
-
-	rc = 0;
-out:
-	closedir(d);
-
-	return rc;
-}
-
-static int ipvrf_get_netns(char *netns, int len)
-{
-	if (netns_identify_pid("self", netns, len-3)) {
-		fprintf(stderr, "Failed to get name of network namespace: %s\n",
-			strerror(errno));
-		return -1;
-	}
-
-	if (*netns != '\0')
-		strcat(netns, "-ns");
-
-	return 0;
-}
-
-static int ipvrf_pids(int argc, char **argv)
-{
-	char *mnt, *vrf;
-	char netns[256];
-	int ret = -1;
-
-	if (argc != 1) {
-		fprintf(stderr, "Invalid arguments\n");
-		return -1;
-	}
-
-	vrf = argv[0];
-	if (!name_is_vrf(vrf)) {
-		fprintf(stderr, "Invalid VRF name\n");
-		return -1;
-	}
-
-	mnt = find_cgroup2_mount();
-	if (!mnt)
-		return -1;
-
-	if (ipvrf_get_netns(netns, sizeof(netns)) < 0)
-		goto out;
-
-	ret = recurse_dir(mnt, vrf, netns);
-
-out:
-	free(mnt);
-
-	return ret;
-}
-
-/* load BPF program to set sk_bound_dev_if for sockets */
-static char bpf_log_buf[256*1024];
-
-static int prog_load(int idx)
-{
-	struct bpf_insn prog[] = {
-		BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
-		BPF_MOV64_IMM(BPF_REG_3, idx),
-		BPF_MOV64_IMM(BPF_REG_2,
-			      offsetof(struct bpf_sock, bound_dev_if)),
-		BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3,
-			    offsetof(struct bpf_sock, bound_dev_if)),
-		BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */
-		BPF_EXIT_INSN(),
-	};
-
-	return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
-			     "GPL", bpf_log_buf, sizeof(bpf_log_buf));
-}
-
-static int vrf_configure_cgroup(const char *path, int ifindex)
-{
-	int rc = -1, cg_fd, prog_fd = -1;
-
-	cg_fd = open(path, O_DIRECTORY | O_RDONLY);
-	if (cg_fd < 0) {
-		fprintf(stderr,
-			"Failed to open cgroup path: '%s'\n",
-			strerror(errno));
-		goto out;
-	}
-
-	/*
-	 * Load bpf program into kernel and attach to cgroup to affect
-	 * socket creates
-	 */
-	prog_fd = prog_load(ifindex);
-	if (prog_fd < 0) {
-		fprintf(stderr, "Failed to load BPF prog: '%s'\n",
-			strerror(errno));
-
-		if (errno != EPERM) {
-			fprintf(stderr,
-				"Kernel compiled with CGROUP_BPF enabled?\n");
-		}
-		goto out;
-	}
-
-	if (bpf_prog_attach_fd(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE)) {
-		fprintf(stderr, "Failed to attach prog to cgroup: '%s'\n",
-			strerror(errno));
-		goto out;
-	}
-
-	rc = 0;
-out:
-	close(cg_fd);
-	close(prog_fd);
-
-	return rc;
-}
-
-/* get base path for controller-less cgroup for a process.
- * path returned does not include /vrf/NAME if it exists
- */
-static int vrf_path(char *vpath, size_t len)
-{
-	char path[PATH_MAX];
-	char buf[4096];
-	char *vrf;
-	FILE *fp;
-
-	snprintf(path, sizeof(path), "/proc/%d/cgroup", getpid());
-	fp = fopen(path, "r");
-	if (!fp)
-		return -1;
-
-	vpath[0] = '\0';
-
-	while (fgets(buf, sizeof(buf), fp)) {
-		char *start, *nl;
-
-		start = strstr(buf, "::/");
-		if (!start)
-			continue;
-
-		/* advance past '::' */
-		start += 2;
-
-		nl = strchr(start, '\n');
-		if (nl)
-			*nl = '\0';
-
-		vrf = strstr(start, "/vrf");
-		if (vrf)
-			*vrf = '\0';
-
-		strlcpy(vpath, start, len);
-
-		/* if vrf path is just / then return nothing */
-		if (!strcmp(vpath, "/"))
-			vpath[0] = '\0';
-
-		break;
-	}
-
-	fclose(fp);
-
-	return 0;
-}
-
-static int vrf_switch(const char *name)
-{
-	char path[PATH_MAX], *mnt, pid[16];
-	char vpath[PATH_MAX], netns[256];
-	int ifindex = 0;
-	int rc = -1, len, fd = -1;
-
-	if (strcmp(name, "default")) {
-		ifindex = name_is_vrf(name);
-		if (!ifindex) {
-			fprintf(stderr, "Invalid VRF name\n");
-			return -1;
-		}
-	}
-
-	mnt = find_cgroup2_mount();
-	if (!mnt)
-		return -1;
-
-	/* -1 on length to add '/' to the end */
-	if (ipvrf_get_netns(netns, sizeof(netns) - 1) < 0)
-		goto out;
-
-	if (vrf_path(vpath, sizeof(vpath)) < 0) {
-		fprintf(stderr, "Failed to get base cgroup path: %s\n",
-			strerror(errno));
-		goto out;
-	}
-
-	/* if path already ends in netns then don't add it again */
-	if (*netns != '\0') {
-		char *pdir = strrchr(vpath, '/');
-
-		if (!pdir)
-			pdir = vpath;
-		else
-			pdir++;
-
-		if (strcmp(pdir, netns) == 0)
-			*pdir = '\0';
-
-		strcat(netns, "/");
-	}
-
-	/* path to cgroup; make sure buffer has room to cat "/cgroup.procs"
-	 * to the end of the path
-	 */
-	len = snprintf(path, sizeof(path) - sizeof(CGRP_PROC_FILE),
-		       "%s%s/%svrf/%s",
-		       mnt, vpath, netns, ifindex ? name : "");
-	if (len > sizeof(path) - sizeof(CGRP_PROC_FILE)) {
-		fprintf(stderr, "Invalid path to cgroup2 mount\n");
-		goto out;
-	}
-
-	if (make_path(path, 0755)) {
-		fprintf(stderr, "Failed to setup vrf cgroup2 directory\n");
-		goto out;
-	}
-
-	if (ifindex && vrf_configure_cgroup(path, ifindex))
-		goto out;
-
-	/*
-	 * write pid to cgroup.procs making process part of cgroup
-	 */
-	strcat(path, CGRP_PROC_FILE);
-	fd = open(path, O_RDWR | O_APPEND);
-	if (fd < 0) {
-		fprintf(stderr, "Failed to open cgroups.procs file: %s.\n",
-			strerror(errno));
-		goto out;
-	}
-
-	snprintf(pid, sizeof(pid), "%d", getpid());
-	if (write(fd, pid, strlen(pid)) < 0) {
-		fprintf(stderr, "Failed to join cgroup\n");
-		goto out2;
-	}
-
-	rc = 0;
-out2:
-	close(fd);
-out:
-	free(mnt);
-
-	drop_cap();
-
-	return rc;
-}
-
-static int do_switch(void *arg)
-{
-	char *vrf = arg;
-
-	return vrf_switch(vrf);
-}
-
-static int ipvrf_exec(int argc, char **argv)
-{
-	if (argc < 1) {
-		fprintf(stderr, "No VRF name specified\n");
-		return -1;
-	}
-	if (argc < 2) {
-		fprintf(stderr, "No command specified\n");
-		return -1;
-	}
-
-	return -cmd_exec(argv[1], argv + 1, !!batch_mode, do_switch, argv[0]);
-}
-
-/* reset VRF association of current process to default VRF;
- * used by netns_exec
- */
-void vrf_reset(void)
-{
-	char vrf[32];
-
-	if (vrf_identify(getpid(), vrf, sizeof(vrf)) ||
-	    (vrf[0] == '\0'))
-		return;
-
-	vrf_switch("default");
-}
-
-static int ipvrf_filter_req(struct nlmsghdr *nlh, int reqlen)
-{
-	struct rtattr *linkinfo;
-	int err;
-
-	if (vrf_filter.kind) {
-		linkinfo = addattr_nest(nlh, reqlen, IFLA_LINKINFO);
-
-		err = addattr_l(nlh, reqlen, IFLA_INFO_KIND, vrf_filter.kind,
-				strlen(vrf_filter.kind));
-		if (err)
-			return err;
-
-		addattr_nest_end(nlh, linkinfo);
-	}
-
-	return 0;
-}
-
-/* input arg is linkinfo */
-static __u32 vrf_table_linkinfo(struct rtattr *li[])
-{
-	struct rtattr *attr[IFLA_VRF_MAX + 1];
-
-	if (li[IFLA_INFO_DATA]) {
-		parse_rtattr_nested(attr, IFLA_VRF_MAX, li[IFLA_INFO_DATA]);
-
-		if (attr[IFLA_VRF_TABLE])
-			return rta_getattr_u32(attr[IFLA_VRF_TABLE]);
-	}
-
-	return 0;
-}
-
-static int ipvrf_print(struct nlmsghdr *n)
-{
-	struct ifinfomsg *ifi = NLMSG_DATA(n);
-	struct rtattr *tb[IFLA_MAX+1];
-	struct rtattr *li[IFLA_INFO_MAX+1];
-	int len = n->nlmsg_len;
-	const char *name;
-	__u32 tb_id;
-
-	len -= NLMSG_LENGTH(sizeof(*ifi));
-	if (len < 0)
-		return 0;
-
-	if (vrf_filter.ifindex && vrf_filter.ifindex != ifi->ifi_index)
-		return 0;
-
-	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
-
-	/* kernel does not support filter by master device */
-	if (tb[IFLA_MASTER]) {
-		int master = *(int *)RTA_DATA(tb[IFLA_MASTER]);
-
-		if (vrf_filter.master && master != vrf_filter.master)
-			return 0;
-	}
-
-	if (!tb[IFLA_IFNAME]) {
-		fprintf(stderr,
-			"BUG: device with ifindex %d has nil ifname\n",
-			ifi->ifi_index);
-		return 0;
-	}
-	name = rta_getattr_str(tb[IFLA_IFNAME]);
-
-	/* missing LINKINFO means not VRF. e.g., kernel does not
-	 * support filtering on kind, so userspace needs to handle
-	 */
-	if (!tb[IFLA_LINKINFO])
-		return 0;
-
-	parse_rtattr_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
-
-	if (!li[IFLA_INFO_KIND])
-		return 0;
-
-	if (strcmp(RTA_DATA(li[IFLA_INFO_KIND]), "vrf"))
-		return 0;
-
-	tb_id = vrf_table_linkinfo(li);
-	if (!tb_id) {
-		fprintf(stderr,
-			"BUG: VRF %s is missing table id\n", name);
-		return 0;
-	}
-
-	printf("%-16s %5u", name, tb_id);
-
-	printf("\n");
-	return 1;
-}
-
-static int ipvrf_show(int argc, char **argv)
-{
-	struct nlmsg_chain linfo = { NULL, NULL};
-	int rc = 0;
-
-	vrf_filter.kind = "vrf";
-
-	if (argc > 1)
-		usage();
-
-	if (argc == 1) {
-		__u32 tb_id;
-
-		tb_id = ipvrf_get_table(argv[0]);
-		if (!tb_id) {
-			fprintf(stderr, "Invalid VRF\n");
-			return 1;
-		}
-		printf("%s %u\n", argv[0], tb_id);
-		return 0;
-	}
-
-	if (ip_link_list(ipvrf_filter_req, &linfo) == 0) {
-		struct nlmsg_list *l;
-		unsigned nvrf = 0;
-		int n;
-
-		n = printf("%-16s  %5s\n", "Name", "Table");
-		printf("%.*s\n", n-1, "-----------------------");
-		for (l = linfo.head; l; l = l->next)
-			nvrf += ipvrf_print(&l->h);
-
-		if (!nvrf)
-			printf("No VRF has been configured\n");
-	} else
-		rc = 1;
-
-	free_nlmsg_chain(&linfo);
-
-	return rc;
-}
-
-int do_ipvrf(int argc, char **argv)
-{
-	if (argc == 0)
-		return ipvrf_show(0, NULL);
-
-	if (matches(*argv, "identify") == 0)
-		return ipvrf_identify(argc-1, argv+1);
-
-	if (matches(*argv, "pids") == 0)
-		return ipvrf_pids(argc-1, argv+1);
-
-	if (matches(*argv, "exec") == 0)
-		return ipvrf_exec(argc-1, argv+1);
-
-	if (matches(*argv, "show") == 0 ||
-	    matches(*argv, "lst") == 0 ||
-	    matches(*argv, "list") == 0)
-		return ipvrf_show(argc-1, argv+1);
-
-	if (matches(*argv, "help") == 0)
-		usage();
-
-	fprintf(stderr, "Command \"%s\" is unknown, try \"ip vrf help\".\n",
-		*argv);
-
-	exit(-1);
-}
diff --git a/ip/ipxfrm.c b/ip/ipxfrm.c
index 32f5609..e583abf 100644
--- a/ip/ipxfrm.c
+++ b/ip/ipxfrm.c
@@ -40,6 +40,17 @@
 #include "ip_common.h"
 
 #define STRBUF_SIZE	(128)
+#define STRBUF_CAT(buf, str) \
+	do { \
+		int rest = sizeof(buf) - 1 - strlen(buf); \
+		if (rest > 0) { \
+			int len = strlen(str); \
+			if (len > rest) \
+				len = rest; \
+			strncat(buf, str, len); \
+			buf[sizeof(buf) - 1] = '\0'; \
+		} \
+	} while(0);
 
 struct xfrm_filter filter;
 
@@ -100,7 +111,7 @@
 	int t_type;
 };
 
-static const struct typeent xfrmproto_types[] = {
+static const struct typeent xfrmproto_types[]= {
 	{ "esp", IPPROTO_ESP }, { "ah", IPPROTO_AH }, { "comp", IPPROTO_COMP },
 	{ "route2", IPPROTO_ROUTING }, { "hao", IPPROTO_DSTOPTS },
 	{ "ipsec-any", IPSEC_PROTO_ANY },
@@ -113,7 +124,6 @@
 
 	for (i = 0; ; i++) {
 		const struct typeent *t = &xfrmproto_types[i];
-
 		if (!t->t_name || t->t_type == -1)
 			break;
 
@@ -131,7 +141,6 @@
 
 	for (i = 0; ; i++) {
 		const struct typeent *t = &xfrmproto_types[i];
-
 		if (!t->t_name || t->t_type == -1)
 			break;
 
@@ -143,7 +152,7 @@
 	return str;
 }
 
-static const struct typeent algo_types[] = {
+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 },
@@ -156,7 +165,6 @@
 
 	for (i = 0; ; i++) {
 		const struct typeent *t = &algo_types[i];
-
 		if (!t->t_name || t->t_type == -1)
 			break;
 
@@ -174,7 +182,6 @@
 
 	for (i = 0; ; i++) {
 		const struct typeent *t = &algo_types[i];
-
 		if (!t->t_name || t->t_type == -1)
 			break;
 
@@ -186,7 +193,7 @@
 	return str;
 }
 
-static const char *strxf_mask8(__u8 mask)
+const char *strxf_mask8(__u8 mask)
 {
 	static char str[16];
 	const int sn = sizeof(mask) * 8 - 1;
@@ -209,7 +216,7 @@
 	return str;
 }
 
-static const char *strxf_share(__u8 share)
+const char *strxf_share(__u8 share)
 {
 	static char str[32];
 
@@ -270,15 +277,21 @@
 	return str;
 }
 
-static void xfrm_id_info_print(xfrm_address_t *saddr, struct xfrm_id *id,
+void xfrm_id_info_print(xfrm_address_t *saddr, struct xfrm_id *id,
 			__u8 mode, __u32 reqid, __u16 family, int force_spi,
 			FILE *fp, const char *prefix, const char *title)
 {
+	char abuf[256];
+
 	if (title)
 		fputs(title, fp);
 
-	fprintf(fp, "src %s ", rt_addr_n2a(family, sizeof(*saddr), saddr));
-	fprintf(fp, "dst %s", rt_addr_n2a(family, sizeof(id->daddr), &id->daddr));
+	memset(abuf, '\0', sizeof(abuf));
+	fprintf(fp, "src %s ", rt_addr_n2a(family, sizeof(*saddr),
+					   saddr, abuf, sizeof(abuf)));
+	memset(abuf, '\0', sizeof(abuf));
+	fprintf(fp, "dst %s", rt_addr_n2a(family, sizeof(id->daddr),
+					  &id->daddr, abuf, sizeof(abuf)));
 	fprintf(fp, "%s", _SL_);
 
 	if (prefix)
@@ -289,7 +302,6 @@
 
 	if (show_stats > 0 || force_spi || id->spi) {
 		__u32 spi = ntohl(id->spi);
-
 		fprintf(fp, "spi 0x%08x", spi);
 		if (show_stats > 0)
 			fprintf(fp, "(%u)", spi);
@@ -328,7 +340,6 @@
 static const char *strxf_limit(__u64 limit)
 {
 	static char str[32];
-
 	if (limit == XFRM_INF)
 		strcpy(str, "(INF)");
 	else
@@ -337,8 +348,7 @@
 	return str;
 }
 
-static void xfrm_stats_print(struct xfrm_stats *s, FILE *fp,
-			     const char *prefix)
+void xfrm_stats_print(struct xfrm_stats *s, FILE *fp, const char *prefix)
 {
 	if (prefix)
 		fputs(prefix, fp);
@@ -372,14 +382,14 @@
 	return str;
 }
 
-static void xfrm_lifetime_print(struct xfrm_lifetime_cfg *cfg,
+void xfrm_lifetime_print(struct xfrm_lifetime_cfg *cfg,
 			 struct xfrm_lifetime_cur *cur,
 			 FILE *fp, const char *prefix)
 {
 	if (cfg) {
 		if (prefix)
 			fputs(prefix, fp);
-		fprintf(fp, "lifetime config:%s", _SL_);
+		fprintf(fp, "lifetime config:%s",_SL_);
 
 		if (prefix)
 			fputs(prefix, fp);
@@ -431,6 +441,7 @@
 void xfrm_selector_print(struct xfrm_selector *sel, __u16 family,
 			 FILE *fp, const char *prefix)
 {
+	char abuf[256];
 	__u16 f;
 
 	f = sel->family;
@@ -442,12 +453,16 @@
 	if (prefix)
 		fputs(prefix, fp);
 
+	memset(abuf, '\0', sizeof(abuf));
 	fprintf(fp, "src %s/%u ",
-		rt_addr_n2a(f, sizeof(sel->saddr), &sel->saddr),
+		rt_addr_n2a(f, sizeof(sel->saddr), &sel->saddr,
+			    abuf, sizeof(abuf)),
 		sel->prefixlen_s);
 
+	memset(abuf, '\0', sizeof(abuf));
 	fprintf(fp, "dst %s/%u ",
-		rt_addr_n2a(f, sizeof(sel->daddr), &sel->daddr),
+		rt_addr_n2a(f, sizeof(sel->daddr), &sel->daddr,
+			    abuf, sizeof(abuf)),
 		sel->prefixlen_d);
 
 	if (sel->proto)
@@ -497,8 +512,7 @@
 }
 
 static void __xfrm_algo_print(struct xfrm_algo *algo, int type, int len,
-			      FILE *fp, const char *prefix, int newline,
-			      bool nokeys)
+			      FILE *fp, const char *prefix, int newline)
 {
 	int keylen;
 	int i;
@@ -522,11 +536,9 @@
 		goto fin;
 	}
 
-	if (nokeys)
-		fprintf(fp, "<<Keys hidden>>");
-	else if (keylen > 0) {
+	if (keylen > 0) {
 		fprintf(fp, "0x");
-		for (i = 0; i < keylen; i++)
+		for (i = 0; i < keylen; i ++)
 			fprintf(fp, "%.2x", (unsigned char)algo->alg_key[i]);
 
 		if (show_stats > 0)
@@ -539,13 +551,13 @@
 }
 
 static inline void xfrm_algo_print(struct xfrm_algo *algo, int type, int len,
-				   FILE *fp, const char *prefix, bool nokeys)
+				   FILE *fp, const char *prefix)
 {
-	return __xfrm_algo_print(algo, type, len, fp, prefix, 1, nokeys);
+	return __xfrm_algo_print(algo, type, len, fp, prefix, 1);
 }
 
 static void xfrm_aead_print(struct xfrm_algo_aead *algo, int len,
-			    FILE *fp, const char *prefix, bool nokeys)
+			    FILE *fp, const char *prefix)
 {
 	struct xfrm_algo *base_algo = alloca(sizeof(*base_algo) + algo->alg_key_len / 8);
 
@@ -553,8 +565,7 @@
 	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_AEAD, len, fp, prefix, 0,
-			  nokeys);
+	__xfrm_algo_print(base_algo, XFRMA_ALG_AEAD, len, fp, prefix, 0);
 
 	fprintf(fp, " %d", algo->alg_icv_len);
 
@@ -562,7 +573,7 @@
 }
 
 static void xfrm_auth_trunc_print(struct xfrm_algo_auth *algo, int len,
-				  FILE *fp, const char *prefix, bool nokeys)
+				  FILE *fp, const char *prefix)
 {
 	struct xfrm_algo *base_algo = alloca(sizeof(*base_algo) + algo->alg_key_len / 8);
 
@@ -570,8 +581,7 @@
 	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,
-			  nokeys);
+	__xfrm_algo_print(base_algo, XFRMA_ALG_AUTH_TRUNC, len, fp, prefix, 0);
 
 	fprintf(fp, " %d", algo->alg_trunc_len);
 
@@ -643,13 +653,6 @@
 	}
 }
 
-static void xfrm_output_mark_print(struct rtattr *tb[], FILE *fp)
-{
-	__u32 output_mark = rta_getattr_u32(tb[XFRMA_OUTPUT_MARK]);
-
-	fprintf(fp, "output-mark 0x%x", output_mark);
-}
-
 int xfrm_parse_mark(struct xfrm_mark *mark, int *argcp, char ***argvp)
 {
 	int argc = *argcp;
@@ -684,61 +687,48 @@
 }
 
 void xfrm_xfrma_print(struct rtattr *tb[], __u16 family,
-		      FILE *fp, const char *prefix, bool nokeys)
+		      FILE *fp, const char *prefix)
 {
 	if (tb[XFRMA_MARK]) {
 		struct rtattr *rta = tb[XFRMA_MARK];
-		struct xfrm_mark *m = RTA_DATA(rta);
-
-		fprintf(fp, "\tmark %#x/%#x ", m->v, m->m);
-
-		if (tb[XFRMA_OUTPUT_MARK])
-			xfrm_output_mark_print(tb, fp);
-		fprintf(fp, "%s", _SL_);
-	} else if (tb[XFRMA_OUTPUT_MARK]) {
-		fprintf(fp, "\t");
-
-		xfrm_output_mark_print(tb, fp);
+		struct xfrm_mark *m = (struct xfrm_mark *) RTA_DATA(rta);
+		fprintf(fp, "\tmark %#x/%#x", m->v, m->m);
 		fprintf(fp, "%s", _SL_);
 	}
 
 	if (tb[XFRMA_ALG_AUTH] && !tb[XFRMA_ALG_AUTH_TRUNC]) {
 		struct rtattr *rta = tb[XFRMA_ALG_AUTH];
-
-		xfrm_algo_print(RTA_DATA(rta), XFRMA_ALG_AUTH, RTA_PAYLOAD(rta),
-				fp, prefix, nokeys);
+		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(RTA_DATA(rta), RTA_PAYLOAD(rta), fp,
-				      prefix, nokeys);
+		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(RTA_DATA(rta), RTA_PAYLOAD(rta), fp, prefix,
-				nokeys);
+		xfrm_aead_print((struct xfrm_algo_aead *)RTA_DATA(rta),
+				RTA_PAYLOAD(rta), fp, prefix);
 	}
 
 	if (tb[XFRMA_ALG_CRYPT]) {
 		struct rtattr *rta = tb[XFRMA_ALG_CRYPT];
-
-		xfrm_algo_print(RTA_DATA(rta), XFRMA_ALG_CRYPT,
-				RTA_PAYLOAD(rta), fp, prefix, nokeys);
+		xfrm_algo_print((struct xfrm_algo *) RTA_DATA(rta),
+				XFRMA_ALG_CRYPT, RTA_PAYLOAD(rta), fp, prefix);
 	}
 
 	if (tb[XFRMA_ALG_COMP]) {
 		struct rtattr *rta = tb[XFRMA_ALG_COMP];
-
-		xfrm_algo_print(RTA_DATA(rta), XFRMA_ALG_COMP, RTA_PAYLOAD(rta),
-				fp, prefix, nokeys);
+		xfrm_algo_print((struct xfrm_algo *) RTA_DATA(rta),
+				XFRMA_ALG_COMP, RTA_PAYLOAD(rta), fp, prefix);
 	}
 
 	if (tb[XFRMA_ENCAP]) {
 		struct xfrm_encap_tmpl *e;
+		char abuf[256];
 
 		if (prefix)
 			fputs(prefix, fp);
@@ -749,7 +739,7 @@
 			fprintf(fp, "%s", _SL_);
 			return;
 		}
-		e = RTA_DATA(tb[XFRMA_ENCAP]);
+		e = (struct xfrm_encap_tmpl *) RTA_DATA(tb[XFRMA_ENCAP]);
 
 		fprintf(fp, "type ");
 		switch (e->encap_type) {
@@ -766,34 +756,39 @@
 		fprintf(fp, "sport %u ", ntohs(e->encap_sport));
 		fprintf(fp, "dport %u ", ntohs(e->encap_dport));
 
+		memset(abuf, '\0', sizeof(abuf));
 		fprintf(fp, "addr %s",
-			rt_addr_n2a(family, sizeof(e->encap_oa), &e->encap_oa));
+			rt_addr_n2a(family, sizeof(e->encap_oa), &e->encap_oa,
+				    abuf, sizeof(abuf)));
 		fprintf(fp, "%s", _SL_);
 	}
 
 	if (tb[XFRMA_TMPL]) {
 		struct rtattr *rta = tb[XFRMA_TMPL];
-
-		xfrm_tmpl_print(RTA_DATA(rta),
+		xfrm_tmpl_print((struct xfrm_user_tmpl *) RTA_DATA(rta),
 				RTA_PAYLOAD(rta), fp, prefix);
 	}
 
 	if (tb[XFRMA_COADDR]) {
-		const xfrm_address_t *coa;
+		char abuf[256];
+		xfrm_address_t *coa;
 
 		if (prefix)
 			fputs(prefix, fp);
 		fprintf(fp, "coa ");
 
-		coa = RTA_DATA(tb[XFRMA_COADDR]);
+		coa = (xfrm_address_t *)RTA_DATA(tb[XFRMA_COADDR]);
+
 		if (RTA_PAYLOAD(tb[XFRMA_COADDR]) < sizeof(*coa)) {
 			fprintf(fp, "(ERROR truncated)");
 			fprintf(fp, "%s", _SL_);
 			return;
 		}
 
+		memset(abuf, '\0', sizeof(abuf));
 		fprintf(fp, "%s",
-			rt_addr_n2a(family, sizeof(*coa), coa));
+			rt_addr_n2a(family, sizeof(*coa), coa,
+				    abuf, sizeof(abuf)));
 		fprintf(fp, "%s", _SL_);
 	}
 
@@ -829,7 +824,7 @@
 			return;
 		}
 
-		replay = RTA_DATA(tb[XFRMA_REPLAY_VAL]);
+		replay = (struct xfrm_replay_state *)RTA_DATA(tb[XFRMA_REPLAY_VAL]);
 		fprintf(fp, "seq 0x%x, oseq 0x%x, bitmap 0x%08x",
 			replay->seq, replay->oseq, replay->bitmap);
 		fprintf(fp, "%s", _SL_);
@@ -850,7 +845,7 @@
 		}
 		fprintf(fp, "%s", _SL_);
 
-		replay = RTA_DATA(tb[XFRMA_REPLAY_ESN_VAL]);
+		replay = (struct xfrm_replay_state_esn *)RTA_DATA(tb[XFRMA_REPLAY_ESN_VAL]);
 		if (prefix)
 			fputs(prefix, fp);
 		fprintf(fp, " seq-hi 0x%x, seq 0x%x, oseq-hi 0x%0x, oseq 0x%0x",
@@ -872,56 +867,33 @@
 		}
 		fprintf(fp, "%s", _SL_);
 	}
-	if (tb[XFRMA_OFFLOAD_DEV]) {
-		struct xfrm_user_offload *xuo;
-
-		if (prefix)
-			fputs(prefix, fp);
-		fprintf(fp, "crypto offload parameters: ");
-
-		if (RTA_PAYLOAD(tb[XFRMA_OFFLOAD_DEV]) < sizeof(*xuo)) {
-			fprintf(fp, "(ERROR truncated)");
-			fprintf(fp, "%s", _SL_);
-			return;
-		}
-
-		xuo = (struct xfrm_user_offload *)
-			RTA_DATA(tb[XFRMA_OFFLOAD_DEV]);
-		fprintf(fp, "dev %s dir %s", ll_index_to_name(xuo->ifindex),
-			(xuo->flags & XFRM_OFFLOAD_INBOUND) ? "in" : "out");
-		fprintf(fp, "%s", _SL_);
-	}
-	if (tb[XFRMA_IF_ID]) {
-		__u32 if_id = rta_getattr_u32(tb[XFRMA_IF_ID]);
-
-		if (prefix)
-			fputs(prefix, fp);
-		fprintf(fp, "if_id %#x", if_id);
-		fprintf(fp, "%s", _SL_);
-	}
 }
 
 static int xfrm_selector_iszero(struct xfrm_selector *s)
 {
-	struct xfrm_selector s0 = {};
+	struct xfrm_selector s0;
+
+	memset(&s0, 0, sizeof(s0));
 
 	return (memcmp(&s0, s, sizeof(s0)) == 0);
 }
 
 void xfrm_state_info_print(struct xfrm_usersa_info *xsinfo,
 			    struct rtattr *tb[], FILE *fp, const char *prefix,
-			    const char *title, bool nokeys)
+			    const char *title)
 {
-	char buf[STRBUF_SIZE] = {};
+	char buf[STRBUF_SIZE];
 	int force_spi = xfrm_xfrmproto_is_ipsec(xsinfo->id.proto);
 
+	memset(buf, '\0', sizeof(buf));
+
 	xfrm_id_info_print(&xsinfo->saddr, &xsinfo->id, xsinfo->mode,
 			   xsinfo->reqid, xsinfo->family, force_spi, fp,
 			   prefix, title);
 
 	if (prefix)
-		strlcat(buf, prefix, sizeof(buf));
-	strlcat(buf, "\t", sizeof(buf));
+		STRBUF_CAT(buf, prefix);
+	STRBUF_CAT(buf, "\t");
 
 	fputs(buf, fp);
 	fprintf(fp, "replay-window %u ", xsinfo->replay_window);
@@ -943,7 +915,7 @@
 			fprintf(fp, "%x", flags);
 	}
 	if (show_stats > 0 && tb[XFRMA_SA_EXTRA_FLAGS]) {
-		__u32 extra_flags = rta_getattr_u32(tb[XFRMA_SA_EXTRA_FLAGS]);
+		__u32 extra_flags = *(__u32 *)RTA_DATA(tb[XFRMA_SA_EXTRA_FLAGS]);
 
 		fprintf(fp, "extra_flag ");
 		XFRM_FLAG_PRINT(fp, extra_flags,
@@ -956,13 +928,13 @@
 		fprintf(fp, " (0x%s)", strxf_mask8(xsinfo->flags));
 	fprintf(fp, "%s", _SL_);
 
-	xfrm_xfrma_print(tb, xsinfo->family, fp, buf, nokeys);
+	xfrm_xfrma_print(tb, xsinfo->family, fp, buf);
 
 	if (!xfrm_selector_iszero(&xsinfo->sel)) {
 		char sbuf[STRBUF_SIZE];
 
 		memcpy(sbuf, buf, sizeof(sbuf));
-		strlcat(sbuf, "sel ", sizeof(sbuf));
+		STRBUF_CAT(sbuf, "sel ");
 
 		xfrm_selector_print(&xsinfo->sel, xsinfo->family, fp, sbuf);
 	}
@@ -980,7 +952,7 @@
 		if (RTA_PAYLOAD(tb[XFRMA_SEC_CTX]) < sizeof(*sctx))
 			fprintf(fp, "(ERROR truncated)");
 
-		sctx = RTA_DATA(tb[XFRMA_SEC_CTX]);
+		sctx = (struct xfrm_user_sec_ctx *)RTA_DATA(tb[XFRMA_SEC_CTX]);
 
 		fprintf(fp, "%s %s", (char *)(sctx + 1), _SL_);
 	}
@@ -991,7 +963,9 @@
 			    struct rtattr *tb[], FILE *fp, const char *prefix,
 			    const char *title)
 {
-	char buf[STRBUF_SIZE] = {};
+	char buf[STRBUF_SIZE];
+
+	memset(buf, '\0', sizeof(buf));
 
 	xfrm_selector_print(&xpinfo->sel, preferred_family, fp, title);
 
@@ -1003,15 +977,15 @@
 		if (RTA_PAYLOAD(tb[XFRMA_SEC_CTX]) < sizeof(*sctx))
 			fprintf(fp, "(ERROR truncated)");
 
-		sctx = RTA_DATA(tb[XFRMA_SEC_CTX]);
+		sctx = (struct xfrm_user_sec_ctx *)RTA_DATA(tb[XFRMA_SEC_CTX]);
 
 		fprintf(fp, "%s ", (char *)(sctx + 1));
 		fprintf(fp, "%s", _SL_);
 	}
 
 	if (prefix)
-		strlcat(buf, prefix, sizeof(buf));
-	strlcat(buf, "\t", sizeof(buf));
+		STRBUF_CAT(buf, prefix);
+	STRBUF_CAT(buf, "\t");
 
 	fputs(buf, fp);
 	if (xpinfo->dir >= XFRM_POLICY_MAX) {
@@ -1061,7 +1035,7 @@
 		if (RTA_PAYLOAD(tb[XFRMA_POLICY_TYPE]) < sizeof(*upt))
 			fprintf(fp, "(ERROR truncated)");
 
-		upt = RTA_DATA(tb[XFRMA_POLICY_TYPE]);
+		upt = (struct xfrm_userpolicy_type *)RTA_DATA(tb[XFRMA_POLICY_TYPE]);
 		fprintf(fp, "%s ", strxf_ptype(upt->type));
 	}
 
@@ -1084,7 +1058,7 @@
 	if (show_stats > 0)
 		xfrm_lifetime_print(&xpinfo->lft, &xpinfo->curlft, fp, buf);
 
-	xfrm_xfrma_print(tb, xpinfo->sel.family, fp, buf, false);
+	xfrm_xfrma_print(tb, xpinfo->sel.family, fp, buf);
 }
 
 int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family,
@@ -1092,8 +1066,11 @@
 {
 	int argc = *argcp;
 	char **argv = *argvp;
-	inet_prefix dst = {};
-	inet_prefix src = {};
+	inet_prefix dst;
+	inet_prefix src;
+
+	memset(&dst, 0, sizeof(dst));
+	memset(&src, 0, sizeof(src));
 
 	while (1) {
 		if (strcmp(*argv, "src") == 0) {
@@ -1136,10 +1113,15 @@
 			filter.id_proto_mask = XFRM_FILTER_MASK_FULL;
 
 		} else if (strcmp(*argv, "spi") == 0) {
+			__u32 spi;
+
 			NEXT_ARG();
-			if (get_be32(&id->spi, *argv, 0))
+			if (get_u32(&spi, *argv, 0))
 				invarg("SPI value is invalid", *argv);
 
+			spi = htonl(spi);
+			id->spi = spi;
+
 			filter.id_spi_mask = XFRM_FILTER_MASK_FULL;
 
 		} else {
@@ -1158,11 +1140,11 @@
 	if (id->spi && id->proto) {
 		if (xfrm_xfrmproto_is_ro(id->proto)) {
 			fprintf(stderr, "\"spi\" is invalid with XFRM-PROTO value \"%s\"\n",
-				strxf_xfrmproto(id->proto));
+			        strxf_xfrmproto(id->proto));
 			exit(1);
 		} else if (id->proto == IPPROTO_COMP && ntohl(id->spi) >= 0x10000) {
 			fprintf(stderr, "SPI value is too large with XFRM-PROTO value \"%s\"\n",
-				strxf_xfrmproto(id->proto));
+			        strxf_xfrmproto(id->proto));
 			exit(1);
 		}
 	}
@@ -1256,7 +1238,6 @@
 				upspec = 0;
 			else {
 				struct protoent *pp;
-
 				pp = getprotobyname(*argv);
 				if (pp)
 					upspec = pp->p_proto;
@@ -1274,8 +1255,9 @@
 
 			NEXT_ARG();
 
-			if (get_be16(&sel->sport, *argv, 0))
+			if (get_u16(&sel->sport, *argv, 0))
 				invarg("value after \"sport\" is invalid", *argv);
+			sel->sport = htons(sel->sport);
 			if (sel->sport)
 				sel->sport_mask = ~((__u16)0);
 
@@ -1286,8 +1268,9 @@
 
 			NEXT_ARG();
 
-			if (get_be16(&sel->dport, *argv, 0))
+			if (get_u16(&sel->dport, *argv, 0))
 				invarg("value after \"dport\" is invalid", *argv);
+			sel->dport = htons(sel->dport);
 			if (sel->dport)
 				sel->dport_mask = ~((__u16)0);
 
@@ -1321,7 +1304,7 @@
 			filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL;
 
 		} else if (strcmp(*argv, "key") == 0) {
-			unsigned int uval;
+			unsigned uval;
 
 			grekey = *argv;
 
@@ -1330,7 +1313,7 @@
 			if (strchr(*argv, '.'))
 				uval = htonl(get_addr32(*argv));
 			else {
-				if (get_unsigned(&uval, *argv, 0) < 0) {
+				if (get_unsigned(&uval, *argv, 0)<0) {
 					fprintf(stderr, "value after \"key\" is invalid\n");
 					exit(-1);
 				}
@@ -1398,10 +1381,13 @@
 {
 	int argc = *argcp;
 	char **argv = *argvp;
-	inet_prefix dst = {};
-	inet_prefix src = {};
+	inet_prefix dst;
+	inet_prefix src;
 	char *upspecp = NULL;
 
+	memset(&dst, 0, sizeof(dst));
+	memset(&src, 0, sizeof(src));
+
 	while (1) {
 		if (strcmp(*argv, "src") == 0) {
 			NEXT_ARG();
diff --git a/ip/link_gre.c b/ip/link_gre.c
index 15beb73..c85741f 100644
--- a/ip/link_gre.c
+++ b/ip/link_gre.c
@@ -23,100 +23,81 @@
 #include "ip_common.h"
 #include "tunnel.h"
 
-static void gre_print_help(struct link_util *lu, int argc, char **argv, FILE *f)
+static void print_usage(FILE *f)
 {
-	fprintf(f,
-		"Usage: ... %-9s	[ remote ADDR ]\n"
-		"			[ local ADDR ]\n"
-		"			[ [no][i|o]seq ]\n"
-		"			[ [i|o]key KEY | no[i|o]key ]\n"
-		"			[ [no][i|o]csum ]\n"
-		"			[ ttl TTL ]\n"
-		"			[ tos TOS ]\n"
-		"			[ [no]pmtudisc ]\n"
-		"			[ [no]ignore-df ]\n"
-		"			[ dev PHYS_DEV ]\n"
-		"			[ fwmark MARK ]\n"
-		"			[ external ]\n"
-		"			[ noencap ]\n"
-		"			[ encap { fou | gue | none } ]\n"
-		"			[ encap-sport PORT ]\n"
-		"			[ encap-dport PORT ]\n"
-		"			[ [no]encap-csum ]\n"
-		"			[ [no]encap-csum6 ]\n"
-		"			[ [no]encap-remcsum ]\n"
-		"			[ erspan_ver version ]\n"
-		"			[ erspan IDX ]\n"
-		"			[ erspan_dir { ingress | egress } ]\n"
-		"			[ erspan_hwid hwid ]\n"
-		"\n"
-		"Where:	ADDR := { IP_ADDRESS | any }\n"
-		"	TOS  := { NUMBER | inherit }\n"
-		"	TTL  := { 1..255 | inherit }\n"
-		"	KEY  := { DOTTED_QUAD | NUMBER }\n"
-		"	MARK := { 0x0..0xffffffff }\n",
-		lu->id);
+	fprintf(f, "Usage: ip link { add | set | change | replace | del } NAME\n");
+	fprintf(f, "          type { gre | gretap } [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(f, "          [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
+	fprintf(f, "          [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n");
+	fprintf(f, "          [ noencap ] [ encap { fou | gue | none } ]\n");
+	fprintf(f, "          [ encap-sport PORT ] [ encap-dport PORT ]\n");
+	fprintf(f, "          [ [no]encap-csum ] [ [no]encap-csum6 ] [ [no]encap-remcsum ]\n");
+	fprintf(f, "\n");
+	fprintf(f, "Where: NAME := STRING\n");
+	fprintf(f, "       ADDR := { IP_ADDRESS | any }\n");
+	fprintf(f, "       TOS  := { NUMBER | inherit }\n");
+	fprintf(f, "       TTL  := { 1..255 | inherit }\n");
+	fprintf(f, "       KEY  := { DOTTED_QUAD | NUMBER }\n");
+}
+
+static void usage(void) __attribute__((noreturn));
+static void usage(void)
+{
+	print_usage(stderr);
+	exit(-1);
 }
 
 static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
 			 struct nlmsghdr *n)
 {
-	struct ifinfomsg *ifi = NLMSG_DATA(n);
 	struct {
 		struct nlmsghdr n;
 		struct ifinfomsg i;
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_GETLINK,
-		.i.ifi_family = preferred_family,
-		.i.ifi_index = ifi->ifi_index,
-	};
-	struct nlmsghdr *answer;
+		char buf[16384];
+	} req;
+	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
 	struct rtattr *tb[IFLA_MAX + 1];
 	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
 	struct rtattr *greinfo[IFLA_GRE_MAX + 1];
-	int len;
 	__u16 iflags = 0;
 	__u16 oflags = 0;
-	__be32 ikey = 0;
-	__be32 okey = 0;
-	inet_prefix saddr, daddr;
+	unsigned ikey = 0;
+	unsigned okey = 0;
+	unsigned saddr = 0;
+	unsigned daddr = 0;
+	unsigned link = 0;
 	__u8 pmtudisc = 1;
-	__u8 ignore_df = 0;
-	__u8 tos = 0;
 	__u8 ttl = 0;
-	__u32 link = 0;
+	__u8 tos = 0;
+	int len;
 	__u16 encaptype = 0;
 	__u16 encapflags = 0;
 	__u16 encapsport = 0;
 	__u16 encapdport = 0;
 	__u8 metadata = 0;
-	__u32 fwmark = 0;
-	__u32 erspan_idx = 0;
-	__u8 erspan_ver = 0;
-	__u8 erspan_dir = 0;
-	__u16 erspan_hwid = 0;
-
-	inet_prefix_reset(&saddr);
-	inet_prefix_reset(&daddr);
 
 	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
-		const struct rtattr *rta;
+		memset(&req, 0, sizeof(req));
 
-		if (rtnl_talk(&rth, &req.n, &answer) < 0) {
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETLINK;
+		req.i.ifi_family = preferred_family;
+		req.i.ifi_index = ifi->ifi_index;
+
+		if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) {
 get_failed:
 			fprintf(stderr,
 				"Failed to get existing tunnel info.\n");
 			return -1;
 		}
 
-		len = answer->nlmsg_len;
+		len = req.n.nlmsg_len;
 		len -= NLMSG_LENGTH(sizeof(*ifi));
 		if (len < 0)
 			goto get_failed;
 
-		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len);
+		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
 
 		if (!tb[IFLA_LINKINFO])
 			goto get_failed;
@@ -129,14 +110,6 @@
 		parse_rtattr_nested(greinfo, IFLA_GRE_MAX,
 				    linkinfo[IFLA_INFO_DATA]);
 
-		rta = greinfo[IFLA_GRE_LOCAL];
-		if (rta && get_addr_rta(&saddr, rta, AF_INET))
-			goto get_failed;
-
-		rta = greinfo[IFLA_GRE_REMOTE];
-		if (rta && get_addr_rta(&daddr, rta, AF_INET))
-			goto get_failed;
-
 		if (greinfo[IFLA_GRE_IKEY])
 			ikey = rta_getattr_u32(greinfo[IFLA_GRE_IKEY]);
 
@@ -149,132 +122,133 @@
 		if (greinfo[IFLA_GRE_OFLAGS])
 			oflags = rta_getattr_u16(greinfo[IFLA_GRE_OFLAGS]);
 
+		if (greinfo[IFLA_GRE_LOCAL])
+			saddr = rta_getattr_u32(greinfo[IFLA_GRE_LOCAL]);
+
+		if (greinfo[IFLA_GRE_REMOTE])
+			daddr = rta_getattr_u32(greinfo[IFLA_GRE_REMOTE]);
+
 		if (greinfo[IFLA_GRE_PMTUDISC])
 			pmtudisc = rta_getattr_u8(
 				greinfo[IFLA_GRE_PMTUDISC]);
 
-		if (greinfo[IFLA_GRE_IGNORE_DF])
-			ignore_df =
-				!!rta_getattr_u8(greinfo[IFLA_GRE_IGNORE_DF]);
+		if (greinfo[IFLA_GRE_TTL])
+			ttl = rta_getattr_u8(greinfo[IFLA_GRE_TTL]);
 
 		if (greinfo[IFLA_GRE_TOS])
 			tos = rta_getattr_u8(greinfo[IFLA_GRE_TOS]);
 
-		if (greinfo[IFLA_GRE_TTL])
-			ttl = rta_getattr_u8(greinfo[IFLA_GRE_TTL]);
-
 		if (greinfo[IFLA_GRE_LINK])
-			link = rta_getattr_u32(greinfo[IFLA_GRE_LINK]);
+			link = rta_getattr_u8(greinfo[IFLA_GRE_LINK]);
 
 		if (greinfo[IFLA_GRE_ENCAP_TYPE])
 			encaptype = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_TYPE]);
-
 		if (greinfo[IFLA_GRE_ENCAP_FLAGS])
 			encapflags = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_FLAGS]);
-
 		if (greinfo[IFLA_GRE_ENCAP_SPORT])
 			encapsport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_SPORT]);
-
 		if (greinfo[IFLA_GRE_ENCAP_DPORT])
 			encapdport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_DPORT]);
 
 		if (greinfo[IFLA_GRE_COLLECT_METADATA])
 			metadata = 1;
-
-		if (greinfo[IFLA_GRE_FWMARK])
-			fwmark = rta_getattr_u32(greinfo[IFLA_GRE_FWMARK]);
-
-		if (greinfo[IFLA_GRE_ERSPAN_INDEX])
-			erspan_idx = rta_getattr_u32(greinfo[IFLA_GRE_ERSPAN_INDEX]);
-
-		if (greinfo[IFLA_GRE_ERSPAN_VER])
-			erspan_ver = rta_getattr_u8(greinfo[IFLA_GRE_ERSPAN_VER]);
-
-		if (greinfo[IFLA_GRE_ERSPAN_DIR])
-			erspan_dir = rta_getattr_u8(greinfo[IFLA_GRE_ERSPAN_DIR]);
-
-		if (greinfo[IFLA_GRE_ERSPAN_HWID])
-			erspan_hwid = rta_getattr_u16(greinfo[IFLA_GRE_ERSPAN_HWID]);
-
-		free(answer);
 	}
 
 	while (argc > 0) {
 		if (!matches(*argv, "key")) {
+			unsigned uval;
+
 			NEXT_ARG();
 			iflags |= GRE_KEY;
 			oflags |= GRE_KEY;
-			ikey = okey = tnl_parse_key("key", *argv);
-		} else if (!matches(*argv, "nokey")) {
-			iflags &= ~GRE_KEY;
-			oflags &= ~GRE_KEY;
-			ikey = okey = 0;
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0) < 0) {
+					fprintf(stderr,
+						"Invalid value for \"key\": \"%s\"; it should be an unsigned integer\n", *argv);
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+
+			ikey = okey = uval;
 		} else if (!matches(*argv, "ikey")) {
+			unsigned uval;
+
 			NEXT_ARG();
 			iflags |= GRE_KEY;
-			ikey = tnl_parse_key("ikey", *argv);
-		} else if (!matches(*argv, "noikey")) {
-			iflags &= ~GRE_KEY;
-			ikey = 0;
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0)<0) {
+					fprintf(stderr, "invalid value for \"ikey\": \"%s\"; it should be an unsigned integer\n", *argv);
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+			ikey = uval;
 		} else if (!matches(*argv, "okey")) {
+			unsigned uval;
+
 			NEXT_ARG();
 			oflags |= GRE_KEY;
-			okey = tnl_parse_key("okey", *argv);
-		} else if (!matches(*argv, "nookey")) {
-			oflags &= ~GRE_KEY;
-			okey = 0;
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0)<0) {
+					fprintf(stderr, "invalid value for \"okey\": \"%s\"; it should be an unsigned integer\n", *argv);
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+			okey = uval;
 		} else if (!matches(*argv, "seq")) {
 			iflags |= GRE_SEQ;
 			oflags |= GRE_SEQ;
-		} else if (!matches(*argv, "noseq")) {
-			iflags &= ~GRE_SEQ;
-			oflags &= ~GRE_SEQ;
 		} else if (!matches(*argv, "iseq")) {
 			iflags |= GRE_SEQ;
-		} else if (!matches(*argv, "noiseq")) {
-			iflags &= ~GRE_SEQ;
 		} else if (!matches(*argv, "oseq")) {
 			oflags |= GRE_SEQ;
-		} else if (!matches(*argv, "nooseq")) {
-			oflags &= ~GRE_SEQ;
 		} else if (!matches(*argv, "csum")) {
 			iflags |= GRE_CSUM;
 			oflags |= GRE_CSUM;
-		} else if (!matches(*argv, "nocsum")) {
-			iflags &= ~GRE_CSUM;
-			oflags &= ~GRE_CSUM;
 		} else if (!matches(*argv, "icsum")) {
 			iflags |= GRE_CSUM;
-		} else if (!matches(*argv, "noicsum")) {
-			iflags &= ~GRE_CSUM;
 		} else if (!matches(*argv, "ocsum")) {
 			oflags |= GRE_CSUM;
-		} else if (!matches(*argv, "noocsum")) {
-			oflags &= ~GRE_CSUM;
 		} else if (!matches(*argv, "nopmtudisc")) {
 			pmtudisc = 0;
 		} else if (!matches(*argv, "pmtudisc")) {
 			pmtudisc = 1;
 		} else if (!matches(*argv, "remote")) {
 			NEXT_ARG();
-			get_addr(&daddr, *argv, AF_INET);
+			if (strcmp(*argv, "any"))
+				daddr = get_addr32(*argv);
 		} else if (!matches(*argv, "local")) {
 			NEXT_ARG();
-			get_addr(&saddr, *argv, AF_INET);
+			if (strcmp(*argv, "any"))
+				saddr = get_addr32(*argv);
 		} else if (!matches(*argv, "dev")) {
 			NEXT_ARG();
-			link = ll_name_to_index(*argv);
-			if (!link)
-				exit(nodev(*argv));
+			link = if_nametoindex(*argv);
+			if (link == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n",
+					*argv);
+				exit(-1);
+			}
 		} else if (!matches(*argv, "ttl") ||
-			   !matches(*argv, "hoplimit") ||
-			   !matches(*argv, "hlim")) {
+			   !matches(*argv, "hoplimit")) {
+			unsigned uval;
+
 			NEXT_ARG();
 			if (strcmp(*argv, "inherit") != 0) {
-				if (get_u8(&ttl, *argv, 0))
+				if (get_unsigned(&uval, *argv, 0))
 					invarg("invalid TTL\n", *argv);
-			} else
-				ttl = 0;
+				if (uval > 255)
+					invarg("TTL must be <= 255\n", *argv);
+				ttl = uval;
+			}
 		} else if (!matches(*argv, "tos") ||
 			   !matches(*argv, "tclass") ||
 			   !matches(*argv, "dsfield")) {
@@ -316,162 +290,111 @@
 		} else if (strcmp(*argv, "encap-udp6-csum") == 0) {
 			encapflags |= TUNNEL_ENCAP_FLAG_CSUM6;
 		} else if (strcmp(*argv, "noencap-udp6-csum") == 0) {
-			encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM6;
+			encapflags |= ~TUNNEL_ENCAP_FLAG_CSUM6;
 		} else if (strcmp(*argv, "encap-remcsum") == 0) {
 			encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM;
 		} else if (strcmp(*argv, "noencap-remcsum") == 0) {
-			encapflags &= ~TUNNEL_ENCAP_FLAG_REMCSUM;
+			encapflags |= ~TUNNEL_ENCAP_FLAG_REMCSUM;
 		} else if (strcmp(*argv, "external") == 0) {
 			metadata = 1;
-		} else if (strcmp(*argv, "ignore-df") == 0) {
-			ignore_df = 1;
-		} else if (strcmp(*argv, "noignore-df") == 0) {
-			/*
-			 *only the lsb is significant, use 2 for presence
-			 */
-			ignore_df = 2;
-		} else if (strcmp(*argv, "fwmark") == 0) {
-			NEXT_ARG();
-			if (get_u32(&fwmark, *argv, 0))
-				invarg("invalid fwmark\n", *argv);
-		} else if (strcmp(*argv, "erspan") == 0) {
-			NEXT_ARG();
-			if (get_u32(&erspan_idx, *argv, 0))
-				invarg("invalid erspan index\n", *argv);
-			if (erspan_idx & ~((1<<20) - 1) || erspan_idx == 0)
-				invarg("erspan index must be > 0 and <= 20-bit\n", *argv);
-		} else if (strcmp(*argv, "erspan_ver") == 0) {
-			NEXT_ARG();
-			if (get_u8(&erspan_ver, *argv, 0))
-				invarg("invalid erspan version\n", *argv);
-			if (erspan_ver != 1 && erspan_ver != 2)
-				invarg("erspan version must be 1 or 2\n", *argv);
-		} else if (strcmp(*argv, "erspan_dir") == 0) {
-			NEXT_ARG();
-			if (matches(*argv, "ingress") == 0)
-				erspan_dir = 0;
-			else if (matches(*argv, "egress") == 0)
-				erspan_dir = 1;
-			else
-				invarg("Invalid erspan direction.", *argv);
-		} else if (strcmp(*argv, "erspan_hwid") == 0) {
-			NEXT_ARG();
-			if (get_u16(&erspan_hwid, *argv, 0))
-				invarg("invalid erspan hwid\n", *argv);
-		} else {
-			gre_print_help(lu, argc, argv, stderr);
-			return -1;
-		}
+		} else
+			usage();
 		argc--; argv++;
 	}
 
-	if (is_addrtype_inet_multi(&daddr)) {
-		if (!ikey) {
-			ikey = daddr.data[0];
-			iflags |= GRE_KEY;
-		}
-		if (!okey) {
-			okey = daddr.data[0];
-			oflags |= GRE_KEY;
-		}
-		if (!is_addrtype_inet_not_unspec(&saddr)) {
-			fprintf(stderr,
-				"A broadcast tunnel requires a source address.\n");
-			return -1;
-		}
+	if (!ikey && IN_MULTICAST(ntohl(daddr))) {
+		ikey = daddr;
+		iflags |= GRE_KEY;
 	}
-
-	if (metadata) {
-		addattr_l(n, 1024, IFLA_GRE_COLLECT_METADATA, NULL, 0);
-		return 0;
+	if (!okey && IN_MULTICAST(ntohl(daddr))) {
+		okey = daddr;
+		oflags |= GRE_KEY;
+	}
+	if (IN_MULTICAST(ntohl(daddr)) && !saddr) {
+		fprintf(stderr, "A broadcast tunnel requires a source address.\n");
+		return -1;
 	}
 
 	addattr32(n, 1024, IFLA_GRE_IKEY, ikey);
 	addattr32(n, 1024, IFLA_GRE_OKEY, okey);
 	addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2);
 	addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2);
-	if (is_addrtype_inet_not_unspec(&saddr))
-		addattr_l(n, 1024, IFLA_GRE_LOCAL, saddr.data, saddr.bytelen);
-	if (is_addrtype_inet_not_unspec(&daddr))
-		addattr_l(n, 1024, IFLA_GRE_REMOTE, daddr.data, daddr.bytelen);
+	addattr_l(n, 1024, IFLA_GRE_LOCAL, &saddr, 4);
+	addattr_l(n, 1024, IFLA_GRE_REMOTE, &daddr, 4);
 	addattr_l(n, 1024, IFLA_GRE_PMTUDISC, &pmtudisc, 1);
-	if (ignore_df)
-		addattr8(n, 1024, IFLA_GRE_IGNORE_DF, ignore_df & 1);
-	addattr_l(n, 1024, IFLA_GRE_TOS, &tos, 1);
 	if (link)
 		addattr32(n, 1024, IFLA_GRE_LINK, link);
 	addattr_l(n, 1024, IFLA_GRE_TTL, &ttl, 1);
-	addattr32(n, 1024, IFLA_GRE_FWMARK, fwmark);
-	if (erspan_ver) {
-		addattr8(n, 1024, IFLA_GRE_ERSPAN_VER, erspan_ver);
-		if (erspan_ver == 1 && erspan_idx != 0) {
-			addattr32(n, 1024, IFLA_GRE_ERSPAN_INDEX, erspan_idx);
-		} else if (erspan_ver == 2) {
-			addattr8(n, 1024, IFLA_GRE_ERSPAN_DIR, erspan_dir);
-			addattr16(n, 1024, IFLA_GRE_ERSPAN_HWID, erspan_hwid);
-		}
-	}
+	addattr_l(n, 1024, IFLA_GRE_TOS, &tos, 1);
+
 	addattr16(n, 1024, IFLA_GRE_ENCAP_TYPE, encaptype);
 	addattr16(n, 1024, IFLA_GRE_ENCAP_FLAGS, encapflags);
 	addattr16(n, 1024, IFLA_GRE_ENCAP_SPORT, htons(encapsport));
 	addattr16(n, 1024, IFLA_GRE_ENCAP_DPORT, htons(encapdport));
+	if (metadata)
+		addattr_l(n, 1024, IFLA_GRE_COLLECT_METADATA, NULL, 0);
 
 	return 0;
 }
 
 static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
+	char s1[1024];
 	char s2[64];
-	__u16 iflags = 0;
-	__u16 oflags = 0;
-	__u8 ttl = 0;
-	__u8 tos = 0;
+	const char *local = "any";
+	const char *remote = "any";
+	unsigned iflags = 0;
+	unsigned oflags = 0;
 
 	if (!tb)
 		return;
 
-	if (tb[IFLA_GRE_COLLECT_METADATA]) {
-		print_bool(PRINT_ANY, "external", "external ", true);
-		return;
+	if (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));
 	}
 
-	tnl_print_endpoint("remote", tb[IFLA_GRE_REMOTE], AF_INET);
-	tnl_print_endpoint("local", tb[IFLA_GRE_LOCAL], AF_INET);
+	fprintf(f, "remote %s ", remote);
 
-	if (tb[IFLA_GRE_LINK]) {
-		__u32 link = rta_getattr_u32(tb[IFLA_GRE_LINK]);
+	if (tb[IFLA_GRE_LOCAL]) {
+		unsigned addr = rta_getattr_u32(tb[IFLA_GRE_LOCAL]);
 
-		if (link) {
-			print_string(PRINT_ANY, "link", "dev %s ",
-				     ll_index_to_name(link));
-		}
+		if (addr)
+			local = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
 	}
 
-	if (tb[IFLA_GRE_TTL])
-		ttl = rta_getattr_u8(tb[IFLA_GRE_TTL]);
-	if (is_json_context() || ttl)
-		print_uint(PRINT_ANY, "ttl", "ttl %u ", ttl);
+	fprintf(f, "local %s ", local);
+
+	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);
+		else
+			fprintf(f, "dev %u ", link);
+	}
+
+	if (tb[IFLA_GRE_TTL] && rta_getattr_u8(tb[IFLA_GRE_TTL]))
+		fprintf(f, "ttl %d ", rta_getattr_u8(tb[IFLA_GRE_TTL]));
 	else
-		print_string(PRINT_FP, NULL, "ttl %s ", "inherit");
+		fprintf(f, "ttl inherit ");
 
-	if (tb[IFLA_GRE_TOS])
-		tos = rta_getattr_u8(tb[IFLA_GRE_TOS]);
-	if (tos) {
-		if (is_json_context() || tos != 1)
-			print_0xhex(PRINT_ANY, "tos", "tos %#llx ", 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)
+			fputs("inherit ", f);
 		else
-			print_string(PRINT_FP, NULL, "tos %s ", "inherit");
+			fprintf(f, "0x%x ", tos);
 	}
 
-	if (tb[IFLA_GRE_PMTUDISC]) {
-		if (!rta_getattr_u8(tb[IFLA_GRE_PMTUDISC]))
-			print_bool(PRINT_ANY, "pmtudisc", "nopmtudisc ", false);
-		else
-			print_bool(PRINT_JSON, "pmtudisc", NULL, true);
-	}
-
-	if (tb[IFLA_GRE_IGNORE_DF] && rta_getattr_u8(tb[IFLA_GRE_IGNORE_DF]))
-		print_bool(PRINT_ANY, "ignore_df", "ignore-df ", true);
+	if (tb[IFLA_GRE_PMTUDISC] &&
+	    !rta_getattr_u8(tb[IFLA_GRE_PMTUDISC]))
+		fputs("nopmtudisc ", f);
 
 	if (tb[IFLA_GRE_IFLAGS])
 		iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]);
@@ -481,69 +404,74 @@
 
 	if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) {
 		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2));
-		print_string(PRINT_ANY, "ikey", "ikey %s ", s2);
+		fprintf(f, "ikey %s ", s2);
 	}
 
 	if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) {
 		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2));
-		print_string(PRINT_ANY, "okey", "okey %s ", s2);
+		fprintf(f, "okey %s ", s2);
 	}
 
 	if (iflags & GRE_SEQ)
-		print_bool(PRINT_ANY, "iseq", "iseq ", true);
+		fputs("iseq ", f);
 	if (oflags & GRE_SEQ)
-		print_bool(PRINT_ANY, "oseq", "oseq ", true);
+		fputs("oseq ", f);
 	if (iflags & GRE_CSUM)
-		print_bool(PRINT_ANY, "icsum", "icsum ", true);
+		fputs("icsum ", f);
 	if (oflags & GRE_CSUM)
-		print_bool(PRINT_ANY, "ocsum", "ocsum ", true);
+		fputs("ocsum ", f);
 
-	if (tb[IFLA_GRE_FWMARK]) {
-		__u32 fwmark = rta_getattr_u32(tb[IFLA_GRE_FWMARK]);
+	if (tb[IFLA_GRE_COLLECT_METADATA])
+		fputs("external ", f);
 
-		if (fwmark) {
-			print_0xhex(PRINT_ANY,
-				    "fwmark", "fwmark %#llx ", fwmark);
+	if (tb[IFLA_GRE_ENCAP_TYPE] &&
+	    *(__u16 *)RTA_DATA(tb[IFLA_GRE_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE) {
+		__u16 type = rta_getattr_u16(tb[IFLA_GRE_ENCAP_TYPE]);
+		__u16 flags = rta_getattr_u16(tb[IFLA_GRE_ENCAP_FLAGS]);
+		__u16 sport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_SPORT]);
+		__u16 dport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_DPORT]);
+
+		fputs("encap ", f);
+		switch (type) {
+		case TUNNEL_ENCAP_FOU:
+			fputs("fou ", f);
+			break;
+		case TUNNEL_ENCAP_GUE:
+			fputs("gue ", f);
+			break;
+		default:
+			fputs("unknown ", f);
+			break;
 		}
-	}
 
-	if (tb[IFLA_GRE_ERSPAN_INDEX]) {
-		__u32 erspan_idx = rta_getattr_u32(tb[IFLA_GRE_ERSPAN_INDEX]);
-
-		print_uint(PRINT_ANY,
-			   "erspan_index", "erspan_index %u ", erspan_idx);
-	}
-
-	if (tb[IFLA_GRE_ERSPAN_VER]) {
-		__u8 erspan_ver = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_VER]);
-
-		print_uint(PRINT_ANY,
-			   "erspan_ver", "erspan_ver %u ", erspan_ver);
-	}
-
-	if (tb[IFLA_GRE_ERSPAN_DIR]) {
-		__u8 erspan_dir = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_DIR]);
-
-		if (erspan_dir == 0)
-			print_string(PRINT_ANY, "erspan_dir",
-				     "erspan_dir ingress ", NULL);
+		if (sport == 0)
+			fputs("encap-sport auto ", f);
 		else
-			print_string(PRINT_ANY, "erspan_dir",
-				     "erspan_dir egress ", NULL);
+			fprintf(f, "encap-sport %u", ntohs(sport));
+
+		fprintf(f, "encap-dport %u ", ntohs(dport));
+
+		if (flags & TUNNEL_ENCAP_FLAG_CSUM)
+			fputs("encap-csum ", f);
+		else
+			fputs("noencap-csum ", f);
+
+		if (flags & TUNNEL_ENCAP_FLAG_CSUM6)
+			fputs("encap-csum6 ", f);
+		else
+			fputs("noencap-csum6 ", f);
+
+		if (flags & TUNNEL_ENCAP_FLAG_REMCSUM)
+			fputs("encap-remcsum ", f);
+		else
+			fputs("noencap-remcsum ", f);
 	}
+}
 
-	if (tb[IFLA_GRE_ERSPAN_HWID]) {
-		__u16 erspan_hwid = rta_getattr_u16(tb[IFLA_GRE_ERSPAN_HWID]);
-
-		print_0xhex(PRINT_ANY,
-			    "erspan_hwid", "erspan_hwid %#llx ", erspan_hwid);
-	}
-
-	tnl_print_encap(tb,
-			IFLA_GRE_ENCAP_TYPE,
-			IFLA_GRE_ENCAP_FLAGS,
-			IFLA_GRE_ENCAP_SPORT,
-			IFLA_GRE_ENCAP_DPORT);
+static void gre_print_help(struct link_util *lu, int argc, char **argv,
+	FILE *f)
+{
+	print_usage(f);
 }
 
 struct link_util gre_link_util = {
@@ -561,11 +489,3 @@
 	.print_opt = gre_print_opt,
 	.print_help = gre_print_help,
 };
-
-struct link_util erspan_link_util = {
-	.id = "erspan",
-	.maxattr = IFLA_GRE_MAX,
-	.parse_opt = gre_parse_opt,
-	.print_opt = gre_print_opt,
-	.print_help = gre_print_help,
-};
diff --git a/ip/link_gre6.c b/ip/link_gre6.c
index 9d1741b..e00ea09 100644
--- a/ip/link_gre6.c
+++ b/ip/link_gre6.c
@@ -30,105 +30,80 @@
 
 #define DEFAULT_TNL_HOP_LIMIT	(64)
 
-static void gre_print_help(struct link_util *lu, int argc, char **argv, FILE *f)
+static void print_usage(FILE *f)
 {
-	fprintf(f,
-		"Usage: ... %-9s	[ remote ADDR ]\n"
-		"			[ local ADDR ]\n"
-		"			[ [no][i|o]seq ]\n"
-		"			[ [i|o]key KEY | no[i|o]key ]\n"
-		"			[ [no][i|o]csum ]\n"
-		"			[ hoplimit TTL ]\n"
-		"			[ encaplimit ELIM ]\n"
-		"			[ tclass TCLASS ]\n"
-		"			[ flowlabel FLOWLABEL ]\n"
-		"			[ dscp inherit ]\n"
-		"			[ dev PHYS_DEV ]\n"
-		"			[ fwmark MARK ]\n"
-		"			[ [no]allow-localremote ]\n"
-		"			[ external ]\n"
-		"			[ noencap ]\n"
-		"			[ encap { fou | gue | none } ]\n"
-		"			[ encap-sport PORT ]\n"
-		"			[ encap-dport PORT ]\n"
-		"			[ [no]encap-csum ]\n"
-		"			[ [no]encap-csum6 ]\n"
-		"			[ [no]encap-remcsum ]\n"
-		"			[ erspan_ver version ]\n"
-		"			[ erspan IDX ]\n"
-		"			[ erspan_dir { ingress | egress } ]\n"
-		"			[ erspan_hwid hwid ]\n"
-		"\n"
-		"Where:	ADDR	  := IPV6_ADDRESS\n"
-		"	TTL	  := { 0..255 } (default=%d)\n"
-		"	KEY	  := { DOTTED_QUAD | NUMBER }\n"
-		"	ELIM	  := { none | 0..255 }(default=%d)\n"
-		"	TCLASS	  := { 0x0..0xff | inherit }\n"
-		"	FLOWLABEL := { 0x0..0xfffff | inherit }\n"
-		"	MARK	  := { 0x0..0xffffffff | inherit }\n",
-		lu->id,
-		DEFAULT_TNL_HOP_LIMIT, IPV6_DEFAULT_TNL_ENCAP_LIMIT);
+	fprintf(f, "Usage: ip link { add | set | change | replace | del } NAME\n");
+	fprintf(f, "          type { ip6gre | ip6gretap } [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(f, "          [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
+	fprintf(f, "          [ hoplimit TTL ] [ encaplimit ELIM ]\n");
+	fprintf(f, "          [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n");
+	fprintf(f, "          [ dscp inherit ] [ dev PHYS_DEV ]\n");
+	fprintf(f, "\n");
+	fprintf(f, "Where: NAME      := STRING\n");
+	fprintf(f, "       ADDR      := IPV6_ADDRESS\n");
+	fprintf(f, "       TTL       := { 0..255 } (default=%d)\n",
+		DEFAULT_TNL_HOP_LIMIT);
+	fprintf(f, "       KEY       := { DOTTED_QUAD | NUMBER }\n");
+	fprintf(f, "       ELIM      := { none | 0..255 }(default=%d)\n",
+		IPV6_DEFAULT_TNL_ENCAP_LIMIT);
+	fprintf(f, "       TCLASS    := { 0x0..0xff | inherit }\n");
+	fprintf(f, "       FLOWLABEL := { 0x0..0xfffff | inherit }\n");
+}
+
+static void usage(void) __attribute__((noreturn));
+static void usage(void)
+{
+	print_usage(stderr);
+	exit(-1);
 }
 
 static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
 			 struct nlmsghdr *n)
 {
-	struct ifinfomsg *ifi = NLMSG_DATA(n);
 	struct {
 		struct nlmsghdr n;
 		struct ifinfomsg i;
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_GETLINK,
-		.i.ifi_family = preferred_family,
-		.i.ifi_index = ifi->ifi_index,
-	};
-	struct nlmsghdr *answer;
+		char buf[1024];
+	} req;
+	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
 	struct rtattr *tb[IFLA_MAX + 1];
 	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
 	struct rtattr *greinfo[IFLA_GRE_MAX + 1];
-	int len;
 	__u16 iflags = 0;
 	__u16 oflags = 0;
-	__be32 ikey = 0;
-	__be32 okey = 0;
-	inet_prefix saddr, daddr;
+	unsigned ikey = 0;
+	unsigned okey = 0;
+	struct in6_addr raddr = IN6ADDR_ANY_INIT;
+	struct in6_addr laddr = IN6ADDR_ANY_INIT;
+	unsigned link = 0;
+	unsigned flowinfo = 0;
+	unsigned flags = 0;
 	__u8 hop_limit = DEFAULT_TNL_HOP_LIMIT;
 	__u8 encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
-	__u32 flowinfo = 0;
-	__u32 flags = 0;
-	__u32 link = 0;
-	__u16 encaptype = 0;
-	__u16 encapflags = TUNNEL_ENCAP_FLAG_CSUM6;
-	__u16 encapsport = 0;
-	__u16 encapdport = 0;
-	__u8 metadata = 0;
-	__u32 fwmark = 0;
-	__u32 erspan_idx = 0;
-	__u8 erspan_ver = 0;
-	__u8 erspan_dir = 0;
-	__u16 erspan_hwid = 0;
-
-	inet_prefix_reset(&saddr);
-	inet_prefix_reset(&daddr);
+	int len;
 
 	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
-		const struct rtattr *rta;
+		memset(&req, 0, sizeof(req));
 
-		if (rtnl_talk(&rth, &req.n, &answer) < 0) {
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETLINK;
+		req.i.ifi_family = preferred_family;
+		req.i.ifi_index = ifi->ifi_index;
+
+		if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) {
 get_failed:
 			fprintf(stderr,
 				"Failed to get existing tunnel info.\n");
 			return -1;
 		}
 
-		len = answer->nlmsg_len;
+		len = req.n.nlmsg_len;
 		len -= NLMSG_LENGTH(sizeof(*ifi));
 		if (len < 0)
 			goto get_failed;
 
-		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len);
+		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
 
 		if (!tb[IFLA_LINKINFO])
 			goto get_failed;
@@ -141,14 +116,6 @@
 		parse_rtattr_nested(greinfo, IFLA_GRE_MAX,
 				    linkinfo[IFLA_INFO_DATA]);
 
-		rta = greinfo[IFLA_GRE_LOCAL];
-		if (rta && get_addr_rta(&saddr, rta, AF_INET6))
-			goto get_failed;
-
-		rta = greinfo[IFLA_GRE_REMOTE];
-		if (rta && get_addr_rta(&daddr, rta, AF_INET6))
-			goto get_failed;
-
 		if (greinfo[IFLA_GRE_IKEY])
 			ikey = rta_getattr_u32(greinfo[IFLA_GRE_IKEY]);
 
@@ -161,6 +128,12 @@
 		if (greinfo[IFLA_GRE_OFLAGS])
 			oflags = rta_getattr_u16(greinfo[IFLA_GRE_OFLAGS]);
 
+		if (greinfo[IFLA_GRE_LOCAL])
+			memcpy(&laddr, RTA_DATA(greinfo[IFLA_GRE_LOCAL]), sizeof(laddr));
+
+		if (greinfo[IFLA_GRE_REMOTE])
+			memcpy(&raddr, RTA_DATA(greinfo[IFLA_GRE_REMOTE]), sizeof(raddr));
+
 		if (greinfo[IFLA_GRE_TTL])
 			hop_limit = rta_getattr_u8(greinfo[IFLA_GRE_TTL]);
 
@@ -175,119 +148,105 @@
 
 		if (greinfo[IFLA_GRE_FLAGS])
 			flags = rta_getattr_u32(greinfo[IFLA_GRE_FLAGS]);
-
-		if (greinfo[IFLA_GRE_ENCAP_TYPE])
-			encaptype = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_TYPE]);
-
-		if (greinfo[IFLA_GRE_ENCAP_FLAGS])
-			encapflags = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_FLAGS]);
-
-		if (greinfo[IFLA_GRE_ENCAP_SPORT])
-			encapsport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_SPORT]);
-
-		if (greinfo[IFLA_GRE_ENCAP_DPORT])
-			encapdport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_DPORT]);
-
-		if (greinfo[IFLA_GRE_COLLECT_METADATA])
-			metadata = 1;
-
-		if (greinfo[IFLA_GRE_FWMARK])
-			fwmark = rta_getattr_u32(greinfo[IFLA_GRE_FWMARK]);
-
-		if (greinfo[IFLA_GRE_ERSPAN_INDEX])
-			erspan_idx = rta_getattr_u32(greinfo[IFLA_GRE_ERSPAN_INDEX]);
-
-		if (greinfo[IFLA_GRE_ERSPAN_VER])
-			erspan_ver = rta_getattr_u8(greinfo[IFLA_GRE_ERSPAN_VER]);
-
-		if (greinfo[IFLA_GRE_ERSPAN_DIR])
-			erspan_dir = rta_getattr_u8(greinfo[IFLA_GRE_ERSPAN_DIR]);
-
-		if (greinfo[IFLA_GRE_ERSPAN_HWID])
-			erspan_hwid = rta_getattr_u16(greinfo[IFLA_GRE_ERSPAN_HWID]);
-
-		free(answer);
 	}
 
 	while (argc > 0) {
 		if (!matches(*argv, "key")) {
+			unsigned uval;
+
 			NEXT_ARG();
 			iflags |= GRE_KEY;
 			oflags |= GRE_KEY;
-			ikey = okey = tnl_parse_key("key", *argv);
-		} else if (!matches(*argv, "nokey")) {
-			iflags &= ~GRE_KEY;
-			oflags &= ~GRE_KEY;
-			ikey = okey = 0;
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0) < 0) {
+					fprintf(stderr,
+						"Invalid value for \"key\"\n");
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+
+			ikey = okey = uval;
 		} else if (!matches(*argv, "ikey")) {
+			unsigned uval;
+
 			NEXT_ARG();
 			iflags |= GRE_KEY;
-			ikey = tnl_parse_key("ikey", *argv);
-		} else if (!matches(*argv, "noikey")) {
-			iflags &= ~GRE_KEY;
-			ikey = 0;
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0)<0) {
+					fprintf(stderr, "invalid value of \"ikey\"\n");
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+			ikey = uval;
 		} else if (!matches(*argv, "okey")) {
+			unsigned uval;
+
 			NEXT_ARG();
 			oflags |= GRE_KEY;
-			okey = tnl_parse_key("okey", *argv);
-		} else if (!matches(*argv, "nookey")) {
-			oflags &= ~GRE_KEY;
-			okey = 0;
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0)<0) {
+					fprintf(stderr, "invalid value of \"okey\"\n");
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+			okey = uval;
 		} else if (!matches(*argv, "seq")) {
 			iflags |= GRE_SEQ;
 			oflags |= GRE_SEQ;
-		} else if (!matches(*argv, "noseq")) {
-			iflags &= ~GRE_SEQ;
-			oflags &= ~GRE_SEQ;
 		} else if (!matches(*argv, "iseq")) {
 			iflags |= GRE_SEQ;
-		} else if (!matches(*argv, "noiseq")) {
-			iflags &= ~GRE_SEQ;
 		} else if (!matches(*argv, "oseq")) {
 			oflags |= GRE_SEQ;
-		} else if (!matches(*argv, "nooseq")) {
-			oflags &= ~GRE_SEQ;
 		} else if (!matches(*argv, "csum")) {
 			iflags |= GRE_CSUM;
 			oflags |= GRE_CSUM;
-		} else if (!matches(*argv, "nocsum")) {
-			iflags &= ~GRE_CSUM;
-			oflags &= ~GRE_CSUM;
 		} else if (!matches(*argv, "icsum")) {
 			iflags |= GRE_CSUM;
-		} else if (!matches(*argv, "noicsum")) {
-			iflags &= ~GRE_CSUM;
 		} else if (!matches(*argv, "ocsum")) {
 			oflags |= GRE_CSUM;
-		} else if (!matches(*argv, "noocsum")) {
-			oflags &= ~GRE_CSUM;
 		} else if (!matches(*argv, "remote")) {
+			inet_prefix addr;
 			NEXT_ARG();
-			get_addr(&daddr, *argv, AF_INET6);
+			get_prefix(&addr, *argv, preferred_family);
+			if (addr.family == AF_UNSPEC)
+				invarg("\"remote\" address family is AF_UNSPEC", *argv);
+			memcpy(&raddr, &addr.data, sizeof(raddr));
 		} else if (!matches(*argv, "local")) {
+			inet_prefix addr;
 			NEXT_ARG();
-			get_addr(&saddr, *argv, AF_INET6);
+			get_prefix(&addr, *argv, preferred_family);
+			if (addr.family == AF_UNSPEC)
+				invarg("\"local\" address family is AF_UNSPEC", *argv);
+			memcpy(&laddr, &addr.data, sizeof(laddr));
 		} else if (!matches(*argv, "dev")) {
 			NEXT_ARG();
-			link = ll_name_to_index(*argv);
-			if (!link)
-				exit(nodev(*argv));
+			link = if_nametoindex(*argv);
+			if (link == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n",
+					*argv);
+				exit(-1);
+			}
 		} else if (!matches(*argv, "ttl") ||
-			   !matches(*argv, "hoplimit") ||
-			   !matches(*argv, "hlim")) {
+			   !matches(*argv, "hoplimit")) {
+			__u8 uval;
 			NEXT_ARG();
-			if (strcmp(*argv, "inherit") != 0) {
-				if (get_u8(&hop_limit, *argv, 0))
-					invarg("invalid HLIM\n", *argv);
-			} else
-				hop_limit = 0;
+			if (get_u8(&uval, *argv, 0))
+				invarg("invalid TTL", *argv);
+			hop_limit = uval;
 		} else if (!matches(*argv, "tos") ||
 			   !matches(*argv, "tclass") ||
 			   !matches(*argv, "dsfield")) {
 			__u8 uval;
-
 			NEXT_ARG();
-			flowinfo &= ~IP6_FLOWINFO_TCLASS;
 			if (strcmp(*argv, "inherit") == 0)
 				flags |= IP6_TNL_F_USE_ORIG_TCLASS;
 			else {
@@ -299,9 +258,7 @@
 		} else if (strcmp(*argv, "flowlabel") == 0 ||
 			   strcmp(*argv, "fl") == 0) {
 			__u32 uval;
-
 			NEXT_ARG();
-			flowinfo &= ~IP6_FLOWINFO_FLOWLABEL;
 			if (strcmp(*argv, "inherit") == 0)
 				flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
 			else {
@@ -317,218 +274,96 @@
 			if (strcmp(*argv, "inherit") != 0)
 				invarg("not inherit", *argv);
 			flags |= IP6_TNL_F_RCV_DSCP_COPY;
-		} else if (strcmp(*argv, "noencap") == 0) {
-			encaptype = TUNNEL_ENCAP_NONE;
-		} else if (strcmp(*argv, "encap") == 0) {
-			NEXT_ARG();
-			if (strcmp(*argv, "fou") == 0)
-				encaptype = TUNNEL_ENCAP_FOU;
-			else if (strcmp(*argv, "gue") == 0)
-				encaptype = TUNNEL_ENCAP_GUE;
-			else if (strcmp(*argv, "none") == 0)
-				encaptype = TUNNEL_ENCAP_NONE;
-			else
-				invarg("Invalid encap type.", *argv);
-		} else if (strcmp(*argv, "encap-sport") == 0) {
-			NEXT_ARG();
-			if (strcmp(*argv, "auto") == 0)
-				encapsport = 0;
-			else if (get_u16(&encapsport, *argv, 0))
-				invarg("Invalid source port.", *argv);
-		} else if (strcmp(*argv, "encap-dport") == 0) {
-			NEXT_ARG();
-			if (get_u16(&encapdport, *argv, 0))
-				invarg("Invalid destination port.", *argv);
-		} else if (strcmp(*argv, "encap-csum") == 0) {
-			encapflags |= TUNNEL_ENCAP_FLAG_CSUM;
-		} else if (strcmp(*argv, "noencap-csum") == 0) {
-			encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM;
-		} else if (strcmp(*argv, "encap-udp6-csum") == 0) {
-			encapflags |= TUNNEL_ENCAP_FLAG_CSUM6;
-		} else if (strcmp(*argv, "noencap-udp6-csum") == 0) {
-			encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM6;
-		} else if (strcmp(*argv, "encap-remcsum") == 0) {
-			encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM;
-		} else if (strcmp(*argv, "noencap-remcsum") == 0) {
-			encapflags &= ~TUNNEL_ENCAP_FLAG_REMCSUM;
-		} else if (strcmp(*argv, "external") == 0) {
-			metadata = 1;
-		} else if (strcmp(*argv, "fwmark") == 0) {
-			NEXT_ARG();
-			if (strcmp(*argv, "inherit") == 0) {
-				flags |= IP6_TNL_F_USE_ORIG_FWMARK;
-				fwmark = 0;
-			} else {
-				if (get_u32(&fwmark, *argv, 0))
-					invarg("invalid fwmark\n", *argv);
-				flags &= ~IP6_TNL_F_USE_ORIG_FWMARK;
-			}
-		} else if (strcmp(*argv, "allow-localremote") == 0) {
-			flags |= IP6_TNL_F_ALLOW_LOCAL_REMOTE;
-		} else if (strcmp(*argv, "noallow-localremote") == 0) {
-			flags &= ~IP6_TNL_F_ALLOW_LOCAL_REMOTE;
-		} else if (strcmp(*argv, "encaplimit") == 0) {
-			NEXT_ARG();
-			if (strcmp(*argv, "none") == 0) {
-				flags |= IP6_TNL_F_IGN_ENCAP_LIMIT;
-			} else {
-				__u8 uval;
-
-				if (get_u8(&uval, *argv, 0))
-					invarg("invalid ELIM", *argv);
-				encap_limit = uval;
-				flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT;
-			}
-		} else if (strcmp(*argv, "erspan") == 0) {
-			NEXT_ARG();
-			if (get_u32(&erspan_idx, *argv, 0))
-				invarg("invalid erspan index\n", *argv);
-			if (erspan_idx & ~((1<<20) - 1) || erspan_idx == 0)
-				invarg("erspan index must be > 0 and <= 20-bit\n", *argv);
-		} else if (strcmp(*argv, "erspan_ver") == 0) {
-			NEXT_ARG();
-			if (get_u8(&erspan_ver, *argv, 0))
-				invarg("invalid erspan version\n", *argv);
-			if (erspan_ver != 1 && erspan_ver != 2)
-				invarg("erspan version must be 1 or 2\n", *argv);
-		} else if (strcmp(*argv, "erspan_dir") == 0) {
-			NEXT_ARG();
-			if (matches(*argv, "ingress") == 0)
-				erspan_dir = 0;
-			else if (matches(*argv, "egress") == 0)
-				erspan_dir = 1;
-			else
-				invarg("Invalid erspan direction.", *argv);
-		} else if (strcmp(*argv, "erspan_hwid") == 0) {
-			NEXT_ARG();
-			if (get_u16(&erspan_hwid, *argv, 0))
-				invarg("invalid erspan hwid\n", *argv);
-		} else {
-			gre_print_help(lu, argc, argv, stderr);
-			return -1;
-		}
+		} else
+			usage();
 		argc--; argv++;
 	}
 
-	if (metadata) {
-		addattr_l(n, 1024, IFLA_GRE_COLLECT_METADATA, NULL, 0);
-		return 0;
-	}
-
 	addattr32(n, 1024, IFLA_GRE_IKEY, ikey);
 	addattr32(n, 1024, IFLA_GRE_OKEY, okey);
 	addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2);
 	addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2);
-	if (is_addrtype_inet_not_unspec(&saddr))
-		addattr_l(n, 1024, IFLA_GRE_LOCAL, saddr.data, saddr.bytelen);
-	if (is_addrtype_inet_not_unspec(&daddr))
-		addattr_l(n, 1024, IFLA_GRE_REMOTE, daddr.data, daddr.bytelen);
+	addattr_l(n, 1024, IFLA_GRE_LOCAL, &laddr, sizeof(laddr));
+	addattr_l(n, 1024, IFLA_GRE_REMOTE, &raddr, sizeof(raddr));
 	if (link)
 		addattr32(n, 1024, IFLA_GRE_LINK, link);
 	addattr_l(n, 1024, IFLA_GRE_TTL, &hop_limit, 1);
 	addattr_l(n, 1024, IFLA_GRE_ENCAP_LIMIT, &encap_limit, 1);
 	addattr_l(n, 1024, IFLA_GRE_FLOWINFO, &flowinfo, 4);
-	addattr32(n, 1024, IFLA_GRE_FLAGS, flags);
-	addattr32(n, 1024, IFLA_GRE_FWMARK, fwmark);
-	if (erspan_ver) {
-		addattr8(n, 1024, IFLA_GRE_ERSPAN_VER, erspan_ver);
-		if (erspan_ver == 1 && erspan_idx != 0) {
-			addattr32(n, 1024, IFLA_GRE_ERSPAN_INDEX, erspan_idx);
-		} else if (erspan_ver == 2) {
-			addattr8(n, 1024, IFLA_GRE_ERSPAN_DIR, erspan_dir);
-			addattr16(n, 1024, IFLA_GRE_ERSPAN_HWID, erspan_hwid);
-		}
-	}
-	addattr16(n, 1024, IFLA_GRE_ENCAP_TYPE, encaptype);
-	addattr16(n, 1024, IFLA_GRE_ENCAP_FLAGS, encapflags);
-	addattr16(n, 1024, IFLA_GRE_ENCAP_SPORT, htons(encapsport));
-	addattr16(n, 1024, IFLA_GRE_ENCAP_DPORT, htons(encapdport));
+	addattr_l(n, 1024, IFLA_GRE_FLAGS, &flowinfo, 4);
 
 	return 0;
 }
 
 static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
+	char s1[1024];
 	char s2[64];
-	__u16 iflags = 0;
-	__u16 oflags = 0;
-	__u32 flags = 0;
-	__u32 flowinfo = 0;
-	__u8 ttl = 0;
+	const char *local = "any";
+	const char *remote = "any";
+	unsigned iflags = 0;
+	unsigned oflags = 0;
+	unsigned flags = 0;
+	unsigned flowinfo = 0;
+	struct in6_addr in6_addr_any = IN6ADDR_ANY_INIT;
 
 	if (!tb)
 		return;
 
-	if (tb[IFLA_GRE_COLLECT_METADATA]) {
-		print_bool(PRINT_ANY, "external", "external ", true);
-		return;
-	}
-
 	if (tb[IFLA_GRE_FLAGS])
 		flags = rta_getattr_u32(tb[IFLA_GRE_FLAGS]);
 
 	if (tb[IFLA_GRE_FLOWINFO])
-		flowinfo = rta_getattr_u32(tb[IFLA_GRE_FLOWINFO]);
+		flags = rta_getattr_u32(tb[IFLA_GRE_FLOWINFO]);
 
-	tnl_print_endpoint("remote", tb[IFLA_GRE_REMOTE], AF_INET6);
-	tnl_print_endpoint("local", tb[IFLA_GRE_LOCAL], AF_INET6);
+	if (tb[IFLA_GRE_REMOTE]) {
+		struct in6_addr addr;
+		memcpy(&addr, RTA_DATA(tb[IFLA_GRE_REMOTE]), sizeof(addr));
 
-	if (tb[IFLA_GRE_LINK]) {
-		__u32 link = rta_getattr_u32(tb[IFLA_GRE_LINK]);
-
-		if (link) {
-			print_string(PRINT_ANY, "link", "dev %s ",
-				     ll_index_to_name(link));
-		}
+		if (memcmp(&addr, &in6_addr_any, sizeof(addr)))
+			remote = format_host(AF_INET6, sizeof(addr), &addr, s1, sizeof(s1));
 	}
 
-	if (tb[IFLA_GRE_TTL])
-		ttl = rta_getattr_u8(tb[IFLA_GRE_TTL]);
-	if (is_json_context() || ttl)
-		print_uint(PRINT_ANY, "ttl", "hoplimit %u ", ttl);
+	fprintf(f, "remote %s ", remote);
+
+	if (tb[IFLA_GRE_LOCAL]) {
+		struct in6_addr addr;
+		memcpy(&addr, RTA_DATA(tb[IFLA_GRE_LOCAL]), sizeof(addr));
+
+		if (memcmp(&addr, &in6_addr_any, sizeof(addr)))
+			local = format_host(AF_INET6, sizeof(addr), &addr, s1, sizeof(s1));
+	}
+
+	fprintf(f, "local %s ", local);
+
+	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);
+		else
+			fprintf(f, "dev %u ", link);
+	}
+
+	if (tb[IFLA_GRE_TTL] && rta_getattr_u8(tb[IFLA_GRE_TTL]))
+		fprintf(f, "hoplimit %d ", rta_getattr_u8(tb[IFLA_GRE_TTL]));
+
+	if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
+		fprintf(f, "encaplimit none ");
+	else if (tb[IFLA_GRE_ENCAP_LIMIT]) {
+		int encap_limit = rta_getattr_u8(tb[IFLA_GRE_ENCAP_LIMIT]);
+
+		fprintf(f, "encaplimit %d ", encap_limit);
+	}
+
+	if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
+		fprintf(f, "flowlabel inherit ");
 	else
-		print_string(PRINT_FP, NULL, "hoplimit %s ", "inherit");
-
-	if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT) {
-		print_bool(PRINT_ANY,
-			   "ip6_tnl_f_ign_encap_limit",
-			   "encaplimit none ",
-			   true);
-	} else if (tb[IFLA_GRE_ENCAP_LIMIT]) {
-		__u8 val = rta_getattr_u8(tb[IFLA_GRE_ENCAP_LIMIT]);
-
-		print_uint(PRINT_ANY, "encap_limit", "encaplimit %u ", val);
-	}
-
-	if (flags & IP6_TNL_F_USE_ORIG_TCLASS) {
-		print_bool(PRINT_ANY,
-			   "ip6_tnl_f_use_orig_tclass",
-			   "tclass inherit ",
-			   true);
-	} else if (tb[IFLA_GRE_FLOWINFO]) {
-		__u32 val = ntohl(flowinfo & IP6_FLOWINFO_TCLASS) >> 20;
-
-		snprintf(s2, sizeof(s2), "0x%02x", val);
-		print_string(PRINT_ANY, "tclass", "tclass %s ", s2);
-	}
-
-	if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) {
-		print_bool(PRINT_ANY,
-			   "ip6_tnl_f_use_orig_flowlabel",
-			   "flowlabel inherit ",
-			   true);
-	} else if (tb[IFLA_GRE_FLOWINFO]) {
-		__u32 val = ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL);
-
-		snprintf(s2, sizeof(s2), "0x%05x", val);
-		print_string(PRINT_ANY, "flowlabel", "flowlabel %s ", s2);
-	}
+		fprintf(f, "flowlabel 0x%05x ", ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
 
 	if (flags & IP6_TNL_F_RCV_DSCP_COPY)
-		print_bool(PRINT_ANY,
-			   "ip6_tnl_f_rcv_dscp_copy",
-			   "dscp inherit ",
-			   true);
+		fprintf(f, "dscp inherit ");
 
 	if (tb[IFLA_GRE_IFLAGS])
 		iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]);
@@ -538,80 +373,28 @@
 
 	if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) {
 		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2));
-		print_string(PRINT_ANY, "ikey", "ikey %s ", s2);
+		fprintf(f, "ikey %s ", s2);
 	}
 
 	if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) {
 		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2));
-		print_string(PRINT_ANY, "okey", "okey %s ", s2);
+		fprintf(f, "okey %s ", s2);
 	}
 
 	if (iflags & GRE_SEQ)
-		print_bool(PRINT_ANY, "iseq", "iseq ", true);
+		fputs("iseq ", f);
 	if (oflags & GRE_SEQ)
-		print_bool(PRINT_ANY, "oseq", "oseq ", true);
+		fputs("oseq ", f);
 	if (iflags & GRE_CSUM)
-		print_bool(PRINT_ANY, "icsum", "icsum ", true);
+		fputs("icsum ", f);
 	if (oflags & GRE_CSUM)
-		print_bool(PRINT_ANY, "ocsum", "ocsum ", true);
+		fputs("ocsum ", f);
+}
 
-	if (flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE)
-		print_bool(PRINT_ANY,
-			   "ip6_tnl_f_allow_local_remote",
-			   "allow-localremote ",
-			   true);
-
-	if (flags & IP6_TNL_F_USE_ORIG_FWMARK) {
-		print_bool(PRINT_ANY,
-			   "ip6_tnl_f_use_orig_fwmark",
-			   "fwmark inherit ",
-			   true);
-	} else if (tb[IFLA_GRE_FWMARK]) {
-		__u32 fwmark = rta_getattr_u32(tb[IFLA_GRE_FWMARK]);
-
-		if (fwmark) {
-			print_0xhex(PRINT_ANY,
-				    "fwmark", "fwmark %#llx ", fwmark);
-		}
-	}
-
-	if (tb[IFLA_GRE_ERSPAN_INDEX]) {
-		__u32 erspan_idx = rta_getattr_u32(tb[IFLA_GRE_ERSPAN_INDEX]);
-
-		print_uint(PRINT_ANY,
-			   "erspan_index", "erspan_index %u ", erspan_idx);
-	}
-
-	if (tb[IFLA_GRE_ERSPAN_VER]) {
-		__u8 erspan_ver = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_VER]);
-
-		print_uint(PRINT_ANY,
-			   "erspan_ver", "erspan_ver %u ", erspan_ver);
-	}
-
-	if (tb[IFLA_GRE_ERSPAN_DIR]) {
-		__u8 erspan_dir = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_DIR]);
-
-		if (erspan_dir == 0)
-			print_string(PRINT_ANY, "erspan_dir",
-				     "erspan_dir ingress ", NULL);
-		else
-			print_string(PRINT_ANY, "erspan_dir",
-				     "erspan_dir egress ", NULL);
-	}
-
-	if (tb[IFLA_GRE_ERSPAN_HWID]) {
-		__u16 erspan_hwid = rta_getattr_u16(tb[IFLA_GRE_ERSPAN_HWID]);
-
-		print_0xhex(PRINT_ANY,
-			    "erspan_hwid", "erspan_hwid %#llx ", erspan_hwid);
-	}
-
-	tnl_print_encap(tb,
-			IFLA_GRE_ENCAP_TYPE,
-			IFLA_GRE_ENCAP_FLAGS,
-			IFLA_GRE_ENCAP_SPORT,
-			IFLA_GRE_ENCAP_DPORT);
+static void gre_print_help(struct link_util *lu, int argc, char **argv,
+	FILE *f)
+{
+	print_usage(f);
 }
 
 struct link_util ip6gre_link_util = {
@@ -629,11 +412,3 @@
 	.print_opt = gre_print_opt,
 	.print_help = gre_print_help,
 };
-
-struct link_util ip6erspan_link_util = {
-	.id = "ip6erspan",
-	.maxattr = IFLA_GRE_MAX,
-	.parse_opt = gre_parse_opt,
-	.print_opt = gre_print_opt,
-	.print_help = gre_print_help,
-};
diff --git a/ip/link_ip6tnl.c b/ip/link_ip6tnl.c
index c7b49b0..f771c75 100644
--- a/ip/link_ip6tnl.c
+++ b/ip/link_ip6tnl.c
@@ -29,92 +29,79 @@
 
 #define DEFAULT_TNL_HOP_LIMIT	(64)
 
-static void ip6tunnel_print_help(struct link_util *lu, int argc, char **argv,
-				 FILE *f)
+static void print_usage(FILE *f)
 {
-	fprintf(f,
-		"Usage: ... %-6s	[ remote ADDR ]\n"
-		"			[ local ADDR ]\n"
-		"			[ encaplimit ELIM ]\n"
-		"			[ hoplimit HLIM ]\n"
-		"			[ tclass TCLASS ]\n"
-		"			[ flowlabel FLOWLABEL ]\n"
-		"			[ dscp inherit ]\n"
-		"			[ [no]allow-localremote ]\n"
-		"			[ dev PHYS_DEV ]\n"
-		"			[ fwmark MARK ]\n"
-		"			[ external ]\n"
-		"			[ noencap ]\n"
-		"			[ encap { fou | gue | none } ]\n"
-		"			[ encap-sport PORT ]\n"
-		"			[ encap-dport PORT ]\n"
-		"			[ [no]encap-csum ]\n"
-		"			[ [no]encap-csum6 ]\n"
-		"			[ [no]encap-remcsum ]\n"
-		"			[ mode { ip6ip6 | ipip6 | any } ]\n"
-		"\n"
-		"Where:	ADDR	  := IPV6_ADDRESS\n"
-		"	ELIM	  := { none | 0..255 }(default=%d)\n"
-		"	HLIM	  := 0..255 (default=%d)\n"
-		"	TCLASS    := { 0x0..0xff | inherit }\n"
-		"	FLOWLABEL := { 0x0..0xfffff | inherit }\n"
-		"	MARK	  := { 0x0..0xffffffff | inherit }\n",
-		lu->id,
-		IPV6_DEFAULT_TNL_ENCAP_LIMIT, DEFAULT_TNL_HOP_LIMIT);
+	fprintf(f, "Usage: ip link { add | set | change | replace | del } NAME\n");
+	fprintf(f, "          [ mode { ip6ip6 | ipip6 | any } ]\n");
+	fprintf(f, "          type ip6tnl [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(f, "          [ dev PHYS_DEV ] [ encaplimit ELIM ]\n");
+	fprintf(f ,"          [ hoplimit HLIM ] [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n");
+	fprintf(f, "          [ dscp inherit ] [ fwmark inherit ]\n");
+	fprintf(f, "\n");
+	fprintf(f, "Where: NAME      := STRING\n");
+	fprintf(f, "       ADDR      := IPV6_ADDRESS\n");
+	fprintf(f, "       ELIM      := { none | 0..255 }(default=%d)\n",
+		IPV6_DEFAULT_TNL_ENCAP_LIMIT);
+	fprintf(f, "       HLIM      := 0..255 (default=%d)\n",
+		DEFAULT_TNL_HOP_LIMIT);
+	fprintf(f, "       TCLASS    := { 0x0..0xff | inherit }\n");
+	fprintf(f, "       FLOWLABEL := { 0x0..0xfffff | inherit }\n");
+}
+
+static void usage(void) __attribute__((noreturn));
+static void usage(void)
+{
+	print_usage(stderr);
+	exit(-1);
 }
 
 static int ip6tunnel_parse_opt(struct link_util *lu, int argc, char **argv,
 			       struct nlmsghdr *n)
 {
-	struct ifinfomsg *ifi = NLMSG_DATA(n);
 	struct {
 		struct nlmsghdr n;
 		struct ifinfomsg i;
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_GETLINK,
-		.i.ifi_family = preferred_family,
-		.i.ifi_index = ifi->ifi_index,
-	};
-	struct nlmsghdr *answer;
+		char buf[2048];
+	} req;
+	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
 	struct rtattr *tb[IFLA_MAX + 1];
 	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
 	struct rtattr *iptuninfo[IFLA_IPTUN_MAX + 1];
 	int len;
-	inet_prefix saddr, daddr;
+	struct in6_addr laddr;
+	struct in6_addr raddr;
 	__u8 hop_limit = DEFAULT_TNL_HOP_LIMIT;
 	__u8 encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
 	__u32 flowinfo = 0;
 	__u32 flags = 0;
-	__u8 proto = 0;
 	__u32 link = 0;
-	__u16 encaptype = 0;
-	__u16 encapflags = TUNNEL_ENCAP_FLAG_CSUM6;
-	__u16 encapsport = 0;
-	__u16 encapdport = 0;
-	__u8 metadata = 0;
-	__u32 fwmark = 0;
+	__u8 proto = 0;
 
-	inet_prefix_reset(&saddr);
-	inet_prefix_reset(&daddr);
+	memset(&laddr, 0, sizeof(laddr));
+	memset(&raddr, 0, sizeof(raddr));
 
 	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
-		const struct rtattr *rta;
+		memset(&req, 0, sizeof(req));
 
-		if (rtnl_talk(&rth, &req.n, &answer) < 0) {
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETLINK;
+		req.i.ifi_family = preferred_family;
+		req.i.ifi_index = ifi->ifi_index;
+
+		if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) {
 get_failed:
 			fprintf(stderr,
 				"Failed to get existing tunnel info.\n");
 			return -1;
 		}
 
-		len = answer->nlmsg_len;
+		len = req.n.nlmsg_len;
 		len -= NLMSG_LENGTH(sizeof(*ifi));
 		if (len < 0)
 			goto get_failed;
 
-		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len);
+		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
 
 		if (!tb[IFLA_LINKINFO])
 			goto get_failed;
@@ -127,13 +114,13 @@
 		parse_rtattr_nested(iptuninfo, IFLA_IPTUN_MAX,
 				    linkinfo[IFLA_INFO_DATA]);
 
-		rta = iptuninfo[IFLA_IPTUN_LOCAL];
-		if (rta && get_addr_rta(&saddr, rta, AF_INET6))
-			goto get_failed;
+		if (iptuninfo[IFLA_IPTUN_LOCAL])
+			memcpy(&laddr, RTA_DATA(iptuninfo[IFLA_IPTUN_LOCAL]),
+			       sizeof(laddr));
 
-		rta = iptuninfo[IFLA_IPTUN_REMOTE];
-		if (rta && get_addr_rta(&daddr, rta, AF_INET6))
-			goto get_failed;
+		if (iptuninfo[IFLA_IPTUN_REMOTE])
+			memcpy(&raddr, RTA_DATA(iptuninfo[IFLA_IPTUN_REMOTE]),
+			       sizeof(raddr));
 
 		if (iptuninfo[IFLA_IPTUN_TTL])
 			hop_limit = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TTL]);
@@ -152,17 +139,10 @@
 
 		if (iptuninfo[IFLA_IPTUN_PROTO])
 			proto = rta_getattr_u8(iptuninfo[IFLA_IPTUN_PROTO]);
-		if (iptuninfo[IFLA_IPTUN_COLLECT_METADATA])
-			metadata = 1;
-
-		if (iptuninfo[IFLA_IPTUN_FWMARK])
-			fwmark = rta_getattr_u32(iptuninfo[IFLA_IPTUN_FWMARK]);
-
-		free(answer);
 	}
 
 	while (argc > 0) {
-		if (strcmp(*argv, "mode") == 0) {
+		if (matches(*argv, "mode") == 0) {
 			NEXT_ARG();
 			if (strcmp(*argv, "ipv6/ipv6") == 0 ||
 			    strcmp(*argv, "ip6ip6") == 0)
@@ -178,43 +158,48 @@
 			else
 				invarg("Cannot guess tunnel mode.", *argv);
 		} else if (strcmp(*argv, "remote") == 0) {
+			inet_prefix addr;
 			NEXT_ARG();
-			get_addr(&daddr, *argv, AF_INET6);
+			get_prefix(&addr, *argv, preferred_family);
+			if (addr.family == AF_UNSPEC)
+				invarg("\"remote\" address family is AF_UNSPEC", *argv);
+			memcpy(&raddr, addr.data, addr.bytelen);
 		} else if (strcmp(*argv, "local") == 0) {
+			inet_prefix addr;
 			NEXT_ARG();
-			get_addr(&saddr, *argv, AF_INET6);
+			get_prefix(&addr, *argv, preferred_family);
+			if (addr.family == AF_UNSPEC)
+				invarg("\"local\" address family is AF_UNSPEC", *argv);
+			memcpy(&laddr, addr.data, addr.bytelen);
 		} else if (matches(*argv, "dev") == 0) {
 			NEXT_ARG();
-			link = ll_name_to_index(*argv);
-			if (!link)
-				exit(nodev(*argv));
-		} else if (strcmp(*argv, "ttl") == 0 ||
-			   strcmp(*argv, "hoplimit") == 0 ||
+			link = if_nametoindex(*argv);
+			if (link == 0)
+				invarg("\"dev\" is invalid", *argv);
+		} else if (strcmp(*argv, "hoplimit") == 0 ||
+			   strcmp(*argv, "ttl") == 0 ||
 			   strcmp(*argv, "hlim") == 0) {
+			__u8 uval;
 			NEXT_ARG();
-			if (strcmp(*argv, "inherit") != 0) {
-				if (get_u8(&hop_limit, *argv, 0))
-					invarg("invalid HLIM\n", *argv);
-			} else
-				hop_limit = 0;
-		} else if (strcmp(*argv, "encaplimit") == 0) {
+			if (get_u8(&uval, *argv, 0))
+				invarg("invalid HLIM", *argv);
+			hop_limit = uval;
+		} else if (matches(*argv, "encaplimit") == 0) {
 			NEXT_ARG();
 			if (strcmp(*argv, "none") == 0) {
 				flags |= IP6_TNL_F_IGN_ENCAP_LIMIT;
 			} else {
 				__u8 uval;
-
 				if (get_u8(&uval, *argv, 0) < -1)
 					invarg("invalid ELIM", *argv);
 				encap_limit = uval;
 				flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT;
 			}
-		} else if (strcmp(*argv, "tos") == 0 ||
-			   strcmp(*argv, "tclass") == 0 ||
+		} else if (strcmp(*argv, "tclass") == 0 ||
 			   strcmp(*argv, "tc") == 0 ||
+			   strcmp(*argv, "tos") == 0 ||
 			   matches(*argv, "dsfield") == 0) {
 			__u8 uval;
-
 			NEXT_ARG();
 			flowinfo &= ~IP6_FLOWINFO_TCLASS;
 			if (strcmp(*argv, "inherit") == 0)
@@ -228,7 +213,6 @@
 		} else if (strcmp(*argv, "flowlabel") == 0 ||
 			   strcmp(*argv, "fl") == 0) {
 			__u32 uval;
-
 			NEXT_ARG();
 			flowinfo &= ~IP6_FLOWINFO_FLOWLABEL;
 			if (strcmp(*argv, "inherit") == 0)
@@ -248,105 +232,36 @@
 			flags |= IP6_TNL_F_RCV_DSCP_COPY;
 		} else if (strcmp(*argv, "fwmark") == 0) {
 			NEXT_ARG();
-			if (strcmp(*argv, "inherit") == 0) {
-				flags |= IP6_TNL_F_USE_ORIG_FWMARK;
-				fwmark = 0;
-			} else {
-				if (get_u32(&fwmark, *argv, 0))
-					invarg("invalid fwmark\n", *argv);
-				flags &= ~IP6_TNL_F_USE_ORIG_FWMARK;
-			}
-		} else if (strcmp(*argv, "allow-localremote") == 0) {
-			flags |= IP6_TNL_F_ALLOW_LOCAL_REMOTE;
-		} else if (strcmp(*argv, "noallow-localremote") == 0) {
-			flags &= ~IP6_TNL_F_ALLOW_LOCAL_REMOTE;
-		} else if (strcmp(*argv, "noencap") == 0) {
-			encaptype = TUNNEL_ENCAP_NONE;
-		} else if (strcmp(*argv, "encap") == 0) {
-			NEXT_ARG();
-			if (strcmp(*argv, "fou") == 0)
-				encaptype = TUNNEL_ENCAP_FOU;
-			else if (strcmp(*argv, "gue") == 0)
-				encaptype = TUNNEL_ENCAP_GUE;
-			else if (strcmp(*argv, "none") == 0)
-				encaptype = TUNNEL_ENCAP_NONE;
-			else
-				invarg("Invalid encap type.", *argv);
-		} else if (strcmp(*argv, "encap-sport") == 0) {
-			NEXT_ARG();
-			if (strcmp(*argv, "auto") == 0)
-				encapsport = 0;
-			else if (get_u16(&encapsport, *argv, 0))
-				invarg("Invalid source port.", *argv);
-		} else if (strcmp(*argv, "encap-dport") == 0) {
-			NEXT_ARG();
-			if (get_u16(&encapdport, *argv, 0))
-				invarg("Invalid destination port.", *argv);
-		} else if (strcmp(*argv, "encap-csum") == 0) {
-			encapflags |= TUNNEL_ENCAP_FLAG_CSUM;
-		} else if (strcmp(*argv, "noencap-csum") == 0) {
-			encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM;
-		} else if (strcmp(*argv, "encap-udp6-csum") == 0) {
-			encapflags |= TUNNEL_ENCAP_FLAG_CSUM6;
-		} else if (strcmp(*argv, "noencap-udp6-csum") == 0) {
-			encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM6;
-		} else if (strcmp(*argv, "encap-remcsum") == 0) {
-			encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM;
-		} else if (strcmp(*argv, "noencap-remcsum") == 0) {
-			encapflags &= ~TUNNEL_ENCAP_FLAG_REMCSUM;
-		} else if (strcmp(*argv, "external") == 0) {
-			metadata = 1;
-		} else {
-			ip6tunnel_print_help(lu, argc, argv, stderr);
-			return -1;
-		}
+			if (strcmp(*argv, "inherit") != 0)
+				invarg("not inherit", *argv);
+			flags |= IP6_TNL_F_USE_ORIG_FWMARK;
+		} else
+			usage();
 		argc--, argv++;
 	}
 
 	addattr8(n, 1024, IFLA_IPTUN_PROTO, proto);
-	if (metadata) {
-		addattr_l(n, 1024, IFLA_IPTUN_COLLECT_METADATA, NULL, 0);
-		return 0;
-	}
-
-	if (is_addrtype_inet_not_unspec(&saddr)) {
-		addattr_l(n, 1024, IFLA_IPTUN_LOCAL,
-			  saddr.data, saddr.bytelen);
-	}
-	if (is_addrtype_inet_not_unspec(&daddr)) {
-		addattr_l(n, 1024, IFLA_IPTUN_REMOTE,
-			  daddr.data, daddr.bytelen);
-	}
+	addattr_l(n, 1024, IFLA_IPTUN_LOCAL, &laddr, sizeof(laddr));
+	addattr_l(n, 1024, IFLA_IPTUN_REMOTE, &raddr, sizeof(raddr));
 	addattr8(n, 1024, IFLA_IPTUN_TTL, hop_limit);
 	addattr8(n, 1024, IFLA_IPTUN_ENCAP_LIMIT, encap_limit);
 	addattr32(n, 1024, IFLA_IPTUN_FLOWINFO, flowinfo);
 	addattr32(n, 1024, IFLA_IPTUN_FLAGS, flags);
 	addattr32(n, 1024, IFLA_IPTUN_LINK, link);
-	addattr32(n, 1024, IFLA_IPTUN_FWMARK, fwmark);
-
-	addattr16(n, 1024, IFLA_IPTUN_ENCAP_TYPE, encaptype);
-	addattr16(n, 1024, IFLA_IPTUN_ENCAP_FLAGS, encapflags);
-	addattr16(n, 1024, IFLA_IPTUN_ENCAP_SPORT, htons(encapsport));
-	addattr16(n, 1024, IFLA_IPTUN_ENCAP_DPORT, htons(encapdport));
 
 	return 0;
 }
 
 static void ip6tunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
+	char s1[256];
 	char s2[64];
-	__u32 flags = 0;
+	int flags = 0;
 	__u32 flowinfo = 0;
-	__u8 ttl = 0;
 
 	if (!tb)
 		return;
 
-	if (tb[IFLA_IPTUN_COLLECT_METADATA]) {
-		print_bool(PRINT_ANY, "external", "external ", true);
-		return;
-	}
-
 	if (tb[IFLA_IPTUN_FLAGS])
 		flags = rta_getattr_u32(tb[IFLA_IPTUN_FLAGS]);
 
@@ -356,105 +271,81 @@
 	if (tb[IFLA_IPTUN_PROTO]) {
 		switch (rta_getattr_u8(tb[IFLA_IPTUN_PROTO])) {
 		case IPPROTO_IPIP:
-			print_string(PRINT_ANY, "proto", "%s ", "ipip6");
+			fprintf(f, "ipip6 ");
 			break;
 		case IPPROTO_IPV6:
-			print_string(PRINT_ANY, "proto", "%s ", "ip6ip6");
+			fprintf(f, "ip6ip6 ");
 			break;
 		case 0:
-			print_string(PRINT_ANY, "proto", "%s ", "any");
+			fprintf(f, "any ");
 			break;
 		}
 	}
 
-	tnl_print_endpoint("remote", tb[IFLA_IPTUN_REMOTE], AF_INET6);
-	tnl_print_endpoint("local", tb[IFLA_IPTUN_LOCAL], AF_INET6);
-
-	if (tb[IFLA_IPTUN_LINK]) {
-		__u32 link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]);
-
-		if (link) {
-			print_string(PRINT_ANY, "link", "dev %s ",
-				     ll_index_to_name(link));
-		}
+	if (tb[IFLA_IPTUN_REMOTE]) {
+		fprintf(f, "remote %s ",
+			rt_addr_n2a(AF_INET6,
+				    RTA_PAYLOAD(tb[IFLA_IPTUN_REMOTE]),
+				    RTA_DATA(tb[IFLA_IPTUN_REMOTE]),
+				    s1, sizeof(s1)));
 	}
 
+	if (tb[IFLA_IPTUN_LOCAL]) {
+		fprintf(f, "local %s ",
+			rt_addr_n2a(AF_INET6,
+				    RTA_PAYLOAD(tb[IFLA_IPTUN_LOCAL]),
+				    RTA_DATA(tb[IFLA_IPTUN_LOCAL]),
+				    s1, sizeof(s1)));
+	}
+
+	if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) {
+		unsigned link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]);
+		const char *n = if_indextoname(link, s2);
+
+		if (n)
+			fprintf(f, "dev %s ", n);
+		else
+			fprintf(f, "dev %u ", link);
+	}
+
+	if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
+		printf("encaplimit none ");
+	else if (tb[IFLA_IPTUN_ENCAP_LIMIT])
+		fprintf(f, "encaplimit %u ",
+			rta_getattr_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]));
+
 	if (tb[IFLA_IPTUN_TTL])
-		ttl = rta_getattr_u8(tb[IFLA_IPTUN_TTL]);
-	if (is_json_context() || ttl)
-		print_uint(PRINT_ANY, "ttl", "hoplimit %u ", ttl);
+		fprintf(f, "hoplimit %u ", rta_getattr_u8(tb[IFLA_IPTUN_TTL]));
+
+	if (flags & IP6_TNL_F_USE_ORIG_TCLASS)
+		printf("tclass inherit ");
+	else if (tb[IFLA_IPTUN_FLOWINFO]) {
+		__u32 val = ntohl(flowinfo & IP6_FLOWINFO_TCLASS);
+
+		printf("tclass 0x%02x ", (__u8)(val >> 20));
+	}
+
+	if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
+		printf("flowlabel inherit ");
 	else
-		print_string(PRINT_FP, NULL, "hoplimit %s ", "inherit");
+		printf("flowlabel 0x%05x ", ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
 
-	if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT) {
-		print_bool(PRINT_ANY,
-			   "ip6_tnl_f_ign_encap_limit",
-			   "encaplimit none ",
-			   true);
-	} else if (tb[IFLA_IPTUN_ENCAP_LIMIT]) {
-		__u8 val = rta_getattr_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]);
-
-		print_uint(PRINT_ANY, "encap_limit", "encaplimit %u ", val);
-	}
-
-	if (flags & IP6_TNL_F_USE_ORIG_TCLASS) {
-		print_bool(PRINT_ANY,
-			   "ip6_tnl_f_use_orig_tclass",
-			   "tclass inherit ",
-			   true);
-	} else if (tb[IFLA_IPTUN_FLOWINFO]) {
-		__u32 val = ntohl(flowinfo & IP6_FLOWINFO_TCLASS) >> 20;
-
-		snprintf(s2, sizeof(s2), "0x%02x", val);
-		print_string(PRINT_ANY, "tclass", "tclass %s ", s2);
-	}
-
-	if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) {
-		print_bool(PRINT_ANY,
-			   "ip6_tnl_f_use_orig_flowlabel",
-			   "flowlabel inherit ",
-			   true);
-	} else if (tb[IFLA_IPTUN_FLOWINFO]) {
-		__u32 val = ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL);
-
-		snprintf(s2, sizeof(s2), "0x%05x", val);
-		print_string(PRINT_ANY, "flowlabel", "flowlabel %s ", s2);
-	}
+	printf("(flowinfo 0x%08x) ", ntohl(flowinfo));
 
 	if (flags & IP6_TNL_F_RCV_DSCP_COPY)
-		print_bool(PRINT_ANY,
-			   "ip6_tnl_f_rcv_dscp_copy",
-			   "dscp inherit ",
-			   true);
+		printf("dscp inherit ");
 
 	if (flags & IP6_TNL_F_MIP6_DEV)
-		print_bool(PRINT_ANY, "ip6_tnl_f_mip6_dev", "mip6 ", true);
+		fprintf(f, "mip6 ");
 
-	if (flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE)
-		print_bool(PRINT_ANY,
-			   "ip6_tnl_f_allow_local_remote",
-			   "allow-localremote ",
-			   true);
+	if (flags & IP6_TNL_F_USE_ORIG_FWMARK)
+		fprintf(f, "fwmark inherit ");
+}
 
-	if (flags & IP6_TNL_F_USE_ORIG_FWMARK) {
-		print_bool(PRINT_ANY,
-			   "ip6_tnl_f_use_orig_fwmark",
-			   "fwmark inherit ",
-			   true);
-	} else if (tb[IFLA_IPTUN_FWMARK]) {
-		__u32 fwmark = rta_getattr_u32(tb[IFLA_IPTUN_FWMARK]);
-
-		if (fwmark) {
-			print_0xhex(PRINT_ANY,
-				    "fwmark", "fwmark %#llx ", fwmark);
-		}
-	}
-
-	tnl_print_encap(tb,
-			IFLA_IPTUN_ENCAP_TYPE,
-			IFLA_IPTUN_ENCAP_FLAGS,
-			IFLA_IPTUN_ENCAP_SPORT,
-			IFLA_IPTUN_ENCAP_DPORT);
+static void ip6tunnel_print_help(struct link_util *lu, int argc, char **argv,
+	FILE *f)
+{
+	print_usage(f);
 }
 
 struct link_util ip6tnl_link_util = {
diff --git a/ip/link_iptnl.c b/ip/link_iptnl.c
index 636cdb2..9d6bc98 100644
--- a/ip/link_iptnl.c
+++ b/ip/link_iptnl.c
@@ -16,7 +16,6 @@
 #include <sys/socket.h>
 #include <arpa/inet.h>
 
-#include <linux/in.h>
 #include <linux/ip.h>
 #include <linux/if_tunnel.h>
 #include "rt_names.h"
@@ -24,101 +23,87 @@
 #include "ip_common.h"
 #include "tunnel.h"
 
-static void iptunnel_print_help(struct link_util *lu, int argc, char **argv,
-				FILE *f)
+static void print_usage(FILE *f, int sit)
 {
-	const char *mode;
-
-	if (strcmp(lu->id, "sit") == 0) {
-		mode =	"{ ip6ip | ipip | mplsip | any } ]\n"
-			"			[ isatap";
-	} else {
-		mode = "{ ipip | mplsip | any }";
+	fprintf(f, "Usage: ip link { add | set | change | replace | del } NAME\n");
+	fprintf(f, "          type { ipip | sit } [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(f, "          [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n");
+	fprintf(f, "          [ 6rd-prefix ADDR ] [ 6rd-relay_prefix ADDR ] [ 6rd-reset ]\n");
+	fprintf(f, "          [ noencap ] [ encap { fou | gue | none } ]\n");
+	fprintf(f, "          [ encap-sport PORT ] [ encap-dport PORT ]\n");
+	fprintf(f, "          [ [no]encap-csum ] [ [no]encap-csum6 ] [ [no]encap-remcsum ]\n");
+	if (sit) {
+		fprintf(f, "          [ mode { ip6ip | ipip | any } ]\n");
+		fprintf(f, "          [ isatap ]\n");
 	}
+	fprintf(f, "\n");
+	fprintf(f, "Where: NAME := STRING\n");
+	fprintf(f, "       ADDR := { IP_ADDRESS | any }\n");
+	fprintf(f, "       TOS  := { NUMBER | inherit }\n");
+	fprintf(f, "       TTL  := { 1..255 | inherit }\n");
+}
 
-	fprintf(f,
-		"Usage: ... %-6s	[ remote ADDR ]\n"
-		"			[ local ADDR ]\n"
-		"			[ ttl TTL ]\n"
-		"			[ tos TOS ]\n"
-		"			[ [no]pmtudisc ]\n"
-		"			[ 6rd-prefix ADDR ]\n"
-		"			[ 6rd-relay_prefix ADDR ]\n"
-		"			[ 6rd-reset ]\n"
-		"			[ dev PHYS_DEV ]\n"
-		"			[ fwmark MARK ]\n"
-		"			[ external ]\n"
-		"			[ noencap ]\n"
-		"			[ encap { fou | gue | none } ]\n"
-		"			[ encap-sport PORT ]\n"
-		"			[ encap-dport PORT ]\n"
-		"			[ [no]encap-csum ]\n"
-		"			[ [no]encap-csum6 ]\n"
-		"			[ [no]encap-remcsum ]\n"
-		"			[ mode %s ]\n"
-		"\n"
-		"Where:	ADDR := { IP_ADDRESS | any }\n"
-		"	TOS  := { NUMBER | inherit }\n"
-		"	TTL  := { 1..255 | inherit }\n"
-		"	MARK := { 0x0..0xffffffff }\n",
-		lu->id, mode);
+static void usage(int sit) __attribute__((noreturn));
+static void usage(int sit)
+{
+	print_usage(stderr, sit);
+	exit(-1);
 }
 
 static int iptunnel_parse_opt(struct link_util *lu, int argc, char **argv,
 			      struct nlmsghdr *n)
 {
-	struct ifinfomsg *ifi = NLMSG_DATA(n);
 	struct {
 		struct nlmsghdr n;
 		struct ifinfomsg i;
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_GETLINK,
-		.i.ifi_family = preferred_family,
-		.i.ifi_index = ifi->ifi_index,
-	};
-	struct nlmsghdr *answer;
+		char buf[2048];
+	} req;
+	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
 	struct rtattr *tb[IFLA_MAX + 1];
 	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
 	struct rtattr *iptuninfo[IFLA_IPTUN_MAX + 1];
 	int len;
-	inet_prefix saddr, daddr, ip6rdprefix, ip6rdrelayprefix;
-	__u8 pmtudisc = 1;
-	__u8 tos = 0;
-	__u16 iflags = 0;
-	__u8 ttl = 0;
-	__u8 proto = 0;
 	__u32 link = 0;
+	__u32 laddr = 0;
+	__u32 raddr = 0;
+	__u8 ttl = 0;
+	__u8 tos = 0;
+	__u8 pmtudisc = 1;
+	__u16 iflags = 0;
+	__u8 proto = 0;
+	struct in6_addr ip6rdprefix;
+	__u16 ip6rdprefixlen = 0;
+	__u32 ip6rdrelayprefix = 0;
+	__u16 ip6rdrelayprefixlen = 0;
 	__u16 encaptype = 0;
 	__u16 encapflags = 0;
 	__u16 encapsport = 0;
 	__u16 encapdport = 0;
-	__u8 metadata = 0;
-	__u32 fwmark = 0;
 
-	inet_prefix_reset(&saddr);
-	inet_prefix_reset(&daddr);
-
-	inet_prefix_reset(&ip6rdprefix);
-	inet_prefix_reset(&ip6rdrelayprefix);
+	memset(&ip6rdprefix, 0, sizeof(ip6rdprefix));
 
 	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
-		const struct rtattr *rta;
+		memset(&req, 0, sizeof(req));
 
-		if (rtnl_talk(&rth, &req.n, &answer) < 0) {
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETLINK;
+		req.i.ifi_family = preferred_family;
+		req.i.ifi_index = ifi->ifi_index;
+
+		if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) {
 get_failed:
 			fprintf(stderr,
 				"Failed to get existing tunnel info.\n");
 			return -1;
 		}
 
-		len = answer->nlmsg_len;
+		len = req.n.nlmsg_len;
 		len -= NLMSG_LENGTH(sizeof(*ifi));
 		if (len < 0)
 			goto get_failed;
 
-		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len);
+		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
 
 		if (!tb[IFLA_LINKINFO])
 			goto get_failed;
@@ -131,38 +116,22 @@
 		parse_rtattr_nested(iptuninfo, IFLA_IPTUN_MAX,
 				    linkinfo[IFLA_INFO_DATA]);
 
-		rta = iptuninfo[IFLA_IPTUN_LOCAL];
-		if (rta && get_addr_rta(&saddr, rta, AF_INET))
-			goto get_failed;
+		if (iptuninfo[IFLA_IPTUN_LOCAL])
+			laddr = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LOCAL]);
 
-		rta = iptuninfo[IFLA_IPTUN_REMOTE];
-		if (rta && get_addr_rta(&daddr, rta, AF_INET))
-			goto get_failed;
-
-		rta = iptuninfo[IFLA_IPTUN_6RD_PREFIX];
-		if (rta && get_addr_rta(&ip6rdprefix, rta, AF_INET6))
-			goto get_failed;
-
-		rta = iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIX];
-		if (rta && get_addr_rta(&ip6rdrelayprefix, rta, AF_INET))
-			goto get_failed;
-
-		rta = iptuninfo[IFLA_IPTUN_6RD_PREFIXLEN];
-		ip6rdprefix.bitlen = rta ? rta_getattr_u16(rta) : 0;
-
-		rta = iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN];
-		ip6rdrelayprefix.bitlen = rta ? rta_getattr_u16(rta) : 0;
+		if (iptuninfo[IFLA_IPTUN_REMOTE])
+			raddr = rta_getattr_u32(iptuninfo[IFLA_IPTUN_REMOTE]);
 
 		if (iptuninfo[IFLA_IPTUN_TTL])
 			ttl = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TTL]);
 
+		if (iptuninfo[IFLA_IPTUN_TOS])
+			tos = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TOS]);
+
 		if (iptuninfo[IFLA_IPTUN_PMTUDISC])
 			pmtudisc =
 				rta_getattr_u8(iptuninfo[IFLA_IPTUN_PMTUDISC]);
 
-		if (iptuninfo[IFLA_IPTUN_TOS])
-			tos = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TOS]);
-
 		if (iptuninfo[IFLA_IPTUN_FLAGS])
 			iflags = rta_getattr_u16(iptuninfo[IFLA_IPTUN_FLAGS]);
 
@@ -180,49 +149,44 @@
 			encapsport = rta_getattr_u16(iptuninfo[IFLA_IPTUN_ENCAP_SPORT]);
 		if (iptuninfo[IFLA_IPTUN_ENCAP_DPORT])
 			encapdport = rta_getattr_u16(iptuninfo[IFLA_IPTUN_ENCAP_DPORT]);
+		if (iptuninfo[IFLA_IPTUN_6RD_PREFIX])
+			memcpy(&ip6rdprefix,
+			       RTA_DATA(iptuninfo[IFLA_IPTUN_6RD_PREFIX]),
+			       sizeof(laddr));
 
-		if (iptuninfo[IFLA_IPTUN_COLLECT_METADATA])
-			metadata = 1;
+		if (iptuninfo[IFLA_IPTUN_6RD_PREFIXLEN])
+			ip6rdprefixlen =
+				rta_getattr_u16(iptuninfo[IFLA_IPTUN_6RD_PREFIXLEN]);
 
-		if (iptuninfo[IFLA_IPTUN_FWMARK])
-			fwmark = rta_getattr_u32(iptuninfo[IFLA_IPTUN_FWMARK]);
+		if (iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIX])
+			ip6rdrelayprefix =
+				rta_getattr_u32(iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIX]);
 
-		free(answer);
+		if (iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN])
+			ip6rdrelayprefixlen =
+				rta_getattr_u16(iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]);
 	}
 
 	while (argc > 0) {
-		if (strcmp(*argv, "mode") == 0) {
+		if (strcmp(*argv, "remote") == 0) {
 			NEXT_ARG();
-			if (strcmp(lu->id, "sit") == 0 &&
-			    (strcmp(*argv, "ipv6/ipv4") == 0 ||
-			     strcmp(*argv, "ip6ip") == 0))
-				proto = IPPROTO_IPV6;
-			else if (strcmp(*argv, "ipv4/ipv4") == 0 ||
-				 strcmp(*argv, "ipip") == 0 ||
-				 strcmp(*argv, "ip4ip4") == 0)
-				proto = IPPROTO_IPIP;
-			else if (strcmp(*argv, "mpls/ipv4") == 0 ||
-				   strcmp(*argv, "mplsip") == 0)
-				proto = IPPROTO_MPLS;
-			else if (strcmp(*argv, "any/ipv4") == 0 ||
-				 strcmp(*argv, "any") == 0)
-				proto = 0;
+			if (strcmp(*argv, "any"))
+				raddr = get_addr32(*argv);
 			else
-				invarg("Cannot guess tunnel mode.", *argv);
-		} else if (strcmp(*argv, "remote") == 0) {
-			NEXT_ARG();
-			get_addr(&daddr, *argv, AF_INET);
+				raddr = 0;
 		} else if (strcmp(*argv, "local") == 0) {
 			NEXT_ARG();
-			get_addr(&saddr, *argv, AF_INET);
+			if (strcmp(*argv, "any"))
+				laddr = get_addr32(*argv);
+			else
+				laddr = 0;
 		} else if (matches(*argv, "dev") == 0) {
 			NEXT_ARG();
-			link = ll_name_to_index(*argv);
-			if (!link)
-				exit(nodev(*argv));
+			link = if_nametoindex(*argv);
+			if (link == 0)
+				invarg("\"dev\" is invalid", *argv);
 		} else if (strcmp(*argv, "ttl") == 0 ||
-			   strcmp(*argv, "hoplimit") == 0 ||
-			   strcmp(*argv, "hlim") == 0) {
+			   strcmp(*argv, "hoplimit") == 0) {
 			NEXT_ARG();
 			if (strcmp(*argv, "inherit") != 0) {
 				if (get_u8(&ttl, *argv, 0))
@@ -231,10 +195,8 @@
 				ttl = 0;
 		} else if (strcmp(*argv, "tos") == 0 ||
 			   strcmp(*argv, "tclass") == 0 ||
-			   strcmp(*argv, "tc") == 0 ||
 			   matches(*argv, "dsfield") == 0) {
 			__u32 uval;
-
 			NEXT_ARG();
 			if (strcmp(*argv, "inherit") != 0) {
 				if (rtnl_dsfield_a2n(&uval, *argv))
@@ -249,6 +211,21 @@
 		} else if (strcmp(lu->id, "sit") == 0 &&
 			   strcmp(*argv, "isatap") == 0) {
 			iflags |= SIT_ISATAP;
+		} else if (strcmp(lu->id, "sit") == 0 &&
+			   strcmp(*argv, "mode") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "ipv6/ipv4") == 0 ||
+			    strcmp(*argv, "ip6ip") == 0)
+				proto = IPPROTO_IPV6;
+			else if (strcmp(*argv, "ipv4/ipv4") == 0 ||
+				 strcmp(*argv, "ipip") == 0 ||
+				 strcmp(*argv, "ip4ip4") == 0)
+				proto = IPPROTO_IPIP;
+			else if (strcmp(*argv, "any/ipv4") == 0 ||
+				 strcmp(*argv, "any") == 0)
+				proto = 0;
+			else
+				invarg("Cannot guess tunnel mode.", *argv);
 		} else if (strcmp(*argv, "noencap") == 0) {
 			encaptype = TUNNEL_ENCAP_NONE;
 		} else if (strcmp(*argv, "encap") == 0) {
@@ -283,27 +260,29 @@
 			encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM;
 		} else if (strcmp(*argv, "noencap-remcsum") == 0) {
 			encapflags &= ~TUNNEL_ENCAP_FLAG_REMCSUM;
-		} else if (strcmp(*argv, "external") == 0) {
-			metadata = 1;
 		} else if (strcmp(*argv, "6rd-prefix") == 0) {
+			inet_prefix prefix;
 			NEXT_ARG();
-			if (get_prefix(&ip6rdprefix, *argv, AF_INET6))
+			if (get_prefix(&prefix, *argv, AF_INET6))
 				invarg("invalid 6rd_prefix\n", *argv);
+			memcpy(&ip6rdprefix, prefix.data, 16);
+			ip6rdprefixlen = prefix.bitlen;
 		} else if (strcmp(*argv, "6rd-relay_prefix") == 0) {
+			inet_prefix prefix;
 			NEXT_ARG();
-			if (get_prefix(&ip6rdrelayprefix, *argv, AF_INET))
+			if (get_prefix(&prefix, *argv, AF_INET))
 				invarg("invalid 6rd-relay_prefix\n", *argv);
+			memcpy(&ip6rdrelayprefix, prefix.data, 4);
+			ip6rdrelayprefixlen = prefix.bitlen;
 		} else if (strcmp(*argv, "6rd-reset") == 0) {
-			get_prefix(&ip6rdprefix, "2002::/16", AF_INET6);
-			inet_prefix_reset(&ip6rdrelayprefix);
-		} else if (strcmp(*argv, "fwmark") == 0) {
-			NEXT_ARG();
-			if (get_u32(&fwmark, *argv, 0))
-				invarg("invalid fwmark\n", *argv);
-		} else {
-			iptunnel_print_help(lu, argc, argv, stderr);
-			return -1;
-		}
+			inet_prefix prefix;
+			get_prefix(&prefix, "2002::", AF_INET6);
+			memcpy(&ip6rdprefix, prefix.data, 16);
+			ip6rdprefixlen = 16;
+			ip6rdrelayprefix = 0;
+			ip6rdrelayprefixlen = 0;
+		} else
+			usage(strcmp(lu->id, "sit") == 0);
 		argc--, argv++;
 	}
 
@@ -312,25 +291,12 @@
 		exit(-1);
 	}
 
-	addattr8(n, 1024, IFLA_IPTUN_PROTO, proto);
-	if (metadata) {
-		addattr_l(n, 1024, IFLA_IPTUN_COLLECT_METADATA, NULL, 0);
-		return 0;
-	}
-
-	if (is_addrtype_inet_not_unspec(&saddr)) {
-		addattr_l(n, 1024, IFLA_IPTUN_LOCAL,
-			  saddr.data, saddr.bytelen);
-	}
-	if (is_addrtype_inet_not_unspec(&daddr)) {
-		addattr_l(n, 1024, IFLA_IPTUN_REMOTE,
-			  daddr.data, daddr.bytelen);
-	}
-	addattr8(n, 1024, IFLA_IPTUN_PMTUDISC, pmtudisc);
-	addattr8(n, 1024, IFLA_IPTUN_TOS, tos);
-	addattr8(n, 1024, IFLA_IPTUN_TTL, ttl);
 	addattr32(n, 1024, IFLA_IPTUN_LINK, link);
-	addattr32(n, 1024, IFLA_IPTUN_FWMARK, fwmark);
+	addattr32(n, 1024, IFLA_IPTUN_LOCAL, laddr);
+	addattr32(n, 1024, IFLA_IPTUN_REMOTE, raddr);
+	addattr8(n, 1024, IFLA_IPTUN_TTL, ttl);
+	addattr8(n, 1024, IFLA_IPTUN_TOS, tos);
+	addattr8(n, 1024, IFLA_IPTUN_PMTUDISC, pmtudisc);
 
 	addattr16(n, 1024, IFLA_IPTUN_ENCAP_TYPE, encaptype);
 	addattr16(n, 1024, IFLA_IPTUN_ENCAP_FLAGS, encapflags);
@@ -339,17 +305,16 @@
 
 	if (strcmp(lu->id, "sit") == 0) {
 		addattr16(n, 1024, IFLA_IPTUN_FLAGS, iflags);
-		if (is_addrtype_inet(&ip6rdprefix)) {
+		addattr8(n, 1024, IFLA_IPTUN_PROTO, proto);
+		if (ip6rdprefixlen) {
 			addattr_l(n, 1024, IFLA_IPTUN_6RD_PREFIX,
-				  ip6rdprefix.data, ip6rdprefix.bytelen);
+				  &ip6rdprefix, sizeof(ip6rdprefix));
 			addattr16(n, 1024, IFLA_IPTUN_6RD_PREFIXLEN,
-				  ip6rdprefix.bitlen);
-		}
-		if (is_addrtype_inet(&ip6rdrelayprefix)) {
+				  ip6rdprefixlen);
 			addattr32(n, 1024, IFLA_IPTUN_6RD_RELAY_PREFIX,
-				  ip6rdrelayprefix.data[0]);
+				  ip6rdrelayprefix);
 			addattr16(n, 1024, IFLA_IPTUN_6RD_RELAY_PREFIXLEN,
-				  ip6rdrelayprefix.bitlen);
+				  ip6rdrelayprefixlen);
 		}
 	}
 
@@ -358,126 +323,137 @@
 
 static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
+	char s1[1024];
 	char s2[64];
-	__u16 prefixlen;
-	__u8 ttl = 0;
-	__u8 tos = 0;
+	const char *local = "any";
+	const char *remote = "any";
 
 	if (!tb)
 		return;
 
-	if (tb[IFLA_IPTUN_COLLECT_METADATA]) {
-		print_bool(PRINT_ANY, "external", "external ", true);
-		return;
+	if (tb[IFLA_IPTUN_REMOTE]) {
+		unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_REMOTE]);
+
+		if (addr)
+			remote = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
 	}
 
-	if (tb[IFLA_IPTUN_PROTO]) {
-		switch (rta_getattr_u8(tb[IFLA_IPTUN_PROTO])) {
-		case IPPROTO_IPIP:
-			print_string(PRINT_ANY, "proto", "%s ", "ipip");
-			break;
-		case IPPROTO_IPV6:
-			print_string(PRINT_ANY, "proto", "%s ", "ip6ip");
-			break;
-		case IPPROTO_MPLS:
-			print_string(PRINT_ANY, "proto", "%s ", "mplsip");
-			break;
-		case 0:
-			print_string(PRINT_ANY, "proto", "%s ", "any");
-			break;
-		}
+	fprintf(f, "remote %s ", remote);
+
+	if (tb[IFLA_IPTUN_LOCAL]) {
+		unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_LOCAL]);
+
+		if (addr)
+			local = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
 	}
 
-	tnl_print_endpoint("remote", tb[IFLA_IPTUN_REMOTE], AF_INET);
-	tnl_print_endpoint("local", tb[IFLA_IPTUN_LOCAL], AF_INET);
+	fprintf(f, "local %s ", local);
 
-	if (tb[IFLA_IPTUN_LINK]) {
-		__u32 link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]);
+	if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) {
+		unsigned link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]);
+		const char *n = if_indextoname(link, s2);
 
-		if (link) {
-			print_string(PRINT_ANY, "link", "dev %s ",
-				     ll_index_to_name(link));
-		}
-	}
-
-	if (tb[IFLA_IPTUN_TTL])
-		ttl = rta_getattr_u8(tb[IFLA_IPTUN_TTL]);
-	if (is_json_context() || ttl)
-		print_uint(PRINT_ANY, "ttl", "ttl %u ", ttl);
-	else
-		print_string(PRINT_FP, NULL, "ttl %s ", "inherit");
-
-	if (tb[IFLA_IPTUN_TOS])
-		tos = rta_getattr_u8(tb[IFLA_IPTUN_TOS]);
-	if (tos) {
-		if (is_json_context() || tos != 1)
-			print_0xhex(PRINT_ANY, "tos", "tos %#llx ", tos);
+		if (n)
+			fprintf(f, "dev %s ", n);
 		else
-			print_string(PRINT_FP, NULL, "tos %s ", "inherit");
+			fprintf(f, "dev %u ", link);
+	}
+
+	if (tb[IFLA_IPTUN_TTL] && rta_getattr_u8(tb[IFLA_IPTUN_TTL]))
+		fprintf(f, "ttl %d ", rta_getattr_u8(tb[IFLA_IPTUN_TTL]));
+	else
+		fprintf(f, "ttl inherit ");
+
+	if (tb[IFLA_IPTUN_TOS] && rta_getattr_u8(tb[IFLA_IPTUN_TOS])) {
+		int tos = rta_getattr_u8(tb[IFLA_IPTUN_TOS]);
+
+		fputs("tos ", f);
+		if (tos == 1)
+			fputs("inherit ", f);
+		else
+			fprintf(f, "0x%x ", tos);
 	}
 
 	if (tb[IFLA_IPTUN_PMTUDISC] && rta_getattr_u8(tb[IFLA_IPTUN_PMTUDISC]))
-		print_bool(PRINT_ANY, "pmtudisc", "pmtudisc ", true);
+		fprintf(f, "pmtudisc ");
 	else
-		print_bool(PRINT_ANY, "pmtudisc", "nopmtudisc ", false);
+		fprintf(f, "nopmtudisc ");
 
 	if (tb[IFLA_IPTUN_FLAGS]) {
 		__u16 iflags = rta_getattr_u16(tb[IFLA_IPTUN_FLAGS]);
 
 		if (iflags & SIT_ISATAP)
-			print_bool(PRINT_ANY, "isatap", "isatap ", true);
+			fprintf(f, "isatap ");
 	}
 
 	if (tb[IFLA_IPTUN_6RD_PREFIXLEN] &&
-	    (prefixlen = rta_getattr_u16(tb[IFLA_IPTUN_6RD_PREFIXLEN]))) {
+	    *(__u16 *)RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIXLEN])) {
+		__u16 prefixlen = rta_getattr_u16(tb[IFLA_IPTUN_6RD_PREFIXLEN]);
 		__u16 relayprefixlen =
 			rta_getattr_u16(tb[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]);
 		__u32 relayprefix =
 			rta_getattr_u32(tb[IFLA_IPTUN_6RD_RELAY_PREFIX]);
 
-		const char *prefix = inet_ntop(AF_INET6,
-					       RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIX]),
-					       s2, sizeof(s2));
-
-		if (is_json_context()) {
-			print_string(PRINT_JSON, "prefix", NULL, prefix);
-			print_int(PRINT_JSON, "prefixlen", NULL, prefixlen);
-			if (relayprefix) {
-				print_string(PRINT_JSON,
-					     "relay_prefix",
-					     NULL,
-					     format_host(AF_INET,
-							 4,
-							 &relayprefix));
-				print_int(PRINT_JSON,
-					  "relay_prefixlen",
-					  NULL,
-					  relayprefixlen);
-			}
-		} else {
-			printf("6rd-prefix %s/%u ", prefix, prefixlen);
-			if (relayprefix) {
-				printf("6rd-relay_prefix %s/%u ",
-				       format_host(AF_INET, 4, &relayprefix),
-				       relayprefixlen);
-			}
+		printf("6rd-prefix %s/%u ",
+		       inet_ntop(AF_INET6, RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIX]),
+				 s1, sizeof(s1)),
+		       prefixlen);
+		if (relayprefix) {
+			printf("6rd-relay_prefix %s/%u ",
+			       format_host(AF_INET, 4, &relayprefix, s1,
+					   sizeof(s1)),
+			       relayprefixlen);
 		}
 	}
 
-	if (tb[IFLA_IPTUN_FWMARK]) {
-		__u32 fwmark = rta_getattr_u32(tb[IFLA_IPTUN_FWMARK]);
+	if (tb[IFLA_IPTUN_ENCAP_TYPE] &&
+	    *(__u16 *)RTA_DATA(tb[IFLA_IPTUN_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE) {
+		__u16 type = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_TYPE]);
+		__u16 flags = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_FLAGS]);
+		__u16 sport = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_SPORT]);
+		__u16 dport = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_DPORT]);
 
-		if (fwmark) {
-			print_0xhex(PRINT_ANY,
-				    "fwmark", "fwmark %#llx ", fwmark);
+		fputs("encap ", f);
+		switch (type) {
+		case TUNNEL_ENCAP_FOU:
+			fputs("fou ", f);
+			break;
+		case TUNNEL_ENCAP_GUE:
+			fputs("gue ", f);
+			break;
+		default:
+			fputs("unknown ", f);
+			break;
 		}
-	}
 
-	tnl_print_encap(tb,
-			IFLA_IPTUN_ENCAP_TYPE,
-			IFLA_IPTUN_ENCAP_FLAGS,
-			IFLA_IPTUN_ENCAP_SPORT,
-			IFLA_IPTUN_ENCAP_DPORT);
+		if (sport == 0)
+			fputs("encap-sport auto ", f);
+		else
+			fprintf(f, "encap-sport %u", ntohs(sport));
+
+		fprintf(f, "encap-dport %u ", ntohs(dport));
+
+		if (flags & TUNNEL_ENCAP_FLAG_CSUM)
+			fputs("encap-csum ", f);
+		else
+			fputs("noencap-csum ", f);
+
+		if (flags & TUNNEL_ENCAP_FLAG_CSUM6)
+			fputs("encap-csum6 ", f);
+		else
+			fputs("noencap-csum6 ", f);
+
+		if (flags & TUNNEL_ENCAP_FLAG_REMCSUM)
+			fputs("encap-remcsum ", f);
+		else
+			fputs("noencap-remcsum ", f);
+	}
+}
+
+static void iptunnel_print_help(struct link_util *lu, int argc, char **argv,
+	FILE *f)
+{
+	print_usage(f, strcmp(lu->id, "sit") == 0);
 }
 
 struct link_util ipip_link_util = {
diff --git a/ip/link_veth.c b/ip/link_veth.c
index 33e8f2b..314216c 100644
--- a/ip/link_veth.c
+++ b/ip/link_veth.c
@@ -29,47 +29,58 @@
 }
 
 static int veth_parse_opt(struct link_util *lu, int argc, char **argv,
-			  struct nlmsghdr *n)
+			  struct nlmsghdr *hdr)
 {
+	char *dev = NULL;
+	char *name = NULL;
+	char *link = NULL;
 	char *type = NULL;
-	int err;
-	struct rtattr *data;
+	int index = 0;
+	int err, len;
+	struct rtattr * data;
+	int group;
 	struct ifinfomsg *ifm, *peer_ifm;
-	unsigned int ifi_flags, ifi_change, ifi_index;
+	unsigned int ifi_flags, ifi_change;
 
 	if (strcmp(argv[0], "peer") != 0) {
 		usage();
 		return -1;
 	}
 
-	ifm = NLMSG_DATA(n);
+	ifm = NLMSG_DATA(hdr);
 	ifi_flags = ifm->ifi_flags;
 	ifi_change = ifm->ifi_change;
-	ifi_index = ifm->ifi_index;
 	ifm->ifi_flags = 0;
 	ifm->ifi_change = 0;
-	ifm->ifi_index = 0;
 
-	data = addattr_nest(n, 1024, VETH_INFO_PEER);
+	data = NLMSG_TAIL(hdr);
+	addattr_l(hdr, 1024, VETH_INFO_PEER, NULL, 0);
 
-	n->nlmsg_len += sizeof(struct ifinfomsg);
+	hdr->nlmsg_len += sizeof(struct ifinfomsg);
 
-	err = iplink_parse(argc - 1, argv + 1, (struct iplink_req *)n, &type);
+	err = iplink_parse(argc - 1, argv + 1, (struct iplink_req *)hdr,
+			   &name, &type, &link, &dev, &group, &index);
 	if (err < 0)
 		return err;
 
-	if (type)
-		duparg("type", argv[err]);
+	if (name) {
+		len = strlen(name) + 1;
+		if (len > IFNAMSIZ)
+			invarg("\"name\" too long\n", *argv);
+		addattr_l(hdr, 1024, IFLA_IFNAME, name, len);
+	}
 
 	peer_ifm = RTA_DATA(data);
-	peer_ifm->ifi_index = ifm->ifi_index;
+	peer_ifm->ifi_index = index;
 	peer_ifm->ifi_flags = ifm->ifi_flags;
 	peer_ifm->ifi_change = ifm->ifi_change;
 	ifm->ifi_flags = ifi_flags;
 	ifm->ifi_change = ifi_change;
-	ifm->ifi_index = ifi_index;
 
-	addattr_nest_end(n, data);
+	if (group != -1)
+		addattr32(hdr, 1024, IFLA_GROUP, group);
+
+	data->rta_len = (void *)NLMSG_TAIL(hdr) - (void *)data;
 	return argc - 1 - err;
 }
 
diff --git a/ip/link_vti.c b/ip/link_vti.c
index 3a52ea8..f3fea33 100644
--- a/ip/link_vti.c
+++ b/ip/link_vti.c
@@ -23,65 +23,67 @@
 #include "ip_common.h"
 #include "tunnel.h"
 
-static void vti_print_help(struct link_util *lu, int argc, char **argv, FILE *f)
+
+static void print_usage(FILE *f)
 {
-	fprintf(f,
-		"Usage: ... %-4s	[ remote ADDR ]\n"
-		"		[ local ADDR ]\n"
-		"		[ [i|o]key KEY ]\n"
-		"		[ dev PHYS_DEV ]\n"
-		"		[ fwmark MARK ]\n"
-		"\n"
-		"Where:	ADDR := { IP_ADDRESS }\n"
-		"	KEY  := { DOTTED_QUAD | NUMBER }\n"
-		"	MARK := { 0x0..0xffffffff }\n",
-		lu->id);
+	fprintf(f, "Usage: ip link { add | set | change | replace | del } NAME\n");
+	fprintf(f, "          type { vti } [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(f, "          [ [i|o]key KEY ]\n");
+	fprintf(f, "          [ dev PHYS_DEV ]\n");
+	fprintf(f, "\n");
+	fprintf(f, "Where: NAME := STRING\n");
+	fprintf(f, "       ADDR := { IP_ADDRESS }\n");
+	fprintf(f, "       KEY  := { DOTTED_QUAD | NUMBER }\n");
+}
+
+static void usage(void) __attribute__((noreturn));
+static void usage(void)
+{
+	print_usage(stderr);
+	exit(-1);
 }
 
 static int vti_parse_opt(struct link_util *lu, int argc, char **argv,
 			 struct nlmsghdr *n)
 {
-	struct ifinfomsg *ifi = NLMSG_DATA(n);
 	struct {
 		struct nlmsghdr n;
 		struct ifinfomsg i;
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_GETLINK,
-		.i.ifi_family = preferred_family,
-		.i.ifi_index = ifi->ifi_index,
-	};
-	struct nlmsghdr *answer;
+		char buf[1024];
+	} req;
+	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
 	struct rtattr *tb[IFLA_MAX + 1];
 	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
 	struct rtattr *vtiinfo[IFLA_VTI_MAX + 1];
-	__be32 ikey = 0;
-	__be32 okey = 0;
-	inet_prefix saddr, daddr;
-	unsigned int link = 0;
-	__u32 fwmark = 0;
+	unsigned ikey = 0;
+	unsigned okey = 0;
+	unsigned saddr = 0;
+	unsigned daddr = 0;
+	unsigned link = 0;
 	int len;
 
-	inet_prefix_reset(&saddr);
-	inet_prefix_reset(&daddr);
-
 	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
-		const struct rtattr *rta;
+		memset(&req, 0, sizeof(req));
 
-		if (rtnl_talk(&rth, &req.n, &answer) < 0) {
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETLINK;
+		req.i.ifi_family = preferred_family;
+		req.i.ifi_index = ifi->ifi_index;
+
+		if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) {
 get_failed:
 			fprintf(stderr,
 				"Failed to get existing tunnel info.\n");
 			return -1;
 		}
 
-		len = answer->nlmsg_len;
+		len = req.n.nlmsg_len;
 		len -= NLMSG_LENGTH(sizeof(*ifi));
 		if (len < 0)
 			goto get_failed;
 
-		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len);
+		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
 
 		if (!tb[IFLA_LINKINFO])
 			goto get_failed;
@@ -94,68 +96,100 @@
 		parse_rtattr_nested(vtiinfo, IFLA_VTI_MAX,
 				    linkinfo[IFLA_INFO_DATA]);
 
-		rta = vtiinfo[IFLA_VTI_LOCAL];
-		if (rta && get_addr_rta(&saddr, rta, AF_INET))
-			goto get_failed;
-
-		rta = vtiinfo[IFLA_VTI_REMOTE];
-		if (rta && get_addr_rta(&daddr, rta, AF_INET))
-			goto get_failed;
-
 		if (vtiinfo[IFLA_VTI_IKEY])
-			ikey = rta_getattr_u32(vtiinfo[IFLA_VTI_IKEY]);
+			ikey = *(__u32 *)RTA_DATA(vtiinfo[IFLA_VTI_IKEY]);
 
 		if (vtiinfo[IFLA_VTI_OKEY])
-			okey = rta_getattr_u32(vtiinfo[IFLA_VTI_OKEY]);
+			okey = *(__u32 *)RTA_DATA(vtiinfo[IFLA_VTI_OKEY]);
+
+		if (vtiinfo[IFLA_VTI_LOCAL])
+			saddr = *(__u32 *)RTA_DATA(vtiinfo[IFLA_VTI_LOCAL]);
+
+		if (vtiinfo[IFLA_VTI_REMOTE])
+			daddr = *(__u32 *)RTA_DATA(vtiinfo[IFLA_VTI_REMOTE]);
 
 		if (vtiinfo[IFLA_VTI_LINK])
-			link = rta_getattr_u8(vtiinfo[IFLA_VTI_LINK]);
-
-		if (vtiinfo[IFLA_VTI_FWMARK])
-			fwmark = rta_getattr_u32(vtiinfo[IFLA_VTI_FWMARK]);
-
-		free(answer);
+			link = *(__u8 *)RTA_DATA(vtiinfo[IFLA_VTI_LINK]);
 	}
 
 	while (argc > 0) {
 		if (!matches(*argv, "key")) {
+			unsigned uval;
+
 			NEXT_ARG();
-			ikey = okey = tnl_parse_key("key", *argv);
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0) < 0) {
+					fprintf(stderr,
+						"Invalid value for \"key\": \"%s\"; it should be an unsigned integer\n", *argv);
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+
+			ikey = okey = uval;
 		} else if (!matches(*argv, "ikey")) {
+			unsigned uval;
+
 			NEXT_ARG();
-			ikey = tnl_parse_key("ikey", *argv);
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0) < 0) {
+					fprintf(stderr, "invalid value for \"ikey\": \"%s\"; it should be an unsigned integer\n", *argv);
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+			ikey = uval;
 		} else if (!matches(*argv, "okey")) {
+			unsigned uval;
+
 			NEXT_ARG();
-			okey = tnl_parse_key("okey", *argv);
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0) < 0) {
+					fprintf(stderr, "invalid value for \"okey\": \"%s\"; it should be an unsigned integer\n", *argv);
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+			okey = uval;
 		} else if (!matches(*argv, "remote")) {
 			NEXT_ARG();
-			get_addr(&daddr, *argv, AF_INET);
+			if (!strcmp(*argv, "any")) {
+				fprintf(stderr, "invalid value for \"remote\": \"%s\"\n", *argv);
+				exit(-1);
+			} else {
+				daddr = get_addr32(*argv);
+			}
 		} else if (!matches(*argv, "local")) {
 			NEXT_ARG();
-			get_addr(&saddr, *argv, AF_INET);
+			if (!strcmp(*argv, "any")) {
+				fprintf(stderr, "invalid value for \"local\": \"%s\"\n", *argv);
+				exit(-1);
+			} else {
+				saddr = get_addr32(*argv);
+			}
 		} else if (!matches(*argv, "dev")) {
 			NEXT_ARG();
-			link = ll_name_to_index(*argv);
-			if (!link)
-				exit(nodev(*argv));
-		} else if (strcmp(*argv, "fwmark") == 0) {
-			NEXT_ARG();
-			if (get_u32(&fwmark, *argv, 0))
-				invarg("invalid fwmark\n", *argv);
-		} else {
-			vti_print_help(lu, argc, argv, stderr);
-			return -1;
-		}
+			link = if_nametoindex(*argv);
+			if (link == 0) {
+				fprintf(stderr, "Cannot find device \"%s\"\n",
+					*argv);
+				exit(-1);
+			}
+		} else
+			usage();
 		argc--; argv++;
 	}
 
 	addattr32(n, 1024, IFLA_VTI_IKEY, ikey);
 	addattr32(n, 1024, IFLA_VTI_OKEY, okey);
-	if (is_addrtype_inet_not_unspec(&saddr))
-		addattr_l(n, 1024, IFLA_VTI_LOCAL, saddr.data, saddr.bytelen);
-	if (is_addrtype_inet_not_unspec(&daddr))
-		addattr_l(n, 1024, IFLA_VTI_REMOTE, daddr.data, daddr.bytelen);
-	addattr32(n, 1024, IFLA_VTI_FWMARK, fwmark);
+	addattr_l(n, 1024, IFLA_VTI_LOCAL, &saddr, 4);
+	addattr_l(n, 1024, IFLA_VTI_REMOTE, &daddr, 4);
 	if (link)
 		addattr32(n, 1024, IFLA_VTI_LINK, link);
 
@@ -164,47 +198,57 @@
 
 static void vti_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
+	char s1[1024];
 	char s2[64];
+	const char *local = "any";
+	const char *remote = "any";
 
 	if (!tb)
 		return;
 
-	tnl_print_endpoint("remote", tb[IFLA_VTI_REMOTE], AF_INET);
-	tnl_print_endpoint("local", tb[IFLA_VTI_LOCAL], AF_INET);
+	if (tb[IFLA_VTI_REMOTE]) {
+		unsigned addr = *(__u32 *)RTA_DATA(tb[IFLA_VTI_REMOTE]);
 
-	if (tb[IFLA_VTI_LINK]) {
-		__u32 link = rta_getattr_u32(tb[IFLA_VTI_LINK]);
+		if (addr)
+			remote = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
+	}
 
-		if (link) {
-			print_string(PRINT_ANY, "link", "dev %s ",
-				     ll_index_to_name(link));
-		}
+	fprintf(f, "remote %s ", remote);
+
+	if (tb[IFLA_VTI_LOCAL]) {
+		unsigned addr = *(__u32 *)RTA_DATA(tb[IFLA_VTI_LOCAL]);
+
+		if (addr)
+			local = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
+	}
+
+	fprintf(f, "local %s ", local);
+
+	if (tb[IFLA_VTI_LINK] && *(__u32 *)RTA_DATA(tb[IFLA_VTI_LINK])) {
+		unsigned link = *(__u32 *)RTA_DATA(tb[IFLA_VTI_LINK]);
+		const char *n = if_indextoname(link, s2);
+
+		if (n)
+			fprintf(f, "dev %s ", n);
+		else
+			fprintf(f, "dev %u ", link);
 	}
 
 	if (tb[IFLA_VTI_IKEY]) {
-		struct rtattr *rta = tb[IFLA_VTI_IKEY];
-		__u32 key = rta_getattr_u32(rta);
-
-		if (key && inet_ntop(AF_INET, RTA_DATA(rta), s2, sizeof(s2)))
-			print_string(PRINT_ANY, "ikey", "ikey %s ", s2);
+		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_VTI_IKEY]), s2, sizeof(s2));
+		fprintf(f, "ikey %s ", s2);
 	}
 
 	if (tb[IFLA_VTI_OKEY]) {
-		struct rtattr *rta = tb[IFLA_VTI_OKEY];
-		__u32 key = rta_getattr_u32(rta);
-
-		if (key && inet_ntop(AF_INET, RTA_DATA(rta), s2, sizeof(s2)))
-			print_string(PRINT_ANY, "okey", "okey %s ", s2);
+		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_VTI_OKEY]), s2, sizeof(s2));
+		fprintf(f, "okey %s ", s2);
 	}
+}
 
-	if (tb[IFLA_VTI_FWMARK]) {
-		__u32 fwmark = rta_getattr_u32(tb[IFLA_VTI_FWMARK]);
-
-		if (fwmark) {
-			print_0xhex(PRINT_ANY,
-				    "fwmark", "fwmark %#llx ", fwmark);
-		}
-	}
+static void vti_print_help(struct link_util *lu, int argc, char **argv,
+	FILE *f)
+{
+	print_usage(f);
 }
 
 struct link_util vti_link_util = {
diff --git a/ip/link_vti6.c b/ip/link_vti6.c
index 0b080fa..c146f79 100644
--- a/ip/link_vti6.c
+++ b/ip/link_vti6.c
@@ -24,66 +24,62 @@
 #include "ip_common.h"
 #include "tunnel.h"
 
-static void vti6_print_help(struct link_util *lu, int argc, char **argv,
-			    FILE *f)
+
+static void usage(void) __attribute__((noreturn));
+static void usage(void)
 {
-	fprintf(f,
-		"Usage: ... %-4s	[ remote ADDR ]\n"
-		"		[ local ADDR ]\n"
-		"		[ [i|o]key KEY ]\n"
-		"		[ dev PHYS_DEV ]\n"
-		"		[ fwmark MARK ]\n"
-		"\n"
-		"Where:	ADDR := { IPV6_ADDRESS }\n"
-		"	KEY  := { DOTTED_QUAD | NUMBER }\n"
-		"	MARK := { 0x0..0xffffffff }\n",
-		lu->id);
+	fprintf(stderr, "Usage: ip link { add | set | change | replace | del } NAME\n");
+	fprintf(stderr, "          type { vti6 } [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(stderr, "          [ [i|o]key KEY ]\n");
+	fprintf(stderr, "          [ dev PHYS_DEV ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME := STRING\n");
+	fprintf(stderr, "       ADDR := { IPV6_ADDRESS }\n");
+	fprintf(stderr, "       KEY  := { DOTTED_QUAD | NUMBER }\n");
+	exit(-1);
 }
 
 static int vti6_parse_opt(struct link_util *lu, int argc, char **argv,
 			  struct nlmsghdr *n)
 {
-	struct ifinfomsg *ifi = NLMSG_DATA(n);
 	struct {
 		struct nlmsghdr n;
 		struct ifinfomsg i;
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = RTM_GETLINK,
-		.i.ifi_family = preferred_family,
-		.i.ifi_index = ifi->ifi_index,
-	};
-	struct nlmsghdr *answer;
+		char buf[1024];
+	} req;
+	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
 	struct rtattr *tb[IFLA_MAX + 1];
 	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
 	struct rtattr *vtiinfo[IFLA_VTI_MAX + 1];
-	__be32 ikey = 0;
-	__be32 okey = 0;
-	inet_prefix saddr, daddr;
-	unsigned int link = 0;
-	__u32 fwmark = 0;
+	struct in6_addr saddr;
+	struct in6_addr daddr;
+	unsigned ikey = 0;
+	unsigned okey = 0;
+	unsigned link = 0;
 	int len;
 
-	inet_prefix_reset(&saddr);
-	inet_prefix_reset(&daddr);
-
 	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
-		const struct rtattr *rta;
+		memset(&req, 0, sizeof(req));
 
-		if (rtnl_talk(&rth, &req.n, &answer) < 0) {
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETLINK;
+		req.i.ifi_family = preferred_family;
+		req.i.ifi_index = ifi->ifi_index;
+
+		if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) {
 get_failed:
 			fprintf(stderr,
 				"Failed to get existing tunnel info.\n");
 			return -1;
 		}
 
-		len = answer->nlmsg_len;
+		len = req.n.nlmsg_len;
 		len -= NLMSG_LENGTH(sizeof(*ifi));
 		if (len < 0)
 			goto get_failed;
 
-		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len);
+		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
 
 		if (!tb[IFLA_LINKINFO])
 			goto get_failed;
@@ -96,68 +92,101 @@
 		parse_rtattr_nested(vtiinfo, IFLA_VTI_MAX,
 				    linkinfo[IFLA_INFO_DATA]);
 
-		rta = vtiinfo[IFLA_VTI_LOCAL];
-		if (rta && get_addr_rta(&saddr, rta, AF_INET6))
-			goto get_failed;
-
-		rta = vtiinfo[IFLA_VTI_REMOTE];
-		if (rta && get_addr_rta(&daddr, rta, AF_INET6))
-			goto get_failed;
-
 		if (vtiinfo[IFLA_VTI_IKEY])
 			ikey = rta_getattr_u32(vtiinfo[IFLA_VTI_IKEY]);
 
 		if (vtiinfo[IFLA_VTI_OKEY])
 			okey = rta_getattr_u32(vtiinfo[IFLA_VTI_OKEY]);
 
+		if (vtiinfo[IFLA_VTI_LOCAL])
+			memcpy(&saddr, RTA_DATA(vtiinfo[IFLA_VTI_LOCAL]), sizeof(saddr));
+
+		if (vtiinfo[IFLA_VTI_REMOTE])
+			memcpy(&daddr, RTA_DATA(vtiinfo[IFLA_VTI_REMOTE]), sizeof(daddr));
+
 		if (vtiinfo[IFLA_VTI_LINK])
 			link = rta_getattr_u8(vtiinfo[IFLA_VTI_LINK]);
-
-		if (vtiinfo[IFLA_VTI_FWMARK])
-			fwmark = rta_getattr_u32(vtiinfo[IFLA_VTI_FWMARK]);
-
-		free(answer);
 	}
 
 	while (argc > 0) {
 		if (!matches(*argv, "key")) {
+			unsigned uval;
+
 			NEXT_ARG();
-			ikey = okey = tnl_parse_key("key", *argv);
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0) < 0) {
+					fprintf(stderr,
+						"Invalid value for \"key\": \"%s\"; it should be an unsigned integer\n", *argv);
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+
+			ikey = okey = uval;
 		} else if (!matches(*argv, "ikey")) {
+			unsigned uval;
+
 			NEXT_ARG();
-			ikey = tnl_parse_key("ikey", *argv);
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0) < 0) {
+					fprintf(stderr, "invalid value for \"ikey\": \"%s\"; it should be an unsigned integer\n", *argv);
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+			ikey = uval;
 		} else if (!matches(*argv, "okey")) {
+			unsigned uval;
+
 			NEXT_ARG();
-			okey = tnl_parse_key("okey", *argv);
+			if (strchr(*argv, '.'))
+				uval = get_addr32(*argv);
+			else {
+				if (get_unsigned(&uval, *argv, 0) < 0) {
+					fprintf(stderr, "invalid value for \"okey\": \"%s\"; it should be an unsigned integer\n", *argv);
+					exit(-1);
+				}
+				uval = htonl(uval);
+			}
+			okey = uval;
 		} else if (!matches(*argv, "remote")) {
 			NEXT_ARG();
-			get_addr(&daddr, *argv, AF_INET6);
+			if (!strcmp(*argv, "any")) {
+				fprintf(stderr, "invalid value for \"remote\": \"%s\"\n", *argv);
+				exit(-1);
+			} else {
+				inet_prefix addr;
+				get_prefix(&addr, *argv, AF_INET6);
+				memcpy(&daddr, addr.data, addr.bytelen);
+			}
 		} else if (!matches(*argv, "local")) {
 			NEXT_ARG();
-			get_addr(&saddr, *argv, AF_INET6);
+			if (!strcmp(*argv, "any")) {
+				fprintf(stderr, "invalid value for \"local\": \"%s\"\n", *argv);
+				exit(-1);
+			} else {
+				inet_prefix addr;
+				get_prefix(&addr, *argv, AF_INET6);
+				memcpy(&saddr, addr.data, addr.bytelen);
+			}
 		} else if (!matches(*argv, "dev")) {
 			NEXT_ARG();
-			link = ll_name_to_index(*argv);
-			if (!link)
-				exit(nodev(*argv));
-		} else if (strcmp(*argv, "fwmark") == 0) {
-			NEXT_ARG();
-			if (get_u32(&fwmark, *argv, 0))
-				invarg("invalid fwmark\n", *argv);
-		} else {
-			vti6_print_help(lu, argc, argv, stderr);
-			return -1;
-		}
+			link = if_nametoindex(*argv);
+			if (link == 0)
+				exit(-1);
+		} else
+			usage();
 		argc--; argv++;
 	}
 
 	addattr32(n, 1024, IFLA_VTI_IKEY, ikey);
 	addattr32(n, 1024, IFLA_VTI_OKEY, okey);
-	if (is_addrtype_inet_not_unspec(&saddr))
-		addattr_l(n, 1024, IFLA_VTI_LOCAL, saddr.data, saddr.bytelen);
-	if (is_addrtype_inet_not_unspec(&daddr))
-		addattr_l(n, 1024, IFLA_VTI_REMOTE, daddr.data, daddr.bytelen);
-	addattr32(n, 1024, IFLA_VTI_FWMARK, fwmark);
+	addattr_l(n, 1024, IFLA_VTI_LOCAL, &saddr, sizeof(saddr));
+	addattr_l(n, 1024, IFLA_VTI_REMOTE, &daddr, sizeof(daddr));
 	if (link)
 		addattr32(n, 1024, IFLA_VTI_LINK, link);
 
@@ -166,46 +195,50 @@
 
 static void vti6_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
+	char s1[1024];
 	char s2[64];
+	const char *local = "any";
+	const char *remote = "any";
+	struct in6_addr saddr;
+	struct in6_addr daddr;
 
 	if (!tb)
 		return;
 
-	tnl_print_endpoint("remote", tb[IFLA_VTI_REMOTE], AF_INET6);
-	tnl_print_endpoint("local", tb[IFLA_VTI_LOCAL], AF_INET6);
+	if (tb[IFLA_VTI_REMOTE]) {
+		memcpy(&daddr, RTA_DATA(tb[IFLA_VTI_REMOTE]), sizeof(daddr));
 
-	if (tb[IFLA_VTI_LINK]) {
-		__u32 link = rta_getattr_u32(tb[IFLA_VTI_LINK]);
+		remote = format_host(AF_INET6, 16, &daddr, s1, sizeof(s1));
+	}
 
-		if (link) {
-			print_string(PRINT_ANY, "link", "dev %s ",
-				     ll_index_to_name(link));
-		}
+	fprintf(f, "remote %s ", remote);
+
+	if (tb[IFLA_VTI_LOCAL]) {
+		memcpy(&saddr, RTA_DATA(tb[IFLA_VTI_LOCAL]), sizeof(saddr));
+
+		local = format_host(AF_INET6, 16, &saddr, s1, sizeof(s1));
+	}
+
+	fprintf(f, "local %s ", local);
+
+	if (tb[IFLA_VTI_LINK] && *(__u32 *)RTA_DATA(tb[IFLA_VTI_LINK])) {
+		unsigned link = *(__u32 *)RTA_DATA(tb[IFLA_VTI_LINK]);
+		const char *n = if_indextoname(link, s2);
+
+		if (n)
+			fprintf(f, "dev %s ", n);
+		else
+			fprintf(f, "dev %u ", link);
 	}
 
 	if (tb[IFLA_VTI_IKEY]) {
-		struct rtattr *rta = tb[IFLA_VTI_IKEY];
-		__u32 key = rta_getattr_u32(rta);
-
-		if (key && inet_ntop(AF_INET, RTA_DATA(rta), s2, sizeof(s2)))
-			print_string(PRINT_ANY, "ikey", "ikey %s ", s2);
+		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_VTI_IKEY]), s2, sizeof(s2));
+		fprintf(f, "ikey %s ", s2);
 	}
 
 	if (tb[IFLA_VTI_OKEY]) {
-		struct rtattr *rta = tb[IFLA_VTI_OKEY];
-		__u32 key = rta_getattr_u32(rta);
-
-		if (key && inet_ntop(AF_INET, RTA_DATA(rta), s2, sizeof(s2)))
-			print_string(PRINT_ANY, "okey", "okey %s ", s2);
-	}
-
-	if (tb[IFLA_VTI_FWMARK]) {
-		__u32 fwmark = rta_getattr_u32(tb[IFLA_VTI_FWMARK]);
-
-		if (fwmark) {
-			print_0xhex(PRINT_ANY,
-				    "fwmark", "fwmark %#llx ", fwmark);
-		}
+		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_VTI_OKEY]), s2, sizeof(s2));
+		fprintf(f, "okey %s ", s2);
 	}
 }
 
@@ -214,5 +247,4 @@
 	.maxattr = IFLA_VTI_MAX,
 	.parse_opt = vti6_parse_opt,
 	.print_opt = vti6_print_opt,
-	.print_help = vti6_print_help,
 };
diff --git a/ip/link_xfrm.c b/ip/link_xfrm.c
deleted file mode 100644
index a28f308..0000000
--- a/ip/link_xfrm.c
+++ /dev/null
@@ -1,76 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * link_xfrm.c	Virtual XFRM Interface driver module
- *
- * Authors:	Matt Ellison <matt@arroyo.io>
- */
-
-#include <string.h>
-#include <linux/if_link.h>
-
-#include "rt_names.h"
-#include "utils.h"
-#include "ip_common.h"
-#include "tunnel.h"
-
-static void xfrm_print_help(struct link_util *lu, int argc, char **argv,
-			    FILE *f)
-{
-	fprintf(f,
-		"Usage: ... %-4s dev [ PHYS_DEV ] [ if_id IF-ID ]\n"
-		"\n"
-		"Where: IF-ID := { 0x0..0xffffffff }\n",
-		lu->id);
-}
-
-static int xfrm_parse_opt(struct link_util *lu, int argc, char **argv,
-			  struct nlmsghdr *n)
-{
-	unsigned int link = 0;
-	__u32 if_id = 0;
-
-	while (argc > 0) {
-		if (!matches(*argv, "dev")) {
-			NEXT_ARG();
-			link = ll_name_to_index(*argv);
-			if (!link)
-				exit(nodev(*argv));
-		} else if (!matches(*argv, "if_id")) {
-			NEXT_ARG();
-			if (!get_u32(&if_id, *argv, 0))
-				addattr32(n, 1024, IFLA_XFRM_IF_ID, if_id);
-		} else {
-			xfrm_print_help(lu, argc, argv, stderr);
-			return -1;
-		}
-		argc--; argv++;
-	}
-
-	if (link)
-		addattr32(n, 1024, IFLA_XFRM_LINK, link);
-
-	return 0;
-}
-
-static void xfrm_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
-{
-
-	if (!tb)
-		return;
-
-	if (tb[IFLA_XFRM_IF_ID]) {
-		__u32 id = rta_getattr_u32(tb[IFLA_XFRM_IF_ID]);
-
-		print_0xhex(PRINT_ANY, "if_id", "if_id %#llx ", id);
-
-	}
-
-}
-
-struct link_util xfrm_link_util = {
-	.id = "xfrm",
-	.maxattr = IFLA_XFRM_MAX,
-	.parse_opt = xfrm_parse_opt,
-	.print_opt = xfrm_print_opt,
-	.print_help = xfrm_print_help,
-};
diff --git a/ip/routef b/ip/routef
index c251e7b..d266e2d 100644
--- a/ip/routef
+++ b/ip/routef
@@ -1,5 +1,4 @@
 #! /bin/sh
-# SPDX-License-Identifier: GPL-2.0
 
 if [ -z "$*" ] ; then
 	exec ip -4 ro flush  scope global  type unicast
diff --git a/ip/routel b/ip/routel
index 7056886..8d1d352 100644
--- a/ip/routel
+++ b/ip/routel
@@ -1,5 +1,5 @@
 #!/bin/sh
-# SPDX-License-Identifier: GPL-2.0
+#$Id$
 
 #
 # Script created by: Stephen R. van den Berg <srb@cuci.nl>, 1999/04/18
@@ -32,22 +32,10 @@
     esac
     while test $# != 0
     do
-       case "$1" in
-          proto|via|dev|scope|src|table)
-             key=$1
-             val=$2
-             eval "$key='$val'"
-             shift 2
-             ;;
-          dead|onlink|pervasive|offload|notify|linkdown|unresolved)
-             shift
-             ;;
-          *)
-             # avoid infinite loop on unknown keyword without value at line end
-             shift
-             shift
-             ;;
-       esac
+       key=$1
+       val=$2
+       eval "$key=$val"
+       shift 2
     done
     echo "$network	$via	$src	$proto	$scope	$dev	$table"
  done | awk -F '	' '
diff --git a/ip/rtm_map.c b/ip/rtm_map.c
index 8d8eafe..1d7d2c7 100644
--- a/ip/rtm_map.c
+++ b/ip/rtm_map.c
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -23,12 +24,6 @@
 
 char *rtnl_rtntype_n2a(int id, char *buf, int len)
 {
-
-	if (numeric) {
-		snprintf(buf, len, "%d", id);
-		return buf;
-	}
-
 	switch (id) {
 	case RTN_UNSPEC:
 		return "none";
diff --git a/ip/rtmon.c b/ip/rtmon.c
index bccdded..42b24fb 100644
--- a/ip/rtmon.c
+++ b/ip/rtmon.c
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <sys/time.h>
@@ -24,12 +25,13 @@
 #include "utils.h"
 #include "libnetlink.h"
 
+int resolve_hosts = 0;
 static int init_phase = 1;
 
 static void write_stamp(FILE *fp)
 {
 	char buf[128];
-	struct nlmsghdr *n1 = (void *)buf;
+	struct nlmsghdr *n1 = (void*)buf;
 	struct timeval tv;
 
 	n1->nlmsg_type = NLMSG_TSTAMP;
@@ -38,35 +40,32 @@
 	n1->nlmsg_pid = 0;
 	n1->nlmsg_len = NLMSG_LENGTH(4*2);
 	gettimeofday(&tv, NULL);
-	((__u32 *)NLMSG_DATA(n1))[0] = tv.tv_sec;
-	((__u32 *)NLMSG_DATA(n1))[1] = tv.tv_usec;
-	fwrite((void *)n1, 1, NLMSG_ALIGN(n1->nlmsg_len), fp);
+	((__u32*)NLMSG_DATA(n1))[0] = tv.tv_sec;
+	((__u32*)NLMSG_DATA(n1))[1] = tv.tv_usec;
+	fwrite((void*)n1, 1, NLMSG_ALIGN(n1->nlmsg_len), fp);
 }
 
-static int dump_msg(struct rtnl_ctrl_data *ctrl,
+static int dump_msg(const struct sockaddr_nl *who, struct rtnl_ctrl_data *ctrl,
 		    struct nlmsghdr *n, void *arg)
 {
-	FILE *fp = (FILE *)arg;
-
+	FILE *fp = (FILE*)arg;
 	if (!init_phase)
 		write_stamp(fp);
-	fwrite((void *)n, 1, NLMSG_ALIGN(n->nlmsg_len), fp);
+	fwrite((void*)n, 1, NLMSG_ALIGN(n->nlmsg_len), fp);
 	fflush(fp);
 	return 0;
 }
 
-static int dump_msg2(struct nlmsghdr *n, void *arg)
+static int dump_msg2(const struct sockaddr_nl *who,
+		     struct nlmsghdr *n, void *arg)
 {
-	return dump_msg(NULL, n, arg);
+	return dump_msg(who, NULL, n, arg);
 }
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: rtmon [ OPTIONS ] file FILE [ all | LISTofOBJECTS ]\n"
-		"OPTIONS := { -f[amily] { inet | inet6 | link | help } |\n"
-		"	     -4 | -6 | -0 | -V[ersion] }\n"
-		"LISTofOBJECTS := [ link ] [ address ] [ route ]\n");
+	fprintf(stderr, "Usage: rtmon file FILE [ all | LISTofOBJECTS]\n");
+	fprintf(stderr, "LISTofOBJECTS := [ link ] [ address ] [ route ]\n");
 	exit(-1);
 }
 
@@ -76,7 +75,7 @@
 	FILE *fp;
 	struct rtnl_handle rth;
 	int family = AF_UNSPEC;
-	unsigned int groups = ~0U;
+	unsigned groups = ~0U;
 	int llink = 0;
 	int laddr = 0;
 	int lroute = 0;
@@ -116,13 +115,13 @@
 				usage();
 			file = argv[1];
 		} else if (matches(argv[1], "link") == 0) {
-			llink = 1;
+			llink=1;
 			groups = 0;
 		} else if (matches(argv[1], "address") == 0) {
-			laddr = 1;
+			laddr=1;
 			groups = 0;
 		} else if (matches(argv[1], "route") == 0) {
-			lroute = 1;
+			lroute=1;
 			groups = 0;
 		} else if (strcmp(argv[1], "all") == 0) {
 			groups = ~0U;
@@ -163,7 +162,7 @@
 	if (rtnl_open(&rth, groups) < 0)
 		exit(1);
 
-	if (rtnl_linkdump_req(&rth, AF_UNSPEC) < 0) {
+	if (rtnl_wilddump_request(&rth, AF_UNSPEC, RTM_GETLINK) < 0) {
 		perror("Cannot send dump request");
 		exit(1);
 	}
@@ -177,7 +176,7 @@
 
 	init_phase = 0;
 
-	if (rtnl_listen(&rth, dump_msg, (void *)fp) < 0)
+	if (rtnl_listen(&rth, dump_msg, (void*)fp) < 0)
 		exit(2);
 
 	exit(0);
diff --git a/ip/rtpr b/ip/rtpr
index 7e48674..c3629fd 100644
--- a/ip/rtpr
+++ b/ip/rtpr
@@ -1,5 +1,4 @@
-#! /bin/sh
-# SPDX-License-Identifier: GPL-2.0
+#! /bin/bash
 
 exec tr "[\\\\]" "[
 ]"
diff --git a/ip/static-syms.c b/ip/static-syms.c
index 47c4092..0bc8074 100644
--- a/ip/static-syms.c
+++ b/ip/static-syms.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * This file creates a dummy version of dynamic loading
  * for environments where dynamic linking
diff --git a/ip/tcp_metrics.c b/ip/tcp_metrics.c
index acbd745..57b605f 100644
--- a/ip/tcp_metrics.c
+++ b/ip/tcp_metrics.c
@@ -28,18 +28,16 @@
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage:	ip tcp_metrics/tcpmetrics { COMMAND | help }\n"
-		"	ip tcp_metrics { show | flush } SELECTOR\n"
-		"	ip tcp_metrics delete [ address ] ADDRESS\n"
-		"SELECTOR := [ [ address ] PREFIX ]\n");
+	fprintf(stderr, "Usage: ip tcp_metrics/tcpmetrics { COMMAND | help }\n");
+	fprintf(stderr, "       ip tcp_metrics { show | flush } SELECTOR\n");
+	fprintf(stderr, "       ip tcp_metrics delete [ address ] ADDRESS\n");
+	fprintf(stderr, "SELECTOR := [ [ address ] PREFIX ]\n");
 	exit(-1);
 }
 
 /* netlink socket */
 static struct rtnl_handle grth = { .fd = -1 };
 static int genl_family = -1;
-static const double usec_per_sec = 1000000.;
 
 #define TCPM_REQUEST(_req, _bufsiz, _cmd, _flags) \
 	GENL_REQUEST(_req, _bufsiz, genl_family, 0, \
@@ -49,8 +47,8 @@
 #define CMD_DEL		0x0002	/* delete, remove		*/
 #define CMD_FLUSH	0x0004	/* flush			*/
 
-static const struct {
-	const char *name;
+static struct {
+	char	*name;
 	int	code;
 } cmds[] = {
 	{	"list",		CMD_LIST	},
@@ -61,7 +59,7 @@
 	{	"flush",	CMD_FLUSH	},
 };
 
-static const char *metric_name[TCP_METRIC_MAX + 1] = {
+static char *metric_name[TCP_METRIC_MAX + 1] = {
 	[TCP_METRIC_RTT]		= "rtt",
 	[TCP_METRIC_RTTVAR]		= "rttvar",
 	[TCP_METRIC_SSTHRESH]		= "ssthresh",
@@ -69,7 +67,8 @@
 	[TCP_METRIC_REORDERING]		= "reordering",
 };
 
-static struct {
+static struct
+{
 	int flushed;
 	char *flushb;
 	int flushp;
@@ -89,83 +88,16 @@
 	return 0;
 }
 
-static void print_tcp_metrics(struct rtattr *a)
-{
-	struct rtattr *m[TCP_METRIC_MAX + 1 + 1];
-	unsigned long rtt = 0, rttvar = 0;
-	int i;
-
-	parse_rtattr_nested(m, TCP_METRIC_MAX + 1, a);
-
-	for (i = 0; i < TCP_METRIC_MAX + 1; i++) {
-		const char *name;
-		__u32 val;
-		SPRINT_BUF(b1);
-
-		a = m[i + 1];
-		if (!a)
-			continue;
-
-		val = rta_getattr_u32(a);
-
-		switch (i) {
-		case TCP_METRIC_RTT:
-			if (!rtt)
-				rtt = (val * 1000UL) >> 3;
-			continue;
-		case TCP_METRIC_RTTVAR:
-			if (!rttvar)
-				rttvar = (val * 1000UL) >> 2;
-			continue;
-		case TCP_METRIC_RTT_US:
-			rtt = val >> 3;
-			continue;
-
-		case TCP_METRIC_RTTVAR_US:
-			rttvar = val >> 2;
-			continue;
-
-		case TCP_METRIC_SSTHRESH:
-		case TCP_METRIC_CWND:
-		case TCP_METRIC_REORDERING:
-			name = metric_name[i];
-			break;
-
-		default:
-			snprintf(b1, sizeof(b1),
-				 " metric_%d ", i);
-			name = b1;
-		}
-
-
-		print_uint(PRINT_JSON, name, NULL, val);
-		print_string(PRINT_FP, NULL, " %s ", name);
-		print_uint(PRINT_FP, NULL, "%u", val);
-	}
-
-	if (rtt) {
-		print_float(PRINT_JSON, "rtt", NULL,
-			    (double)rtt / usec_per_sec);
-		print_u64(PRINT_FP, NULL,
-			   " rtt %luus", rtt);
-	}
-	if (rttvar) {
-		print_float(PRINT_JSON, "rttvar", NULL,
-			    (double) rttvar / usec_per_sec);
-		print_u64(PRINT_FP, NULL,
-			   " rttvar %luus", rttvar);
-	}
-}
-
-static int process_msg(struct nlmsghdr *n, void *arg)
+static int process_msg(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		       void *arg)
 {
 	FILE *fp = (FILE *) arg;
 	struct genlmsghdr *ghdr;
 	struct rtattr *attrs[TCP_METRICS_ATTR_MAX + 1], *a;
-	const char *h;
 	int len = n->nlmsg_len;
+	char abuf[256];
 	inet_prefix daddr, saddr;
-	int atype, stype;
+	int family, i, atype, stype, dlen = 0, slen = 0;
 
 	if (n->nlmsg_type != genl_family)
 		return -1;
@@ -181,65 +113,67 @@
 	parse_rtattr(attrs, TCP_METRICS_ATTR_MAX, (void *) ghdr + GENL_HDRLEN,
 		     len);
 
-	if (attrs[TCP_METRICS_ATTR_ADDR_IPV4]) {
+	a = attrs[TCP_METRICS_ATTR_ADDR_IPV4];
+	if (a) {
 		if (f.daddr.family && f.daddr.family != AF_INET)
 			return 0;
-		a = attrs[TCP_METRICS_ATTR_ADDR_IPV4];
-		daddr.family = AF_INET;
+		memcpy(&daddr.data, RTA_DATA(a), 4);
+		daddr.bytelen = 4;
+		family = AF_INET;
 		atype = TCP_METRICS_ATTR_ADDR_IPV4;
-	} else if (attrs[TCP_METRICS_ATTR_ADDR_IPV6]) {
-		if (f.daddr.family && f.daddr.family != AF_INET6)
-			return 0;
-		a = attrs[TCP_METRICS_ATTR_ADDR_IPV6];
-		daddr.family = AF_INET6;
-		atype = TCP_METRICS_ATTR_ADDR_IPV6;
+		dlen = RTA_PAYLOAD(a);
 	} else {
-		return 0;
+		a = attrs[TCP_METRICS_ATTR_ADDR_IPV6];
+		if (a) {
+			if (f.daddr.family && f.daddr.family != AF_INET6)
+				return 0;
+			memcpy(&daddr.data, RTA_DATA(a), 16);
+			daddr.bytelen = 16;
+			family = AF_INET6;
+			atype = TCP_METRICS_ATTR_ADDR_IPV6;
+			dlen = RTA_PAYLOAD(a);
+		} else
+			return 0;
 	}
 
-	if (get_addr_rta(&daddr, a, daddr.family))
-		return 0;
+	a = attrs[TCP_METRICS_ATTR_SADDR_IPV4];
+	if (a) {
+		if (f.saddr.family && f.saddr.family != AF_INET)
+			return 0;
+		memcpy(&saddr.data, RTA_DATA(a), 4);
+		saddr.bytelen = 4;
+		stype = TCP_METRICS_ATTR_SADDR_IPV4;
+		slen = RTA_PAYLOAD(a);
+	} else {
+		a = attrs[TCP_METRICS_ATTR_SADDR_IPV6];
+		if (a) {
+			if (f.saddr.family && f.saddr.family != AF_INET6)
+				return 0;
+			memcpy(&saddr.data, RTA_DATA(a), 16);
+			saddr.bytelen = 16;
+			stype = TCP_METRICS_ATTR_SADDR_IPV6;
+			slen = RTA_PAYLOAD(a);
+		}
+	}
 
 	if (f.daddr.family && f.daddr.bitlen >= 0 &&
 	    inet_addr_match(&daddr, &f.daddr, f.daddr.bitlen))
+	       return 0;
+	/* Only check for the source-address if the kernel supports it,
+	 * meaning slen != 0.
+	 */
+	if (slen && f.saddr.family && f.saddr.bitlen >= 0 &&
+	    inet_addr_match(&saddr, &f.saddr, f.saddr.bitlen))
 		return 0;
 
-	if (attrs[TCP_METRICS_ATTR_SADDR_IPV4]) {
-		if (f.saddr.family && f.saddr.family != AF_INET)
-			return 0;
-		a = attrs[TCP_METRICS_ATTR_SADDR_IPV4];
-		saddr.family = AF_INET;
-		stype = TCP_METRICS_ATTR_SADDR_IPV4;
-	} else if (attrs[TCP_METRICS_ATTR_SADDR_IPV6]) {
-		if (f.saddr.family && f.saddr.family != AF_INET6)
-			return 0;
-		a = attrs[TCP_METRICS_ATTR_SADDR_IPV6];
-		saddr.family = AF_INET6;
-		stype = TCP_METRICS_ATTR_SADDR_IPV6;
-	} else {
-		saddr.family = AF_UNSPEC;
-		stype = 0;
-	}
-
-	/* Only get/check for the source-address if the kernel supports it. */
-	if (saddr.family) {
-		if (get_addr_rta(&saddr, a, saddr.family))
-			return 0;
-
-		if (f.saddr.family && f.saddr.bitlen >= 0 &&
-		    inet_addr_match(&saddr, &f.saddr, f.saddr.bitlen))
-			return 0;
-	}
-
 	if (f.flushb) {
 		struct nlmsghdr *fn;
-
 		TCPM_REQUEST(req2, 128, TCP_METRICS_CMD_DEL, NLM_F_REQUEST);
 
-		addattr_l(&req2.n, sizeof(req2), atype, daddr.data,
+		addattr_l(&req2.n, sizeof(req2), atype, &daddr.data,
 			  daddr.bytelen);
-		if (saddr.family)
-			addattr_l(&req2.n, sizeof(req2), stype, saddr.data,
+		if (slen)
+			addattr_l(&req2.n, sizeof(req2), stype, &saddr.data,
 				  saddr.bytelen);
 
 		if (NLMSG_ALIGN(f.flushp) + req2.n.nlmsg_len > f.flushe) {
@@ -255,60 +189,96 @@
 			return 0;
 	}
 
-	open_json_object(NULL);
 	if (f.cmd & (CMD_DEL | CMD_FLUSH))
-		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
+		fprintf(fp, "Deleted ");
 
-	h = format_host(daddr.family, daddr.bytelen, daddr.data);
-	print_color_string(PRINT_ANY,
-			   ifa_family_color(daddr.family),
-			   "dst", "%s", h);
+	fprintf(fp, "%s",
+		format_host(family, dlen, &daddr.data, abuf, sizeof(abuf)));
 
 	a = attrs[TCP_METRICS_ATTR_AGE];
 	if (a) {
-		__u64 val = rta_getattr_u64(a);
-		double age = val / 1000.;
+		unsigned long long val = rta_getattr_u64(a);
 
-		print_float(PRINT_ANY, "age",
-			     " age %.03fsec", age);
+		fprintf(fp, " age %llu.%03llusec",
+			val / 1000, val % 1000);
 	}
 
 	a = attrs[TCP_METRICS_ATTR_TW_TS_STAMP];
 	if (a) {
 		__s32 val = (__s32) rta_getattr_u32(a);
 		__u32 tsval;
-		char tw_ts[64];
 
 		a = attrs[TCP_METRICS_ATTR_TW_TSVAL];
 		tsval = a ? rta_getattr_u32(a) : 0;
-		snprintf(tw_ts, sizeof(tw_ts),
-			 "%u/%d", tsval, val);
-		print_string(PRINT_ANY, "tw_ts_stamp",
-		     " tw_ts %s ago", tw_ts);
+		fprintf(fp, " tw_ts %u/%dsec ago", tsval, val);
 	}
 
-	if (attrs[TCP_METRICS_ATTR_VALS])
-		print_tcp_metrics(attrs[TCP_METRICS_ATTR_VALS]);
+	a = attrs[TCP_METRICS_ATTR_VALS];
+	if (a) {
+		struct rtattr *m[TCP_METRIC_MAX + 1 + 1];
+		unsigned long rtt = 0, rttvar = 0;
+
+		parse_rtattr_nested(m, TCP_METRIC_MAX + 1, a);
+
+		for (i = 0; i < TCP_METRIC_MAX + 1; i++) {
+			unsigned long val;
+
+			a = m[i + 1];
+			if (!a)
+				continue;
+			if (i != TCP_METRIC_RTT &&
+			    i != TCP_METRIC_RTT_US &&
+			    i != TCP_METRIC_RTTVAR &&
+			    i != TCP_METRIC_RTTVAR_US) {
+				if (metric_name[i])
+					fprintf(fp, " %s ", metric_name[i]);
+				else
+					fprintf(fp, " metric_%d ", i);
+			}
+			val = rta_getattr_u32(a);
+			switch (i) {
+			case TCP_METRIC_RTT:
+				if (!rtt)
+					rtt = (val * 1000UL) >> 3;
+				break;
+			case TCP_METRIC_RTTVAR:
+				if (!rttvar)
+					rttvar = (val * 1000UL) >> 2;
+				break;
+			case TCP_METRIC_RTT_US:
+				rtt = val >> 3;
+				break;
+			case TCP_METRIC_RTTVAR_US:
+				rttvar = val >> 2;
+				break;
+			case TCP_METRIC_SSTHRESH:
+			case TCP_METRIC_CWND:
+			case TCP_METRIC_REORDERING:
+			default:
+				fprintf(fp, "%lu", val);
+				break;
+			}
+		}
+		if (rtt)
+			fprintf(fp, " rtt %luus", rtt);
+		if (rttvar)
+			fprintf(fp, " rttvar %luus", rttvar);
+	}
 
 	a = attrs[TCP_METRICS_ATTR_FOPEN_MSS];
-	if (a) {
-		print_uint(PRINT_ANY, "fopen_miss", " fo_mss %u",
-			   rta_getattr_u16(a));
-	}
+	if (a)
+		fprintf(fp, " fo_mss %u", rta_getattr_u16(a));
 
 	a = attrs[TCP_METRICS_ATTR_FOPEN_SYN_DROPS];
 	if (a) {
 		__u16 syn_loss = rta_getattr_u16(a);
-		double ts;
+		unsigned long long ts;
 
 		a = attrs[TCP_METRICS_ATTR_FOPEN_SYN_DROP_TS];
 		ts = a ? rta_getattr_u64(a) : 0;
 
-		print_uint(PRINT_ANY, "fopen_syn_drops",
-			   " fo_syn_drops %u", syn_loss);
-		print_float(PRINT_ANY, "fopen_syn_drop_ts",
-			     "/%.03fusec ago",
-			     ts / 1000000.);
+		fprintf(fp, " fo_syn_drops %u/%llu.%03llusec ago",
+			syn_loss, ts / 1000, ts % 1000);
 	}
 
 	a = attrs[TCP_METRICS_ATTR_FOPEN_COOKIE];
@@ -322,21 +292,17 @@
 		cookie[0] = 0;
 		for (i = 0; i < max; i++)
 			sprintf(cookie + i + i, "%02x", ptr[i]);
-
-		print_string(PRINT_ANY, "fo_cookie",
-			     " fo_cookie %s", cookie);
+		fprintf(fp, " fo_cookie %s", cookie);
 	}
 
-	if (saddr.family) {
-		const char *src;
-
-		src = format_host(saddr.family, saddr.bytelen, saddr.data);
-		print_string(PRINT_ANY, "source",
-			     " source %s", src);
+	if (slen) {
+		fprintf(fp, " source %s",
+			format_host(family, slen, &saddr.data, abuf,
+				    sizeof(abuf)));
 	}
 
-	print_string(PRINT_FP, NULL, "\n", "");
-	close_json_object();
+	fprintf(fp, "\n");
+
 	fflush(fp);
 	return 0;
 }
@@ -344,7 +310,6 @@
 static int tcpm_do_cmd(int cmd, int argc, char **argv)
 {
 	TCPM_REQUEST(req, 1024, TCP_METRICS_CMD_GET, NLM_F_REQUEST);
-	struct nlmsghdr *answer;
 	int atype = -1, stype = -1;
 	int ack;
 
@@ -368,7 +333,6 @@
 		if (strcmp(*argv, "src") == 0 ||
 		    strcmp(*argv, "source") == 0) {
 			char *who = *argv;
-
 			NEXT_ARG();
 			if (matches(*argv, "help") == 0)
 				usage();
@@ -390,7 +354,6 @@
 			}
 		} else {
 			char *who = "address";
-
 			if (strcmp(*argv, "addr") == 0 ||
 			    strcmp(*argv, "address") == 0) {
 				who = *argv;
@@ -437,9 +400,17 @@
 		ack = 0;
 	}
 
-	if (genl_init_handle(&grth, TCP_METRICS_GENL_NAME, &genl_family))
-		exit(1);
-	req.n.nlmsg_type = genl_family;
+	if (genl_family < 0) {
+		if (rtnl_open_byproto(&grth, 0, NETLINK_GENERIC) < 0) {
+			fprintf(stderr, "Cannot open generic netlink socket\n");
+			exit(1);
+		}
+		genl_family = genl_resolve_family(&grth,
+						  TCP_METRICS_GENL_NAME);
+		if (genl_family < 0)
+			exit(1);
+		req.n.nlmsg_type = genl_family;
+	}
 
 	if (!(cmd & CMD_FLUSH) && (atype >= 0 || (cmd & CMD_DEL))) {
 		if (ack)
@@ -496,16 +467,15 @@
 	}
 
 	if (ack) {
-		if (rtnl_talk(&grth, &req.n, NULL) < 0)
+		if (rtnl_talk(&grth, &req.n, NULL, 0) < 0)
 			return -2;
 	} else if (atype >= 0) {
-		if (rtnl_talk(&grth, &req.n, &answer) < 0)
+		if (rtnl_talk(&grth, &req.n, &req.n, sizeof(req)) < 0)
 			return -2;
-		if (process_msg(answer, stdout) < 0) {
+		if (process_msg(NULL, &req.n, stdout) < 0) {
 			fprintf(stderr, "Dump terminated\n");
 			exit(1);
 		}
-		free(answer);
 	} else {
 		req.n.nlmsg_seq = grth.dump = ++grth.seq;
 		if (rtnl_send(&grth, &req, req.n.nlmsg_len) < 0) {
@@ -513,12 +483,10 @@
 			exit(1);
 		}
 
-		new_json_obj(json);
 		if (rtnl_dump_filter(&grth, process_msg, stdout) < 0) {
 			fprintf(stderr, "Dump terminated\n");
 			exit(1);
 		}
-		delete_json_obj();
 	}
 	return 0;
 }
@@ -536,7 +504,7 @@
 	if (matches(argv[0], "help") == 0)
 		usage();
 
-	fprintf(stderr, "Command \"%s\" is unknown, try \"ip tcp_metrics help\".\n",
-			*argv);
+	fprintf(stderr, "Command \"%s\" is unknown, "
+			"try \"ip tcp_metrics help\".\n", *argv);
 	exit(-1);
 }
diff --git a/ip/tunnel.c b/ip/tunnel.c
index 88585cf..39f825b 100644
--- a/ip/tunnel.c
+++ b/ip/tunnel.c
@@ -33,30 +33,36 @@
 #include <linux/if.h>
 #include <linux/ip.h>
 #include <linux/if_tunnel.h>
-#include <linux/if_arp.h>
 
 #include "utils.h"
 #include "tunnel.h"
-#include "json_print.h"
 
 const char *tnl_strproto(__u8 proto)
 {
+	static char buf[16];
+
 	switch (proto) {
 	case IPPROTO_IPIP:
-		return "ip";
+		strcpy(buf, "ip");
+		break;
 	case IPPROTO_GRE:
-		return "gre";
+		strcpy(buf, "gre");
+		break;
 	case IPPROTO_IPV6:
-		return "ipv6";
+		strcpy(buf, "ipv6");
+		break;
 	case IPPROTO_ESP:
-		return "esp";
-	case IPPROTO_MPLS:
-		return "mpls";
+		strcpy(buf, "esp");
+		break;
 	case 0:
-		return "any";
+		strcpy(buf, "any");
+		break;
 	default:
-		return "unknown";
+		strcpy(buf, "unknown");
+		break;
 	}
+
+	return buf;
 }
 
 int tnl_get_ioctl(const char *basedev, void *p)
@@ -65,8 +71,8 @@
 	int fd;
 	int err;
 
-	strlcpy(ifr.ifr_name, basedev, IFNAMSIZ);
-	ifr.ifr_ifru.ifru_data = (void *)p;
+	strncpy(ifr.ifr_name, basedev, IFNAMSIZ);
+	ifr.ifr_ifru.ifru_data = (void*)p;
 
 	fd = socket(preferred_family, SOCK_DGRAM, 0);
 	if (fd < 0) {
@@ -90,9 +96,9 @@
 	int err;
 
 	if (cmd == SIOCCHGTUNNEL && name[0])
-		strlcpy(ifr.ifr_name, name, IFNAMSIZ);
+		strncpy(ifr.ifr_name, name, IFNAMSIZ);
 	else
-		strlcpy(ifr.ifr_name, basedev, IFNAMSIZ);
+		strncpy(ifr.ifr_name, basedev, IFNAMSIZ);
 	ifr.ifr_ifru.ifru_data = p;
 
 	fd = socket(preferred_family, SOCK_DGRAM, 0);
@@ -116,9 +122,9 @@
 	int err;
 
 	if (name[0])
-		strlcpy(ifr.ifr_name, name, IFNAMSIZ);
+		strncpy(ifr.ifr_name, name, IFNAMSIZ);
 	else
-		strlcpy(ifr.ifr_name, basedev, IFNAMSIZ);
+		strncpy(ifr.ifr_name, basedev, IFNAMSIZ);
 
 	ifr.ifr_ifru.ifru_data = p;
 
@@ -143,7 +149,7 @@
 	int fd;
 	int err;
 
-	strlcpy(ifr.ifr_name, name, IFNAMSIZ);
+	strncpy(ifr.ifr_name, name, IFNAMSIZ);
 	ifr.ifr_ifru.ifru_data = p;
 
 	fd = socket(preferred_family, SOCK_DGRAM, 0);
@@ -177,276 +183,43 @@
 
 __be32 tnl_parse_key(const char *name, const char *key)
 {
-	unsigned int uval;
+	unsigned uval;
 
 	if (strchr(key, '.'))
 		return get_addr32(key);
 
 	if (get_unsigned(&uval, key, 0) < 0) {
-		fprintf(stderr,
-			"invalid value for \"%s\": \"%s\"; it should be an unsigned integer\n",
-			name, key);
+		fprintf(stderr, "invalid value for \"%s\": \"%s\";", name, key);
+		fprintf(stderr, " it should be an unsigned integer\n");
 		exit(-1);
 	}
 	return htonl(uval);
 }
 
-static const char *tnl_encap_str(const char *name, int enabled, int port)
+/* tnl_print_stats - print tunnel statistics
+ *
+ * @buf - tunnel interface's line in /proc/net/dev,
+ *        starting past the interface name and following colon
+ */
+void tnl_print_stats(const char *buf)
 {
-	static const char ne[][sizeof("no")] = {
-		[0] = "no",
-		[1] = "",
-	};
-	static char buf[32];
-	char b1[16];
-	const char *val;
+	unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
+		      rx_fifo, rx_frame,
+		      tx_bytes, tx_packets, tx_errs, tx_drops,
+		      tx_fifo, tx_colls, tx_carrier, rx_multi;
 
-	if (!port) {
-		val = "auto ";
-	} else if (port < 0) {
-		val = "";
-	} else {
-		snprintf(b1, sizeof(b1), "%u ", port - 1);
-		val = b1;
-	}
-
-	snprintf(buf, sizeof(buf), "%sencap-%s %s", ne[!!enabled], name, val);
-	return buf;
-}
-
-void tnl_print_encap(struct rtattr *tb[],
-		     int encap_type, int encap_flags,
-		     int encap_sport, int encap_dport)
-{
-	__u16 type, flags, sport, dport;
-
-	if (!tb[encap_type])
+	if (sscanf(buf, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu",
+	           &rx_bytes, &rx_packets, &rx_errs, &rx_drops,
+	           &rx_fifo, &rx_frame, &rx_multi,
+	           &tx_bytes, &tx_packets, &tx_errs, &tx_drops,
+	           &tx_fifo, &tx_colls, &tx_carrier) != 14)
 		return;
 
-	type = rta_getattr_u16(tb[encap_type]);
-	if (type == TUNNEL_ENCAP_NONE)
-		return;
-
-	flags = rta_getattr_u16(tb[encap_flags]);
-	sport = rta_getattr_u16(tb[encap_sport]);
-	dport = rta_getattr_u16(tb[encap_dport]);
-
-	open_json_object("encap");
-	print_string(PRINT_FP, NULL, "encap ", NULL);
-
-	switch (type) {
-	case TUNNEL_ENCAP_FOU:
-		print_string(PRINT_ANY, "type", "%s ", "fou");
-		break;
-	case TUNNEL_ENCAP_GUE:
-		print_string(PRINT_ANY, "type", "%s ", "gue");
-		break;
-	default:
-		print_null(PRINT_ANY, "type", "%s ", "unknown");
-		break;
-	}
-
-	if (is_json_context()) {
-		print_uint(PRINT_JSON, "sport", NULL, ntohs(sport));
-		print_uint(PRINT_JSON, "dport", NULL, ntohs(dport));
-		print_bool(PRINT_JSON, "csum", NULL,
-			   flags & TUNNEL_ENCAP_FLAG_CSUM);
-		print_bool(PRINT_JSON, "csum6", NULL,
-			   flags & TUNNEL_ENCAP_FLAG_CSUM6);
-		print_bool(PRINT_JSON, "remcsum", NULL,
-			   flags & TUNNEL_ENCAP_FLAG_REMCSUM);
-		close_json_object();
-	} else {
-		int t;
-
-		t = sport ? ntohs(sport) + 1 : 0;
-		print_string(PRINT_FP, NULL, "%s",
-			     tnl_encap_str("sport", 1, t));
-
-		t = ntohs(dport) + 1;
-		print_string(PRINT_FP, NULL, "%s",
-			     tnl_encap_str("dport", 1, t));
-
-		t = flags & TUNNEL_ENCAP_FLAG_CSUM;
-		print_string(PRINT_FP, NULL, "%s",
-			     tnl_encap_str("csum", t, -1));
-
-		t = flags & TUNNEL_ENCAP_FLAG_CSUM6;
-		print_string(PRINT_FP, NULL, "%s",
-			     tnl_encap_str("csum6", t, -1));
-
-		t = flags & TUNNEL_ENCAP_FLAG_REMCSUM;
-		print_string(PRINT_FP, NULL, "%s",
-			     tnl_encap_str("remcsum", t, -1));
-	}
-}
-
-void tnl_print_endpoint(const char *name, const struct rtattr *rta, int family)
-{
-	const char *value;
-	inet_prefix dst;
-
-	if (!rta) {
-		value = "any";
-	} else if (get_addr_rta(&dst, rta, family)) {
-		value = "unknown";
-	} else if (dst.flags & ADDRTYPE_UNSPEC) {
-		value = "any";
-	} else {
-		value = format_host(family, dst.bytelen, dst.data);
-		if (!value)
-			value = "unknown";
-	}
-
-	if (is_json_context()) {
-		print_string(PRINT_JSON, name, NULL, value);
-	} else {
-		SPRINT_BUF(b1);
-
-		snprintf(b1, sizeof(b1), "%s %%s ", name);
-		print_string(PRINT_FP, NULL, b1, value);
-	}
-}
-
-void tnl_print_gre_flags(__u8 proto,
-			 __be16 i_flags, __be16 o_flags,
-			 __be32 i_key, __be32 o_key)
-{
-	if ((i_flags & GRE_KEY) && (o_flags & GRE_KEY) &&
-	    o_key == i_key) {
-		print_uint(PRINT_ANY, "key", " key %u", ntohl(i_key));
-	} else {
-		if (i_flags & GRE_KEY)
-			print_uint(PRINT_ANY, "ikey", " ikey %u", ntohl(i_key));
-		if (o_flags & GRE_KEY)
-			print_uint(PRINT_ANY, "okey", " okey %u", ntohl(o_key));
-	}
-
-	if (proto != IPPROTO_GRE)
-		return;
-
-	open_json_array(PRINT_JSON, "flags");
-	if (i_flags & GRE_SEQ) {
-		if (is_json_context())
-			print_string(PRINT_JSON, NULL, "%s", "rx_drop_ooseq");
-		else
-			printf("%s  Drop packets out of sequence.", _SL_);
-	}
-	if (i_flags & GRE_CSUM) {
-		if (is_json_context())
-			print_string(PRINT_JSON, NULL, "%s", "rx_csum");
-		else
-			printf("%s  Checksum in received packet is required.", _SL_);
-	}
-	if (o_flags & GRE_SEQ) {
-		if (is_json_context())
-			print_string(PRINT_JSON, NULL, "%s", "tx_seq");
-		else
-			printf("%s  Sequence packets on output.", _SL_);
-	}
-	if (o_flags & GRE_CSUM) {
-		if (is_json_context())
-			print_string(PRINT_JSON, NULL, "%s", "tx_csum");
-		else
-			printf("%s  Checksum output packets.", _SL_);
-	}
-	close_json_array(PRINT_JSON, NULL);
-}
-
-static void tnl_print_stats(const struct rtnl_link_stats64 *s)
-{
 	printf("%s", _SL_);
 	printf("RX: Packets    Bytes        Errors CsumErrs OutOfSeq Mcasts%s", _SL_);
-	printf("    %-10lld %-12lld %-6lld %-8lld %-8lld %-8lld%s",
-	       s->rx_packets, s->rx_bytes, s->rx_errors, s->rx_frame_errors,
-	       s->rx_fifo_errors, s->multicast, _SL_);
+	printf("    %-10ld %-12ld %-6ld %-8ld %-8ld %-8ld%s",
+	       rx_packets, rx_bytes, rx_errs, rx_frame, rx_fifo, rx_multi, _SL_);
 	printf("TX: Packets    Bytes        Errors DeadLoop NoRoute  NoBufs%s", _SL_);
-	printf("    %-10lld %-12lld %-6lld %-8lld %-8lld %-6lld",
-	       s->tx_packets, s->tx_bytes, s->tx_errors, s->collisions,
-	       s->tx_carrier_errors, s->tx_dropped);
-}
-
-static int print_nlmsg_tunnel(struct nlmsghdr *n, void *arg)
-{
-	struct tnl_print_nlmsg_info *info = arg;
-	struct ifinfomsg *ifi = NLMSG_DATA(n);
-	struct rtattr *tb[IFLA_MAX+1];
-	const char *name, *n1;
-
-	if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
-		return 0;
-
-	if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi)))
-		return -1;
-
-	if (preferred_family == AF_INET) {
-		switch (ifi->ifi_type) {
-		case ARPHRD_TUNNEL:
-		case ARPHRD_IPGRE:
-		case ARPHRD_SIT:
-			break;
-		default:
-			return 0;
-		}
-	} else {
-		switch (ifi->ifi_type) {
-		case ARPHRD_TUNNEL6:
-		case ARPHRD_IP6GRE:
-			break;
-		default:
-			return 0;
-		}
-	}
-
-	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
-
-	if (!tb[IFLA_IFNAME])
-		return 0;
-
-	name = rta_getattr_str(tb[IFLA_IFNAME]);
-
-	/* Assume p1->name[IFNAMSIZ] is first field of structure */
-	n1 = info->p1;
-	if (n1[0] && strcmp(n1, name))
-		return 0;
-
-	info->ifi = ifi;
-	info->init(info);
-
-	/* TODO: parse netlink attributes */
-	if (tnl_get_ioctl(name, info->p2))
-		return 0;
-
-	if (!info->match(info))
-		return 0;
-
-	info->print(info->p2);
-	if (show_stats) {
-		struct rtnl_link_stats64 s;
-
-		if (get_rtnl_link_stats_rta(&s, tb) <= 0)
-			return -1;
-
-		tnl_print_stats(&s);
-	}
-	fputc('\n', stdout);
-
-	return 0;
-}
-
-int do_tunnels_list(struct tnl_print_nlmsg_info *info)
-{
-	new_json_obj(json);
-	if (rtnl_linkdump_req(&rth, preferred_family) < 0) {
-		perror("Cannot send dump request\n");
-		return -1;
-	}
-
-	if (rtnl_dump_filter(&rth, print_nlmsg_tunnel, info) < 0) {
-		fprintf(stderr, "Dump terminated\n");
-		return -1;
-	}
-	delete_json_obj();
-
-	return 0;
+	printf("    %-10ld %-12ld %-6ld %-8ld %-8ld %-6ld",
+	       tx_packets, tx_bytes, tx_errs, tx_colls, tx_carrier, tx_drops);
 }
diff --git a/ip/tunnel.h b/ip/tunnel.h
index 604f8cb..9a03c0d 100644
--- a/ip/tunnel.h
+++ b/ip/tunnel.h
@@ -21,26 +21,8 @@
 #ifndef __TUNNEL_H__
 #define __TUNNEL_H__ 1
 
-#include <stdbool.h>
 #include <linux/types.h>
 
-struct rtattr;
-struct ifinfomsg;
-
-extern struct rtnl_handle rth;
-
-struct tnl_print_nlmsg_info {
-	const struct ifinfomsg *ifi;
-	const void *p1;
-	void *p2;
-
-	void (*init)(const struct tnl_print_nlmsg_info *info);
-	bool (*match)(const struct tnl_print_nlmsg_info *info);
-	void (*print)(const void *t);
-};
-
-int do_tunnels_list(struct tnl_print_nlmsg_info *info);
-
 const char *tnl_strproto(__u8 proto);
 
 int tnl_get_ioctl(const char *basedev, void *p);
@@ -50,13 +32,6 @@
 int tnl_6rd_ioctl(int cmd, const char *name, void *p);
 int tnl_ioctl_get_6rd(const char *name, void *p);
 __be32 tnl_parse_key(const char *name, const char *key);
-void tnl_print_encap(struct rtattr *tb[],
-		     int encap_type, int encap_flags,
-		     int encap_sport, int encap_dport);
-void tnl_print_endpoint(const char *name,
-			const struct rtattr *rta, int family);
-void tnl_print_gre_flags(__u8 proto,
-			 __be16 i_flags, __be16 o_flags,
-			 __be32 i_key, __be32 o_key);
+void tnl_print_stats(const char *buf);
 
 #endif
diff --git a/ip/xfrm.h b/ip/xfrm.h
index 9ba5ca6..773c92e 100644
--- a/ip/xfrm.h
+++ b/ip/xfrm.h
@@ -26,12 +26,16 @@
 
 #include <stdio.h>
 #include <sys/socket.h>
-#include <linux/in.h>
 #include <linux/xfrm.h>
-#include <linux/ipsec.h>
 
+#ifndef IPPROTO_SCTP
+# define IPPROTO_SCTP	132
+#endif
+#ifndef IPPROTO_DCCP
+# define IPPROTO_DCCP	33
+#endif
 #ifndef IPPROTO_MH
-#define IPPROTO_MH              135
+# define IPPROTO_MH	135
 #endif
 
 #define XFRMS_RTA(x)  ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_usersa_info))))
@@ -94,7 +98,6 @@
 	__u8 action_mask;
 	__u32 priority_mask;
 	__u8 policy_flags_mask;
-	__u8 filter_socket;
 
 	__u8 ptype;
 	__u8 ptype_mask;
@@ -104,9 +107,10 @@
 
 extern struct xfrm_filter filter;
 
-int xfrm_state_print(struct nlmsghdr *n, void *arg);
-int xfrm_state_print_nokeys(struct nlmsghdr *n, void *arg);
-int xfrm_policy_print(struct nlmsghdr *n, void *arg);
+int xfrm_state_print(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		     void *arg);
+int xfrm_policy_print(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		      void *arg);
 int do_xfrm_state(int argc, char **argv);
 int do_xfrm_policy(int argc, char **argv);
 int do_xfrm_monitor(int argc, char **argv);
@@ -119,16 +123,25 @@
 int xfrm_parse_mark(struct xfrm_mark *mark, int *argcp, char ***argvp);
 const char *strxf_xfrmproto(__u8 proto);
 const char *strxf_algotype(int type);
+const char *strxf_mask8(__u8 mask);
 const char *strxf_mask32(__u32 mask);
+const char *strxf_share(__u8 share);
 const char *strxf_proto(__u8 proto);
 const char *strxf_ptype(__u8 ptype);
+void xfrm_id_info_print(xfrm_address_t *saddr, struct xfrm_id *id,
+			__u8 mode, __u32 reqid, __u16 family, int force_spi,
+			FILE *fp, const char *prefix, const char *title);
+void xfrm_stats_print(struct xfrm_stats *s, FILE *fp, const char *prefix);
+void xfrm_lifetime_print(struct xfrm_lifetime_cfg *cfg,
+			 struct xfrm_lifetime_cur *cur,
+			 FILE *fp, const char *prefix);
 void xfrm_selector_print(struct xfrm_selector *sel, __u16 family,
 			 FILE *fp, const char *prefix);
 void xfrm_xfrma_print(struct rtattr *tb[], __u16 family,
-		      FILE *fp, const char *prefix, bool nokeys);
+		      FILE *fp, const char *prefix);
 void xfrm_state_info_print(struct xfrm_usersa_info *xsinfo,
 			    struct rtattr *tb[], FILE *fp, const char *prefix,
-			   const char *title, bool nokeys);
+			   const char *title);
 void xfrm_policy_info_print(struct xfrm_userpolicy_info *xpinfo,
 			    struct rtattr *tb[], FILE *fp, const char *prefix,
 			    const char *title);
diff --git a/ip/xfrm_monitor.c b/ip/xfrm_monitor.c
index e34b5fb..e6e991a 100644
--- a/ip/xfrm_monitor.c
+++ b/ip/xfrm_monitor.c
@@ -34,23 +34,22 @@
 #include "ip_common.h"
 
 static void usage(void) __attribute__((noreturn));
-static int listen_all_nsid;
-static bool nokeys;
+int listen_all_nsid;
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: ip xfrm monitor [ nokeys ] [ all-nsid ] [ all | OBJECTS | help ]\n"
-		"OBJECTS := { acquire | expire | SA | aevent | policy | report }\n");
+	fprintf(stderr, "Usage: ip xfrm monitor [all-nsid] [ all | OBJECTS | help ]\n");
+	fprintf(stderr, "OBJECTS := { acquire | expire | SA | aevent | policy | report }\n");
 	exit(-1);
 }
 
-static int xfrm_acquire_print(struct nlmsghdr *n, void *arg)
+static int xfrm_acquire_print(const struct sockaddr_nl *who,
+			      struct nlmsghdr *n, void *arg)
 {
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 	struct xfrm_user_acquire *xacq = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
-	struct rtattr *tb[XFRMA_MAX+1];
+	struct rtattr * tb[XFRMA_MAX+1];
 	__u16 family;
 
 	len -= NLMSG_LENGTH(sizeof(*xacq));
@@ -72,7 +71,6 @@
 	fprintf(fp, "proto %s ", strxf_xfrmproto(xacq->id.proto));
 	if (show_stats > 0 || xacq->id.spi) {
 		__u32 spi = ntohl(xacq->id.spi);
-
 		fprintf(fp, "spi 0x%08x", spi);
 		if (show_stats > 0)
 			fprintf(fp, "(%u)", spi);
@@ -106,9 +104,10 @@
 	return 0;
 }
 
-static int xfrm_state_flush_print(struct nlmsghdr *n, void *arg)
+static int xfrm_state_flush_print(const struct sockaddr_nl *who,
+				  struct nlmsghdr *n, void *arg)
 {
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 	struct xfrm_usersa_flush *xsf = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	const char *str;
@@ -135,10 +134,11 @@
 	return 0;
 }
 
-static int xfrm_policy_flush_print(struct nlmsghdr *n, void *arg)
+static int xfrm_policy_flush_print(const struct sockaddr_nl *who,
+				   struct nlmsghdr *n, void *arg)
 {
-	struct rtattr *tb[XFRMA_MAX+1];
-	FILE *fp = (FILE *)arg;
+	struct rtattr * tb[XFRMA_MAX+1];
+	FILE *fp = (FILE*)arg;
 	int len = n->nlmsg_len;
 
 	len -= NLMSG_SPACE(0);
@@ -159,7 +159,7 @@
 		if (RTA_PAYLOAD(tb[XFRMA_POLICY_TYPE]) < sizeof(*upt))
 			fprintf(fp, "(ERROR truncated)");
 
-		upt = RTA_DATA(tb[XFRMA_POLICY_TYPE]);
+		upt = (struct xfrm_userpolicy_type *)RTA_DATA(tb[XFRMA_POLICY_TYPE]);
 		fprintf(fp, "%s ", strxf_ptype(upt->type));
 	}
 
@@ -172,12 +172,13 @@
 	return 0;
 }
 
-static int xfrm_report_print(struct nlmsghdr *n, void *arg)
+static int xfrm_report_print(const struct sockaddr_nl *who,
+			     struct nlmsghdr *n, void *arg)
 {
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 	struct xfrm_user_report *xrep = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
-	struct rtattr *tb[XFRMA_MAX+1];
+	struct rtattr * tb[XFRMA_MAX+1];
 	__u16 family;
 
 	len -= NLMSG_LENGTH(sizeof(*xrep));
@@ -199,7 +200,7 @@
 
 	parse_rtattr(tb, XFRMA_MAX, XFRMREP_RTA(xrep), len);
 
-	xfrm_xfrma_print(tb, family, fp, "  ", nokeys);
+	xfrm_xfrma_print(tb, family, fp, "  ");
 
 	if (oneline)
 		fprintf(fp, "\n");
@@ -209,8 +210,7 @@
 
 static void xfrm_ae_flags_print(__u32 flags, void *arg)
 {
-	FILE *fp = (FILE *)arg;
-
+	FILE *fp = (FILE*)arg;
 	fprintf(fp, " (0x%x) ", flags);
 	if (!flags)
 		return;
@@ -225,8 +225,12 @@
 
 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));
+		rt_addr_n2a(sa_id->family, sizeof(sa_id->daddr), &sa_id->daddr,
+			    buf, sizeof(buf)));
 
 	fprintf(fp, " reqid 0x%x", reqid);
 
@@ -234,16 +238,20 @@
 	fprintf(fp, " SPI 0x%x", ntohl(sa_id->spi));
 }
 
-static int xfrm_ae_print(struct nlmsghdr *n, void *arg)
+static int xfrm_ae_print(const struct sockaddr_nl *who,
+			     struct nlmsghdr *n, void *arg)
 {
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 	struct xfrm_aevent_id *id = NLMSG_DATA(n);
+	char abuf[256];
 
 	fprintf(fp, "Async event ");
 	xfrm_ae_flags_print(id->flags, arg);
-	fprintf(fp, "\n\t");
+	fprintf(fp,"\n\t");
+	memset(abuf, '\0', sizeof(abuf));
 	fprintf(fp, "src %s ", rt_addr_n2a(id->sa_id.family,
-					   sizeof(id->saddr), &id->saddr));
+					   sizeof(id->saddr), &id->saddr,
+					   abuf, sizeof(abuf)));
 
 	xfrm_usersa_print(&id->sa_id, id->reqid, fp);
 
@@ -255,12 +263,16 @@
 
 static void xfrm_print_addr(FILE *fp, int family, xfrm_address_t *a)
 {
-	fprintf(fp, "%s", rt_addr_n2a(family, sizeof(*a), a));
+	char buf[256];
+
+	buf[0] = 0;
+	fprintf(fp, "%s", rt_addr_n2a(family, sizeof(*a), a, buf, sizeof(buf)));
 }
 
-static int xfrm_mapping_print(struct nlmsghdr *n, void *arg)
+static int xfrm_mapping_print(const struct sockaddr_nl *who,
+			     struct nlmsghdr *n, void *arg)
 {
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 	struct xfrm_user_mapping *map = NLMSG_DATA(n);
 
 	fprintf(fp, "Mapping change ");
@@ -277,10 +289,11 @@
 	return 0;
 }
 
-static int xfrm_accept_msg(struct rtnl_ctrl_data *ctrl,
+static int xfrm_accept_msg(const struct sockaddr_nl *who,
+			   struct rtnl_ctrl_data *ctrl,
 			   struct nlmsghdr *n, void *arg)
 {
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 
 	if (timestamp)
 		print_timestamp(fp);
@@ -297,31 +310,31 @@
 	case XFRM_MSG_DELSA:
 	case XFRM_MSG_UPDSA:
 	case XFRM_MSG_EXPIRE:
-		xfrm_state_print(n, arg);
+		xfrm_state_print(who, n, arg);
 		return 0;
 	case XFRM_MSG_NEWPOLICY:
 	case XFRM_MSG_DELPOLICY:
 	case XFRM_MSG_UPDPOLICY:
 	case XFRM_MSG_POLEXPIRE:
-		xfrm_policy_print(n, arg);
+		xfrm_policy_print(who, n, arg);
 		return 0;
 	case XFRM_MSG_ACQUIRE:
-		xfrm_acquire_print(n, arg);
+		xfrm_acquire_print(who, n, arg);
 		return 0;
 	case XFRM_MSG_FLUSHSA:
-		xfrm_state_flush_print(n, arg);
+		xfrm_state_flush_print(who, n, arg);
 		return 0;
 	case XFRM_MSG_FLUSHPOLICY:
-		xfrm_policy_flush_print(n, arg);
+		xfrm_policy_flush_print(who, n, arg);
 		return 0;
 	case XFRM_MSG_REPORT:
-		xfrm_report_print(n, arg);
+		xfrm_report_print(who, n, arg);
 		return 0;
 	case XFRM_MSG_NEWAE:
-		xfrm_ae_print(n, arg);
+		xfrm_ae_print(who, n, arg);
 		return 0;
 	case XFRM_MSG_MAPPING:
-		xfrm_mapping_print(n, arg);
+		xfrm_mapping_print(who, n, arg);
 		return 0;
 	default:
 		break;
@@ -340,13 +353,13 @@
 int do_xfrm_monitor(int argc, char **argv)
 {
 	char *file = NULL;
-	unsigned int groups = ~((unsigned)0); /* XXX */
-	int lacquire = 0;
-	int lexpire = 0;
-	int laevent = 0;
-	int lpolicy = 0;
-	int lsa = 0;
-	int lreport = 0;
+	unsigned groups = ~((unsigned)0); /* XXX */
+	int lacquire=0;
+	int lexpire=0;
+	int laevent=0;
+	int lpolicy=0;
+	int lsa=0;
+	int lreport=0;
 
 	rtnl_close(&rth);
 
@@ -354,33 +367,29 @@
 		if (matches(*argv, "file") == 0) {
 			NEXT_ARG();
 			file = *argv;
-		} else if (strcmp(*argv, "nokeys") == 0) {
-			nokeys = true;
-		} else if (strcmp(*argv, "all") == 0) {
-			/* fall out */
 		} else if (matches(*argv, "all-nsid") == 0) {
 			listen_all_nsid = 1;
 		} else if (matches(*argv, "acquire") == 0) {
-			lacquire = 1;
+			lacquire=1;
 			groups = 0;
 		} else if (matches(*argv, "expire") == 0) {
-			lexpire = 1;
+			lexpire=1;
 			groups = 0;
 		} else if (matches(*argv, "SA") == 0) {
-			lsa = 1;
+			lsa=1;
 			groups = 0;
 		} else if (matches(*argv, "aevent") == 0) {
-			laevent = 1;
+			laevent=1;
 			groups = 0;
 		} else if (matches(*argv, "policy") == 0) {
-			lpolicy = 1;
+			lpolicy=1;
 			groups = 0;
 		} else if (matches(*argv, "report") == 0) {
-			lreport = 1;
+			lreport=1;
 			groups = 0;
 		} else if (matches(*argv, "help") == 0) {
 			usage();
-		} else {
+		} else if (strcmp(*argv, "all")) {
 			fprintf(stderr, "Argument \"%s\" is unknown, try \"ip xfrm monitor help\".\n", *argv);
 			exit(-1);
 		}
@@ -419,7 +428,7 @@
 	if (listen_all_nsid && rtnl_listen_all_nsid(&rth) < 0)
 		exit(1);
 
-	if (rtnl_listen(&rth, xfrm_accept_msg, (void *)stdout) < 0)
+	if (rtnl_listen(&rth, xfrm_accept_msg, (void*)stdout) < 0)
 		exit(2);
 
 	return 0;
diff --git a/ip/xfrm_policy.c b/ip/xfrm_policy.c
index 7c0233c..efea1e8 100644
--- a/ip/xfrm_policy.c
+++ b/ip/xfrm_policy.c
@@ -33,7 +33,7 @@
 #include "xfrm.h"
 #include "ip_common.h"
 
-/* #define NLMSG_DELETEALL_BUF_SIZE (4096-512) */
+//#define NLMSG_DELETEALL_BUF_SIZE (4096-512)
 #define NLMSG_DELETEALL_BUF_SIZE 8192
 
 /*
@@ -52,60 +52,51 @@
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: ip xfrm policy { add | update } SELECTOR dir DIR [ ctx CTX ]\n"
-		"	[ mark MARK [ mask MASK ] ] [ index INDEX ] [ ptype PTYPE ]\n"
-		"	[ action ACTION ] [ priority PRIORITY ] [ flag FLAG-LIST ]\n"
-		"	[ if_id IF_ID ] [ LIMIT-LIST ] [ TMPL-LIST ]\n"
-		"Usage: ip xfrm policy { delete | get } { SELECTOR | index INDEX } dir DIR\n"
-		"	[ ctx CTX ] [ mark MARK [ mask MASK ] ] [ ptype PTYPE ]\n"
-		"Usage: ip xfrm policy { deleteall | list } [ nosock ] [ SELECTOR ] [ dir DIR ]\n"
-		"	[ index INDEX ] [ ptype PTYPE ] [ action ACTION ] [ priority PRIORITY ]\n"
-		"	[ flag FLAG-LIST ]\n"
-		"Usage: ip xfrm policy flush [ ptype PTYPE ]\n"
-		"Usage: ip xfrm policy count\n"
-		"Usage: ip xfrm policy set [ hthresh4 LBITS RBITS ] [ hthresh6 LBITS RBITS ]\n"
-		"SELECTOR := [ src ADDR[/PLEN] ] [ dst ADDR[/PLEN] ] [ dev DEV ] [ UPSPEC ]\n"
-		"UPSPEC := proto { { ");
-	fprintf(stderr, "%s | %s | %s | %s } ",
-		strxf_proto(IPPROTO_TCP),
-		strxf_proto(IPPROTO_UDP),
-		strxf_proto(IPPROTO_SCTP),
-		strxf_proto(IPPROTO_DCCP));
-	fprintf(stderr,
-		"[ sport PORT ] [ dport PORT ] |\n"
-		"                  { %s | %s | %s } ",
-		strxf_proto(IPPROTO_ICMP),
-		strxf_proto(IPPROTO_ICMPV6),
-		strxf_proto(IPPROTO_MH));
-	fprintf(stderr,
-		"[ type NUMBER ] [ code NUMBER ] |\n"
-		"                  %s",
-		strxf_proto(IPPROTO_GRE));
-	fprintf(stderr,
-		" [ key { DOTTED-QUAD | NUMBER } ] | PROTO }\n"
-		"DIR := in | out | fwd\n"
-		"PTYPE := main | sub\n"
-		"ACTION := allow | block\n"
-		"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
-		"FLAG := localok | icmp\n"
-		"LIMIT-LIST := [ LIMIT-LIST ] limit LIMIT\n"
-		"LIMIT := { time-soft | time-hard | time-use-soft | time-use-hard } SECONDS |\n"
-		"         { byte-soft | byte-hard } SIZE | { packet-soft | packet-hard } COUNT\n"
-		"TMPL-LIST := [ TMPL-LIST ] tmpl TMPL\n"
-		"TMPL := ID [ mode MODE ] [ reqid REQID ] [ level LEVEL ]\n"
-		"ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM-PROTO ] [ spi SPI ]\n"
-		"XFRM-PROTO := ");
-	fprintf(stderr,
-		"%s | %s | %s | %s | %s\n",
-		strxf_xfrmproto(IPPROTO_ESP),
-		strxf_xfrmproto(IPPROTO_AH),
-		strxf_xfrmproto(IPPROTO_COMP),
-		strxf_xfrmproto(IPPROTO_ROUTING),
-		strxf_xfrmproto(IPPROTO_DSTOPTS));
-	fprintf(stderr,
-		"MODE := transport | tunnel | beet | ro | in_trigger\n"
-		"LEVEL := required | use\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 policy count\n");
+	fprintf(stderr, "Usage: ip xfrm policy set [ hthresh4 LBITS RBITS ] [ hthresh6 LBITS RBITS ]\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 | 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, "%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\n", strxf_xfrmproto(IPPROTO_DSTOPTS));
+	fprintf(stderr, "MODE := transport | tunnel | beet | ro | in_trigger\n");
+	fprintf(stderr, "LEVEL := required | use\n");
 
 	exit(-1);
 }
@@ -250,37 +241,41 @@
 	return 0;
 }
 
-static int xfrm_policy_modify(int cmd, unsigned int flags, int argc, char **argv)
+static int xfrm_policy_modify(int cmd, unsigned flags, int argc, char **argv)
 {
 	struct rtnl_handle rth;
 	struct {
 		struct nlmsghdr			n;
 		struct xfrm_userpolicy_info	xpinfo;
 		char				buf[RTA_BUF_SIZE];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpinfo)),
-		.n.nlmsg_flags = NLM_F_REQUEST | flags,
-		.n.nlmsg_type = cmd,
-		.xpinfo.sel.family = preferred_family,
-		.xpinfo.lft.soft_byte_limit = XFRM_INF,
-		.xpinfo.lft.hard_byte_limit = XFRM_INF,
-		.xpinfo.lft.soft_packet_limit = XFRM_INF,
-		.xpinfo.lft.hard_packet_limit = XFRM_INF,
-	};
+	} req;
 	char *dirp = NULL;
 	char *selp = NULL;
 	char *ptypep = NULL;
 	char *sctxp = NULL;
-	struct xfrm_userpolicy_type upt = {};
-	char tmpls_buf[XFRM_TMPLS_BUF_SIZE] = {};
+	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 = {};
-	bool is_if_id_set = false;
-	__u32 if_id = 0;
+	} 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;
+	req.n.nlmsg_type = cmd;
+	req.xpinfo.sel.family = preferred_family;
+
+	req.xpinfo.lft.soft_byte_limit = XFRM_INF;
+	req.xpinfo.lft.hard_byte_limit = XFRM_INF;
+	req.xpinfo.lft.soft_packet_limit = XFRM_INF;
+	req.xpinfo.lft.hard_packet_limit = XFRM_INF;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "dir") == 0) {
@@ -349,11 +344,6 @@
 			xfrm_tmpl_parse(tmpl, &argc, &argv);
 
 			tmpls_len += sizeof(*tmpl);
-		} else if (strcmp(*argv, "if_id") == 0) {
-			NEXT_ARG();
-			if (get_u32(&if_id, *argv, 0))
-				invarg("IF_ID value is invalid", *argv);
-			is_if_id_set = true;
 		} else {
 			if (selp)
 				duparg("unknown", *argv);
@@ -386,7 +376,7 @@
 		int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK,
 				  (void *)&mark, sizeof(mark));
 		if (r < 0) {
-			fprintf(stderr, "%s: XFRMA_MARK failed\n", __func__);
+			fprintf(stderr, "%s: XFRMA_MARK failed\n",__func__);
 			exit(1);
 		}
 	}
@@ -396,16 +386,13 @@
 			  (void *)&ctx, ctx.sctx.len);
 	}
 
-	if (is_if_id_set)
-		addattr32(&req.n, sizeof(req.buf), XFRMA_IF_ID, if_id);
-
 	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
 		exit(1);
 
 	if (req.xpinfo.sel.family == AF_UNSPEC)
 		req.xpinfo.sel.family = AF_INET;
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		exit(2);
 
 	rtnl_close(&rth);
@@ -419,16 +406,9 @@
 	if (!filter.use)
 		return 1;
 
-	if (filter.xpinfo.sel.family != AF_UNSPEC &&
-	    filter.xpinfo.sel.family != xpinfo->sel.family)
-		return 0;
-
 	if ((xpinfo->dir^filter.xpinfo.dir)&filter.dir_mask)
 		return 0;
 
-	if (filter.filter_socket && (xpinfo->dir >= XFRM_POLICY_MAX))
-		return 0;
-
 	if ((ptype^filter.ptype)&filter.ptype_mask)
 		return 0;
 
@@ -476,15 +456,16 @@
 	return 1;
 }
 
-int xfrm_policy_print(struct nlmsghdr *n, void *arg)
+int xfrm_policy_print(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		      void *arg)
 {
-	struct rtattr *tb[XFRMA_MAX+1];
-	struct rtattr *rta;
+	struct rtattr * tb[XFRMA_MAX+1];
+	struct rtattr * rta;
 	struct xfrm_userpolicy_info *xpinfo = NULL;
 	struct xfrm_user_polexpire *xpexp = NULL;
 	struct xfrm_userpolicy_id *xpid = NULL;
 	__u8 ptype = XFRM_POLICY_TYPE_MAIN;
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 	int len = n->nlmsg_len;
 
 	if (n->nlmsg_type != XFRM_MSG_NEWPOLICY &&
@@ -530,7 +511,7 @@
 			fprintf(stderr, "too short XFRMA_POLICY_TYPE len\n");
 			return -1;
 		}
-		upt = RTA_DATA(tb[XFRMA_POLICY_TYPE]);
+		upt = (struct xfrm_userpolicy_type *)RTA_DATA(tb[XFRMA_POLICY_TYPE]);
 		ptype = upt->type;
 	}
 
@@ -545,7 +526,7 @@
 		fprintf(fp, "Expired ");
 
 	if (n->nlmsg_type == XFRM_MSG_DELPOLICY) {
-		/* xfrm_policy_id_print(); */
+		//xfrm_policy_id_print();
 		if (!tb[XFRMA_POLICY]) {
 			fprintf(stderr, "Buggy XFRM_MSG_DELPOLICY: no XFRMA_POLICY\n");
 			return -1;
@@ -554,7 +535,7 @@
 			fprintf(stderr, "Buggy XFRM_MSG_DELPOLICY: too short XFRMA_POLICY len\n");
 			return -1;
 		}
-		xpinfo = RTA_DATA(tb[XFRMA_POLICY]);
+		xpinfo = (struct xfrm_userpolicy_info *)RTA_DATA(tb[XFRMA_POLICY]);
 	}
 
 	xfrm_policy_info_print(xpinfo, tb, fp, NULL, NULL);
@@ -573,30 +554,34 @@
 }
 
 static int xfrm_policy_get_or_delete(int argc, char **argv, int delete,
-				     struct nlmsghdr **answer)
+				     void *res_nlbuf, size_t res_size)
 {
 	struct rtnl_handle rth;
 	struct {
 		struct nlmsghdr			n;
 		struct xfrm_userpolicy_id	xpid;
 		char				buf[RTA_BUF_SIZE];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpid)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = delete ? XFRM_MSG_DELPOLICY
-				       : XFRM_MSG_GETPOLICY,
-	};
+	} req;
 	char *dirp = NULL;
 	char *selp = NULL;
 	char *indexp = NULL;
 	char *ptypep = NULL;
 	char *sctxp = NULL;
-	struct xfrm_userpolicy_type upt = {};
+	struct xfrm_userpolicy_type upt;
 	struct xfrm_mark mark = {0, 0};
 	struct {
 		struct xfrm_user_sec_ctx sctx;
 		char    str[CTX_BUF_SIZE];
-	} ctx = {};
+	} 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;
+	req.n.nlmsg_type = delete ? XFRM_MSG_DELPOLICY : XFRM_MSG_GETPOLICY;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "dir") == 0) {
@@ -674,7 +659,7 @@
 		int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK,
 				  (void *)&mark, sizeof(mark));
 		if (r < 0) {
-			fprintf(stderr, "%s: XFRMA_MARK failed\n", __func__);
+			fprintf(stderr, "%s: XFRMA_MARK failed\n",__func__);
 			exit(1);
 		}
 	}
@@ -684,7 +669,7 @@
 			  (void *)&ctx, ctx.sctx.len);
 	}
 
-	if (rtnl_talk(&rth, &req.n, answer) < 0)
+	if (rtnl_talk(&rth, &req.n, res_nlbuf, res_size) < 0)
 		exit(2);
 
 	rtnl_close(&rth);
@@ -694,21 +679,23 @@
 
 static int xfrm_policy_delete(int argc, char **argv)
 {
-	return xfrm_policy_get_or_delete(argc, argv, 1, NULL);
+	return xfrm_policy_get_or_delete(argc, argv, 1, NULL, 0);
 }
 
 static int xfrm_policy_get(int argc, char **argv)
 {
-	struct nlmsghdr *n = NULL;
+	char buf[NLMSG_BUF_SIZE];
+	struct nlmsghdr *n = (struct nlmsghdr *)buf;
 
-	xfrm_policy_get_or_delete(argc, argv, 0, &n);
+	memset(buf, 0, sizeof(buf));
 
-	if (xfrm_policy_print(n, (void *)stdout) < 0) {
+	xfrm_policy_get_or_delete(argc, argv, 0, n, sizeof(buf));
+
+	if (xfrm_policy_print(NULL, n, (void*)stdout) < 0) {
 		fprintf(stderr, "An error :-)\n");
 		exit(1);
 	}
 
-	free(n);
 	return 0;
 }
 
@@ -716,7 +703,9 @@
  * With an existing policy of nlmsg, make new nlmsg for deleting the policy
  * and store it to buffer.
  */
-static int xfrm_policy_keep(struct nlmsghdr *n, void *arg)
+static int xfrm_policy_keep(const struct sockaddr_nl *who,
+			    struct nlmsghdr *n,
+			    void *arg)
 {
 	struct xfrm_buffer *xb = (struct xfrm_buffer *)arg;
 	struct rtnl_handle *rth = xb->rth;
@@ -748,19 +737,17 @@
 			fprintf(stderr, "too short XFRMA_POLICY_TYPE len\n");
 			return -1;
 		}
-		upt = RTA_DATA(tb[XFRMA_POLICY_TYPE]);
+		upt = (struct xfrm_userpolicy_type *)RTA_DATA(tb[XFRMA_POLICY_TYPE]);
 		ptype = upt->type;
 	}
 
 	if (!xfrm_policy_filter_match(xpinfo, ptype))
 		return 0;
 
-	/* can't delete socket policies */
-	if (xpinfo->dir >= XFRM_POLICY_MAX)
-		return 0;
-
-	if (xb->offset + NLMSG_LENGTH(sizeof(*xpid)) > xb->size)
-		return 0;
+	if (xb->offset > xb->size) {
+		fprintf(stderr, "Policy buffer overflow\n");
+		return -1;
+	}
 
 	new_n = (struct nlmsghdr *)(xb->buf + xb->offset);
 	new_n->nlmsg_len = NLMSG_LENGTH(sizeof(*xpid));
@@ -773,17 +760,8 @@
 	xpid->dir = xpinfo->dir;
 	xpid->index = xpinfo->index;
 
-	if (tb[XFRMA_MARK]) {
-		int r = addattr_l(new_n, xb->size, XFRMA_MARK,
-				(void *)RTA_DATA(tb[XFRMA_MARK]), tb[XFRMA_MARK]->rta_len);
-		if (r < 0) {
-			fprintf(stderr, "%s: XFRMA_MARK failed\n", __func__);
-			exit(1);
-		}
-	}
-
 	xb->offset += new_n->nlmsg_len;
-	xb->nlmsg_count++;
+	xb->nlmsg_count ++;
 
 	return 0;
 }
@@ -793,7 +771,7 @@
 	char *selp = NULL;
 	struct rtnl_handle rth;
 
-	if (argc > 0 || preferred_family != AF_UNSPEC)
+	if (argc > 0)
 		filter.use = 1;
 	filter.xpinfo.sel.family = preferred_family;
 
@@ -842,9 +820,6 @@
 
 			filter.policy_flags_mask = XFRM_FILTER_MASK_FULL;
 
-		} else if (strcmp(*argv, "nosock") == 0) {
-			/* filter all socket-based policies */
-			filter.filter_socket = 1;
 		} else {
 			if (selp)
 				invarg("unknown", *argv);
@@ -940,12 +915,12 @@
 	exit(0);
 }
 
-static int print_spdinfo(struct nlmsghdr *n, void *arg)
+static int print_spdinfo( struct nlmsghdr *n, void *arg)
 {
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 	__u32 *f = NLMSG_DATA(n);
-	struct rtattr *tb[XFRMA_SPD_MAX+1];
-	struct rtattr *rta;
+	struct rtattr * tb[XFRMA_SPD_MAX+1];
+	struct rtattr * rta;
 
 	int len = n->nlmsg_len;
 
@@ -958,7 +933,7 @@
 	rta = XFRMSAPD_RTA(f);
 	parse_rtattr(tb, XFRMA_SPD_MAX, rta, len);
 
-	fprintf(fp, "\t SPD");
+	fprintf(fp,"\t SPD");
 	if (tb[XFRMA_SPD_INFO]) {
 		struct xfrmu_spdinfo *si;
 
@@ -967,16 +942,16 @@
 			return -1;
 		}
 		si = RTA_DATA(tb[XFRMA_SPD_INFO]);
-		fprintf(fp, " IN  %d", si->incnt);
-		fprintf(fp, " OUT %d", si->outcnt);
-		fprintf(fp, " FWD %d", si->fwdcnt);
+		fprintf(fp," IN  %d", si->incnt);
+		fprintf(fp," OUT %d", si->outcnt);
+		fprintf(fp," FWD %d", si->fwdcnt);
 
 		if (show_stats) {
-			fprintf(fp, " (Sock:");
-			fprintf(fp, " IN %d", si->inscnt);
-			fprintf(fp, " OUT %d", si->outscnt);
-			fprintf(fp, " FWD %d", si->fwdscnt);
-			fprintf(fp, ")");
+			fprintf(fp," (Sock:");
+			fprintf(fp," IN %d", si->inscnt);
+			fprintf(fp," OUT %d", si->outscnt);
+			fprintf(fp," FWD %d", si->fwdscnt);
+			fprintf(fp,")");
 		}
 
 		fprintf(fp, "%s", _SL_);
@@ -990,36 +965,34 @@
 				return -1;
 			}
 			sh = RTA_DATA(tb[XFRMA_SPD_HINFO]);
-			fprintf(fp, "\t SPD buckets:");
-			fprintf(fp, " count %d", sh->spdhcnt);
-			fprintf(fp, " Max %d", sh->spdhmcnt);
+			fprintf(fp,"\t SPD buckets:");
+			fprintf(fp," count %d", sh->spdhcnt);
+			fprintf(fp," Max %d", sh->spdhmcnt);
 			fprintf(fp, "%s", _SL_);
 		}
 		if (tb[XFRMA_SPD_IPV4_HTHRESH]) {
 			struct xfrmu_spdhthresh *th;
-
 			if (RTA_PAYLOAD(tb[XFRMA_SPD_IPV4_HTHRESH]) < sizeof(*th)) {
 				fprintf(stderr, "SPDinfo: Wrong len %d\n", len);
 				return -1;
 			}
 			th = RTA_DATA(tb[XFRMA_SPD_IPV4_HTHRESH]);
-			fprintf(fp, "\t SPD IPv4 thresholds:");
-			fprintf(fp, " local %d", th->lbits);
-			fprintf(fp, " remote %d", th->rbits);
+			fprintf(fp,"\t SPD IPv4 thresholds:");
+			fprintf(fp," local %d", th->lbits);
+			fprintf(fp," remote %d", th->rbits);
 			fprintf(fp, "%s", _SL_);
 
 		}
 		if (tb[XFRMA_SPD_IPV6_HTHRESH]) {
 			struct xfrmu_spdhthresh *th;
-
 			if (RTA_PAYLOAD(tb[XFRMA_SPD_IPV6_HTHRESH]) < sizeof(*th)) {
 				fprintf(stderr, "SPDinfo: Wrong len %d\n", len);
 				return -1;
 			}
 			th = RTA_DATA(tb[XFRMA_SPD_IPV6_HTHRESH]);
-			fprintf(fp, "\t SPD IPv6 thresholds:");
-			fprintf(fp, " local %d", th->lbits);
-			fprintf(fp, " remote %d", th->rbits);
+			fprintf(fp,"\t SPD IPv6 thresholds:");
+			fprintf(fp," local %d", th->lbits);
+			fprintf(fp," remote %d", th->rbits);
 			fprintf(fp, "%s", _SL_);
 		}
 	}
@@ -1027,7 +1000,7 @@
 	if (oneline)
 		fprintf(fp, "\n");
 
-	return 0;
+        return 0;
 }
 
 static int xfrm_spd_setinfo(int argc, char **argv)
@@ -1037,16 +1010,18 @@
 		struct nlmsghdr			n;
 		__u32				flags;
 		char				buf[RTA_BUF_SIZE];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(__u32)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = XFRM_MSG_NEWSPDINFO,
-		.flags = 0XFFFFFFFF,
-	};
+	} req;
 
 	char *thr4 = NULL;
 	char *thr6 = NULL;
 
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(__u32));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = XFRM_MSG_NEWSPDINFO;
+	req.flags = 0XFFFFFFFF;
+
 	while (argc > 0) {
 		if (strcmp(*argv, "hthresh4") == 0) {
 			struct xfrmu_spdhthresh thr;
@@ -1088,7 +1063,7 @@
 	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
 		exit(1);
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		exit(2);
 
 	rtnl_close(&rth);
@@ -1102,23 +1077,24 @@
 	struct {
 		struct nlmsghdr			n;
 		__u32				flags;
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(__u32)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = XFRM_MSG_GETSPDINFO,
-		.flags = 0XFFFFFFFF,
-	};
-	struct nlmsghdr *answer;
+		char				ans[128];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(__u32));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = XFRM_MSG_GETSPDINFO;
+	req.flags = 0XFFFFFFFF;
 
 	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
 		exit(1);
 
-	if (rtnl_talk(&rth, &req.n, &answer) < 0)
+	if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0)
 		exit(2);
 
-	print_spdinfo(answer, (void *)stdout);
+	print_spdinfo(&req.n, (void*)stdout);
 
-	free(answer);
 	rtnl_close(&rth);
 
 	return 0;
@@ -1130,13 +1106,16 @@
 	struct {
 		struct nlmsghdr	n;
 		char		buf[RTA_BUF_SIZE];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(0), /* nlmsg data is nothing */
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = XFRM_MSG_FLUSHPOLICY,
-	};
+	} req;
 	char *ptypep = NULL;
-	struct xfrm_userpolicy_type upt = {};
+	struct xfrm_userpolicy_type upt;
+
+	memset(&req, 0, sizeof(req));
+	memset(&upt, 0, sizeof(upt));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(0); /* nlmsg data is nothing */
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = XFRM_MSG_FLUSHPOLICY;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "ptype") == 0) {
@@ -1163,7 +1142,7 @@
 	if (show_stats > 1)
 		fprintf(stderr, "Flush policy\n");
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		exit(2);
 
 	rtnl_close(&rth);
diff --git a/ip/xfrm_state.c b/ip/xfrm_state.c
index b03ccc5..b5734da 100644
--- a/ip/xfrm_state.c
+++ b/ip/xfrm_state.c
@@ -32,7 +32,7 @@
 #include "xfrm.h"
 #include "ip_common.h"
 
-/* #define NLMSG_DELETEALL_BUF_SIZE (4096-512) */
+//#define NLMSG_DELETEALL_BUF_SIZE (4096-512)
 #define NLMSG_DELETEALL_BUF_SIZE 8192
 
 /*
@@ -54,84 +54,60 @@
 
 static void usage(void)
 {
-	fprintf(stderr,
-		"Usage: ip xfrm state { add | update } ID [ ALGO-LIST ] [ mode MODE ]\n"
-		"        [ mark MARK [ mask MASK ] ] [ reqid REQID ] [ seq SEQ ]\n"
-		"        [ replay-window SIZE ] [ replay-seq SEQ ] [ replay-oseq SEQ ]\n"
-		"        [ replay-seq-hi SEQ ] [ replay-oseq-hi SEQ ]\n"
-		"        [ flag FLAG-LIST ] [ sel SELECTOR ] [ LIMIT-LIST ] [ encap ENCAP ]\n"
-		"        [ coa ADDR[/PLEN] ] [ ctx CTX ] [ extra-flag EXTRA-FLAG-LIST ]\n"
-		"        [ offload [dev DEV] dir DIR ]\n"
-		"        [ output-mark OUTPUT-MARK ]\n"
-		"        [ if_id IF_ID ]\n"
-		"Usage: ip xfrm state allocspi ID [ mode MODE ] [ mark MARK [ mask MASK ] ]\n"
-		"        [ reqid REQID ] [ seq SEQ ] [ min SPI max SPI ]\n"
-		"Usage: ip xfrm state { delete | get } ID [ mark MARK [ mask MASK ] ]\n"
-		"Usage: ip xfrm state deleteall [ ID ] [ mode MODE ] [ reqid REQID ]\n"
-		"        [ flag FLAG-LIST ]\n"
-		"Usage: ip xfrm state list [ nokeys ] [ ID ] [ mode MODE ] [ reqid REQID ]\n"
-		"        [ flag FLAG-LIST ]\n"
-		"Usage: ip xfrm state flush [ proto XFRM-PROTO ]\n"
-		"Usage: ip xfrm state count\n"
-		"ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM-PROTO ] [ spi SPI ]\n"
-		"XFRM-PROTO := ");
-	fprintf(stderr,
-		"%s | %s | %s | %s | %s\n",
-		strxf_xfrmproto(IPPROTO_ESP),
-		strxf_xfrmproto(IPPROTO_AH),
-		strxf_xfrmproto(IPPROTO_COMP),
-		strxf_xfrmproto(IPPROTO_ROUTING),
-		strxf_xfrmproto(IPPROTO_DSTOPTS));
-	fprintf(stderr,
-		"ALGO-LIST := [ ALGO-LIST ] ALGO\n"
-		"ALGO := { ");
-	fprintf(stderr,
-		"%s | %s",
-		strxf_algotype(XFRMA_ALG_CRYPT),
-		strxf_algotype(XFRMA_ALG_AUTH));
-	fprintf(stderr,
-		" } ALGO-NAME ALGO-KEYMAT |\n"
-		"        %s", strxf_algotype(XFRMA_ALG_AUTH_TRUNC));
-	fprintf(stderr,
-		" ALGO-NAME ALGO-KEYMAT ALGO-TRUNC-LEN |\n"
-		"        %s", strxf_algotype(XFRMA_ALG_AEAD));
-	fprintf(stderr,
-		" ALGO-NAME ALGO-KEYMAT ALGO-ICV-LEN |\n"
-		"        %s", strxf_algotype(XFRMA_ALG_COMP));
-	fprintf(stderr,
-		" ALGO-NAME\n"
-		"MODE := transport | tunnel | beet | ro | in_trigger\n"
-		"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
-		"FLAG := noecn | decap-dscp | nopmtudisc | wildrecv | icmp | af-unspec | align4 | esn\n"
-		"EXTRA-FLAG-LIST := [ EXTRA-FLAG-LIST ] EXTRA-FLAG\n"
-		"EXTRA-FLAG := dont-encap-dscp\n"
-		"SELECTOR := [ src ADDR[/PLEN] ] [ dst ADDR[/PLEN] ] [ dev DEV ] [ UPSPEC ]\n"
-		"UPSPEC := proto { { ");
-	fprintf(stderr,
-		"%s | %s | %s | %s",
-		strxf_proto(IPPROTO_TCP),
-		strxf_proto(IPPROTO_UDP),
-		strxf_proto(IPPROTO_SCTP),
-		strxf_proto(IPPROTO_DCCP));
-	fprintf(stderr,
-		" } [ sport PORT ] [ dport PORT ] |\n"
-		"                  { ");
-	fprintf(stderr,
-		"%s | %s | %s",
-		strxf_proto(IPPROTO_ICMP),
-		strxf_proto(IPPROTO_ICMPV6),
-		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"
-		"LIMIT-LIST := [ LIMIT-LIST ] limit LIMIT\n"
-		"LIMIT := { time-soft | time-hard | time-use-soft | time-use-hard } SECONDS |\n"
-		"         { byte-soft | byte-hard } SIZE | { packet-soft | packet-hard } COUNT\n"
-		"ENCAP := { espinudp | espinudp-nonike } SPORT DPORT OADDR\n"
-		"DIR := in | out\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, "        [ replay-seq-hi SEQ ] [ replay-oseq-hi SEQ ]\n");
+	fprintf(stderr, "        [ flag FLAG-LIST ] [ sel SELECTOR ] [ LIMIT-LIST ] [ encap ENCAP ]\n");
+	fprintf(stderr, "        [ coa ADDR[/PLEN] ] [ ctx CTX ] [ extra-flag EXTRA-FLAG-LIST ]\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 ]\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\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, " } ALGO-NAME ALGO-KEYMAT |\n");
+	fprintf(stderr, "        %s", strxf_algotype(XFRMA_ALG_AUTH_TRUNC));
+	fprintf(stderr, " ALGO-NAME ALGO-KEYMAT ALGO-TRUNC-LEN |\n");
+	fprintf(stderr, "        %s", strxf_algotype(XFRMA_ALG_AEAD));
+	fprintf(stderr, " ALGO-NAME ALGO-KEYMAT ALGO-ICV-LEN |\n");
+	fprintf(stderr, "        %s", strxf_algotype(XFRMA_ALG_COMP));
+	fprintf(stderr, " ALGO-NAME\n");
+	fprintf(stderr, "MODE := transport | tunnel | beet | ro | in_trigger\n");
+	fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
+	fprintf(stderr, "FLAG := noecn | decap-dscp | nopmtudisc | wildrecv | icmp | af-unspec | align4 | esn\n");
+	fprintf(stderr, "EXTRA-FLAG-LIST := [ EXTRA-FLAG-LIST ] EXTRA-FLAG\n");
+	fprintf(stderr, "EXTRA-FLAG := dont-encap-dscp\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");
 
 	exit(-1);
 }
@@ -147,7 +123,7 @@
 	fprintf(stderr, "warning: ALGO-NAME/ALGO-KEYMAT values will be sent to the kernel promiscuously! (verifying them isn't implemented yet)\n");
 #endif
 
-	strlcpy(alg->alg_name, name, sizeof(alg->alg_name));
+	strncpy(alg->alg_name, name, sizeof(alg->alg_name));
 
 	if (slen > 2 && strncmp(key, "0x", 2) == 0) {
 		/* split two chars "0x" from the top */
@@ -166,7 +142,7 @@
 		if (len > max)
 			invarg("ALGO-KEYMAT value makes buffer overflow\n", key);
 
-		for (i = -(plen % 2), j = 0; j < len; i += 2, j++) {
+		for (i = - (plen % 2), j = 0; j < len; i += 2, j++) {
 			char vbuf[3];
 			__u8 val;
 
@@ -199,9 +175,11 @@
 	int argc = *argcp;
 	char **argv = *argvp;
 
-	if (get_be32(seq, *argv, 0))
+	if (get_u32(seq, *argv, 0))
 		invarg("SEQ value is invalid", *argv);
 
+	*seq = htonl(*seq);
+
 	*argcp = argc;
 	*argvp = argv;
 
@@ -288,47 +266,16 @@
 	return 0;
 }
 
-static int xfrm_offload_dir_parse(__u8 *dir, int *argcp, char ***argvp)
-{
-	int argc = *argcp;
-	char **argv = *argvp;
-
-	if (strcmp(*argv, "in") == 0)
-		*dir = XFRM_OFFLOAD_INBOUND;
-	else if (strcmp(*argv, "out") == 0)
-		*dir = 0;
-	else
-		invarg("DIR value is invalid", *argv);
-
-	*argcp = argc;
-	*argvp = argv;
-
-	return 0;
-}
-
-static int xfrm_state_modify(int cmd, unsigned int flags, int argc, char **argv)
+static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv)
 {
 	struct rtnl_handle rth;
 	struct {
 		struct nlmsghdr	n;
 		struct xfrm_usersa_info xsinfo;
-		char			buf[RTA_BUF_SIZE];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo)),
-		.n.nlmsg_flags = NLM_F_REQUEST | flags,
-		.n.nlmsg_type = cmd,
-		.xsinfo.family = preferred_family,
-		.xsinfo.lft.soft_byte_limit = XFRM_INF,
-		.xsinfo.lft.hard_byte_limit = XFRM_INF,
-		.xsinfo.lft.soft_packet_limit = XFRM_INF,
-		.xsinfo.lft.hard_packet_limit = XFRM_INF,
-	};
-	struct xfrm_replay_state replay = {};
-	struct xfrm_replay_state_esn replay_esn = {};
-	struct xfrm_user_offload xuo = {};
-	unsigned int ifindex = 0;
-	__u8 dir = 0;
-	bool is_offload = false;
+		char  			buf[RTA_BUF_SIZE];
+	} req;
+	struct xfrm_replay_state replay;
+	struct xfrm_replay_state_esn replay_esn;
 	__u32 replay_window = 0;
 	__u32 seq = 0, oseq = 0, seq_hi = 0, oseq_hi = 0;
 	char *idp = NULL;
@@ -343,10 +290,22 @@
 	struct {
 		struct xfrm_user_sec_ctx sctx;
 		char    str[CTX_BUF_SIZE];
-	} ctx = {};
-	__u32 output_mark = 0;
-	bool is_if_id_set = false;
-	__u32 if_id = 0;
+	} ctx;
+
+	memset(&req, 0, sizeof(req));
+	memset(&replay, 0, sizeof(replay));
+	memset(&replay_esn, 0, sizeof(replay_esn));
+	memset(&ctx, 0, sizeof(ctx));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo));
+	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+	req.n.nlmsg_type = cmd;
+	req.xsinfo.family = preferred_family;
+
+	req.xsinfo.lft.soft_byte_limit = XFRM_INF;
+	req.xsinfo.lft.hard_byte_limit = XFRM_INF;
+	req.xsinfo.lft.soft_packet_limit = XFRM_INF;
+	req.xsinfo.lft.hard_packet_limit = XFRM_INF;
 
 	while (argc > 0) {
 		if (strcmp(*argv, "mode") == 0) {
@@ -397,14 +356,16 @@
 		} else if (strcmp(*argv, "encap") == 0) {
 			struct xfrm_encap_tmpl encap;
 			inet_prefix oa;
-			NEXT_ARG();
+		        NEXT_ARG();
 			xfrm_encap_type_parse(&encap.encap_type, &argc, &argv);
 			NEXT_ARG();
-			if (get_be16(&encap.encap_sport, *argv, 0))
+			if (get_u16(&encap.encap_sport, *argv, 0))
 				invarg("SPORT value after \"encap\" is invalid", *argv);
+			encap.encap_sport = htons(encap.encap_sport);
 			NEXT_ARG();
-			if (get_be16(&encap.encap_dport, *argv, 0))
+			if (get_u16(&encap.encap_dport, *argv, 0))
 				invarg("DPORT value after \"encap\" is invalid", *argv);
+			encap.encap_dport = htons(encap.encap_dport);
 			NEXT_ARG();
 			get_addr(&oa, *argv, AF_UNSPEC);
 			memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa));
@@ -412,7 +373,7 @@
 				  (void *)&encap, sizeof(encap));
 		} else if (strcmp(*argv, "coa") == 0) {
 			inet_prefix coa;
-			xfrm_address_t xcoa = {};
+			xfrm_address_t xcoa;
 
 			if (coap)
 				duparg("coa", *argv);
@@ -426,6 +387,7 @@
 			if (coa.bytelen > sizeof(xcoa))
 				invarg("value after \"coa\" is too large", *argv);
 
+			memset(&xcoa, 0, sizeof(xcoa));
 			memcpy(&xcoa, &coa.data, coa.bytelen);
 
 			addattr_l(&req.n, sizeof(req.buf), XFRMA_COADDR,
@@ -443,38 +405,9 @@
 			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 if (strcmp(*argv, "offload") == 0) {
-			is_offload = true;
-			NEXT_ARG();
-			if (strcmp(*argv, "dev") == 0) {
-				NEXT_ARG();
-				ifindex = ll_name_to_index(*argv);
-				if (!ifindex) {
-					invarg("value after \"offload dev\" is invalid", *argv);
-					is_offload = false;
-				}
-				NEXT_ARG();
-			}
-			if (strcmp(*argv, "dir") == 0) {
-				NEXT_ARG();
-				xfrm_offload_dir_parse(&dir, &argc, &argv);
-			} else {
-				invarg("value after \"offload dir\" is invalid", *argv);
-				is_offload = false;
-			}
-		} else if (strcmp(*argv, "output-mark") == 0) {
-			NEXT_ARG();
-			if (get_u32(&output_mark, *argv, 0))
-				invarg("value after \"output-mark\" is invalid", *argv);
-		} else if (strcmp(*argv, "if_id") == 0) {
-			NEXT_ARG();
-			if (get_u32(&if_id, *argv, 0))
-				invarg("value after \"if_id\" is invalid", *argv);
-			is_if_id_set = true;
 		} else {
 			/* try to assume ALGO */
 			int type = xfrm_algotype_getbyname(*argv);
-
 			switch (type) {
 			case XFRMA_ALG_AEAD:
 			case XFRMA_ALG_CRYPT:
@@ -573,7 +506,7 @@
 
 				xfrm_algo_parse((void *)&alg, type, name, key,
 						buf, sizeof(alg.buf));
-				len += alg.u.alg.alg_key_len / 8;
+				len += alg.u.alg.alg_key_len;
 
 				addattr_l(&req.n, sizeof(req.buf), type,
 					  (void *)&alg, len);
@@ -608,12 +541,6 @@
 		exit(-1);
 	}
 
-	if (is_offload) {
-		xuo.ifindex = ifindex;
-		xuo.flags = dir;
-		addattr_l(&req.n, sizeof(req.buf), XFRMA_OFFLOAD_DEV, &xuo,
-			  sizeof(xuo));
-	}
 	if (req.xsinfo.flags & XFRM_STATE_ESN ||
 	    replay_window > (sizeof(replay.bitmap) * 8)) {
 		replay_esn.seq = seq;
@@ -653,9 +580,6 @@
 		}
 	}
 
-	if (is_if_id_set)
-		addattr32(&req.n, sizeof(req.buf), XFRMA_IF_ID, if_id);
-
 	if (xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) {
 		switch (req.xsinfo.mode) {
 		case XFRM_MODE_TRANSPORT:
@@ -757,16 +681,13 @@
 		}
 	}
 
-	if (output_mark)
-		addattr32(&req.n, sizeof(req.buf), XFRMA_OUTPUT_MARK, output_mark);
-
 	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
 		exit(1);
 
 	if (req.xsinfo.family == AF_UNSPEC)
 		req.xsinfo.family = AF_INET;
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		exit(2);
 
 	rtnl_close(&rth);
@@ -780,24 +701,30 @@
 	struct {
 		struct nlmsghdr	n;
 		struct xfrm_userspi_info xspi;
-		char			buf[RTA_BUF_SIZE];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xspi)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = XFRM_MSG_ALLOCSPI,
-		.xspi.info.family = preferred_family,
-#if 0
-		.xspi.lft.soft_byte_limit = XFRM_INF,
-		.xspi.lft.hard_byte_limit = XFRM_INF,
-		.xspi.lft.soft_packet_limit = XFRM_INF,
-		.xspi.lft.hard_packet_limit = XFRM_INF,
-#endif
-	};
+		char  			buf[RTA_BUF_SIZE];
+	} req;
 	char *idp = NULL;
 	char *minp = NULL;
 	char *maxp = NULL;
 	struct xfrm_mark mark = {0, 0};
-	struct nlmsghdr *answer;
+	char res_buf[NLMSG_BUF_SIZE];
+	struct nlmsghdr *res_n = (struct nlmsghdr *)res_buf;
+
+	memset(res_buf, 0, sizeof(res_buf));
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xspi));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = XFRM_MSG_ALLOCSPI;
+	req.xspi.info.family = preferred_family;
+
+#if 0
+	req.xsinfo.lft.soft_byte_limit = XFRM_INF;
+	req.xsinfo.lft.hard_byte_limit = XFRM_INF;
+	req.xsinfo.lft.soft_packet_limit = XFRM_INF;
+	req.xsinfo.lft.hard_packet_limit = XFRM_INF;
+#endif
 
 	while (argc > 0) {
 		if (strcmp(*argv, "mode") == 0) {
@@ -897,15 +824,14 @@
 		req.xspi.info.family = AF_INET;
 
 
-	if (rtnl_talk(&rth, &req.n, &answer) < 0)
+	if (rtnl_talk(&rth, &req.n, res_n, sizeof(res_buf)) < 0)
 		exit(2);
 
-	if (xfrm_state_print(answer, (void *)stdout) < 0) {
+	if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) {
 		fprintf(stderr, "An error :-)\n");
 		exit(1);
 	}
 
-	free(answer);
 	rtnl_close(&rth);
 
 	return 0;
@@ -916,10 +842,6 @@
 	if (!filter.use)
 		return 1;
 
-	if (filter.xsinfo.family != AF_UNSPEC &&
-	    filter.xsinfo.family != xsinfo->family)
-		return 0;
-
 	if (filter.id_src_mask)
 		if (xfrm_addr_match(&xsinfo->saddr, &filter.xsinfo.saddr,
 				    filter.id_src_mask))
@@ -943,11 +865,12 @@
 	return 1;
 }
 
-static int __do_xfrm_state_print(struct nlmsghdr *n, void *arg, bool nokeys)
+int xfrm_state_print(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		     void *arg)
 {
-	FILE *fp = (FILE *)arg;
-	struct rtattr *tb[XFRMA_MAX+1];
-	struct rtattr *rta;
+	FILE *fp = (FILE*)arg;
+	struct rtattr * tb[XFRMA_MAX+1];
+	struct rtattr * rta;
 	struct xfrm_usersa_info *xsinfo = NULL;
 	struct xfrm_user_expire *xexp = NULL;
 	struct xfrm_usersa_id	*xsid = NULL;
@@ -963,7 +886,7 @@
 	}
 
 	if (n->nlmsg_type == XFRM_MSG_DELSA) {
-		/* Don't blame me for this .. Herbert made me do it */
+		/* Dont blame me for this .. Herbert made me do it */
 		xsid = NLMSG_DATA(n);
 		len -= NLMSG_SPACE(sizeof(*xsid));
 	} else if (n->nlmsg_type == XFRM_MSG_EXPIRE) {
@@ -1001,7 +924,7 @@
 	parse_rtattr(tb, XFRMA_MAX, rta, len);
 
 	if (n->nlmsg_type == XFRM_MSG_DELSA) {
-		/* xfrm_policy_id_print(); */
+		//xfrm_policy_id_print();
 
 		if (!tb[XFRMA_SA]) {
 			fprintf(stderr, "Buggy XFRM_MSG_DELSA: no XFRMA_SA\n");
@@ -1014,7 +937,7 @@
 		xsinfo = RTA_DATA(tb[XFRMA_SA]);
 	}
 
-	xfrm_state_info_print(xsinfo, tb, fp, NULL, NULL, nokeys);
+	xfrm_state_info_print(xsinfo, tb, fp, NULL, NULL);
 
 	if (n->nlmsg_type == XFRM_MSG_EXPIRE) {
 		fprintf(fp, "\t");
@@ -1029,33 +952,25 @@
 	return 0;
 }
 
-int xfrm_state_print(struct nlmsghdr *n, void *arg)
-{
-	return __do_xfrm_state_print(n, arg, false);
-}
-
-int xfrm_state_print_nokeys(struct nlmsghdr *n, void *arg)
-{
-	return __do_xfrm_state_print(n, arg, true);
-}
-
 static int xfrm_state_get_or_delete(int argc, char **argv, int delete)
 {
 	struct rtnl_handle rth;
 	struct {
 		struct nlmsghdr	n;
 		struct xfrm_usersa_id	xsid;
-		char			buf[RTA_BUF_SIZE];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = delete ? XFRM_MSG_DELSA : XFRM_MSG_GETSA,
-		.xsid.family = preferred_family,
-	};
+		char  			buf[RTA_BUF_SIZE];
+	} req;
 	struct xfrm_id id;
 	char *idp = NULL;
 	struct xfrm_mark mark = {0, 0};
 
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = delete ? XFRM_MSG_DELSA : XFRM_MSG_GETSA;
+	req.xsid.family = preferred_family;
+
 	while (argc > 0) {
 		xfrm_address_t saddr;
 
@@ -1099,20 +1014,21 @@
 		req.xsid.family = AF_INET;
 
 	if (delete) {
-		if (rtnl_talk(&rth, &req.n, NULL) < 0)
+		if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 			exit(2);
 	} else {
-		struct nlmsghdr *answer;
+		char buf[NLMSG_BUF_SIZE];
+		struct nlmsghdr *res_n = (struct nlmsghdr *)buf;
 
-		if (rtnl_talk(&rth, &req.n, &answer) < 0)
+		memset(buf, 0, sizeof(buf));
+
+		if (rtnl_talk(&rth, &req.n, res_n, sizeof(req)) < 0)
 			exit(2);
 
-		if (xfrm_state_print(answer, (void *)stdout) < 0) {
+		if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) {
 			fprintf(stderr, "An error :-)\n");
 			exit(1);
 		}
-
-		free(answer);
 	}
 
 	rtnl_close(&rth);
@@ -1124,7 +1040,9 @@
  * With an existing state of nlmsg, make new nlmsg for deleting the state
  * and store it to buffer.
  */
-static int xfrm_state_keep(struct nlmsghdr *n, void *arg)
+static int xfrm_state_keep(const struct sockaddr_nl *who,
+			   struct nlmsghdr *n,
+			   void *arg)
 {
 	struct xfrm_buffer *xb = (struct xfrm_buffer *)arg;
 	struct rtnl_handle *rth = xb->rth;
@@ -1132,7 +1050,6 @@
 	int len = n->nlmsg_len;
 	struct nlmsghdr *new_n;
 	struct xfrm_usersa_id *xsid;
-	struct rtattr *tb[XFRMA_MAX+1];
 
 	if (n->nlmsg_type != XFRM_MSG_NEWSA) {
 		fprintf(stderr, "Not a state: %08x %08x %08x\n",
@@ -1169,19 +1086,8 @@
 	addattr_l(new_n, xb->size, XFRMA_SRCADDR, &xsinfo->saddr,
 		  sizeof(xsid->daddr));
 
-	parse_rtattr(tb, XFRMA_MAX, XFRMS_RTA(xsinfo), len);
-
-	if (tb[XFRMA_MARK]) {
-		int r = addattr_l(new_n, xb->size, XFRMA_MARK,
-				(void *)RTA_DATA(tb[XFRMA_MARK]), tb[XFRMA_MARK]->rta_len);
-		if (r < 0) {
-			fprintf(stderr, "%s: XFRMA_MARK failed\n", __func__);
-			exit(1);
-		}
-	}
-
 	xb->offset += new_n->nlmsg_len;
-	xb->nlmsg_count++;
+	xb->nlmsg_count ++;
 
 	return 0;
 }
@@ -1190,16 +1096,13 @@
 {
 	char *idp = NULL;
 	struct rtnl_handle rth;
-	bool nokeys = false;
 
-	if (argc > 0 || preferred_family != AF_UNSPEC)
+	if(argc > 0)
 		filter.use = 1;
 	filter.xsinfo.family = preferred_family;
 
 	while (argc > 0) {
-		if (strcmp(*argv, "nokeys") == 0) {
-			nokeys = true;
-		} else if (strcmp(*argv, "mode") == 0) {
+		if (strcmp(*argv, "mode") == 0) {
 			NEXT_ARG();
 			xfrm_mode_parse(&filter.xsinfo.mode, &argc, &argv);
 
@@ -1315,9 +1218,7 @@
 			exit(1);
 		}
 
-		rtnl_filter_t filter = nokeys ?
-				xfrm_state_print_nokeys : xfrm_state_print;
-		if (rtnl_dump_filter(&rth, filter, stdout) < 0) {
+		if (rtnl_dump_filter(&rth, xfrm_state_print, stdout) < 0) {
 			fprintf(stderr, "Dump terminated\n");
 			exit(1);
 		}
@@ -1330,10 +1231,12 @@
 
 static int print_sadinfo(struct nlmsghdr *n, void *arg)
 {
-	FILE *fp = (FILE *)arg;
+	FILE *fp = (FILE*)arg;
 	__u32 *f = NLMSG_DATA(n);
 	struct rtattr *tb[XFRMA_SAD_MAX+1];
 	struct rtattr *rta;
+	__u32 *cnt;
+
 	int len = n->nlmsg_len;
 
 	len -= NLMSG_LENGTH(sizeof(__u32));
@@ -1346,13 +1249,11 @@
 	parse_rtattr(tb, XFRMA_SAD_MAX, rta, len);
 
 	if (tb[XFRMA_SAD_CNT]) {
-		__u32 cnt;
-
-		fprintf(fp, "\t SAD");
-		cnt = rta_getattr_u32(tb[XFRMA_SAD_CNT]);
-		fprintf(fp, " count %u", cnt);
+		fprintf(fp,"\t SAD");
+		cnt = (__u32 *)RTA_DATA(tb[XFRMA_SAD_CNT]);
+		fprintf(fp," count %d", *cnt);
 	} else {
-		fprintf(fp, "BAD SAD info returned\n");
+		fprintf(fp,"BAD SAD info returned\n");
 		return -1;
 	}
 
@@ -1361,20 +1262,20 @@
 			struct xfrmu_sadhinfo *si;
 
 			if (RTA_PAYLOAD(tb[XFRMA_SAD_HINFO]) < sizeof(*si)) {
-				fprintf(fp, "BAD SAD length returned\n");
+				fprintf(fp,"BAD SAD length returned\n");
 				return -1;
 			}
 
 			si = RTA_DATA(tb[XFRMA_SAD_HINFO]);
-			fprintf(fp, " (buckets ");
-			fprintf(fp, "count %d", si->sadhcnt);
-			fprintf(fp, " Max %d", si->sadhmcnt);
-			fprintf(fp, ")");
+			fprintf(fp," (buckets ");
+			fprintf(fp,"count %d", si->sadhcnt);
+			fprintf(fp," Max %d", si->sadhmcnt);
+			fprintf(fp,")");
 		}
 	}
-	fprintf(fp, "\n");
+	fprintf(fp,"\n");
 
-	return 0;
+        return 0;
 }
 
 static int xfrm_sad_getinfo(int argc, char **argv)
@@ -1383,23 +1284,23 @@
 	struct {
 		struct nlmsghdr			n;
 		__u32				flags;
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.flags)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = XFRM_MSG_GETSADINFO,
-		.flags = 0XFFFFFFFF,
-	};
-	struct nlmsghdr *answer;
+		char				ans[64];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.flags));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = XFRM_MSG_GETSADINFO;
+	req.flags = 0XFFFFFFFF;
 
 	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
 		exit(1);
 
-	if (rtnl_talk(&rth, &req.n, &answer) < 0)
+	if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0)
 		exit(2);
 
-	print_sadinfo(answer, (void *)stdout);
+	print_sadinfo(&req.n, (void*)stdout);
 
-	free(answer);
 	rtnl_close(&rth);
 
 	return 0;
@@ -1411,13 +1312,16 @@
 	struct {
 		struct nlmsghdr			n;
 		struct xfrm_usersa_flush	xsf;
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsf)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type = XFRM_MSG_FLUSHSA,
-	};
+	} req;
 	char *protop = NULL;
 
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsf));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = XFRM_MSG_FLUSHSA;
+	req.xsf.proto = 0;
+
 	while (argc > 0) {
 		if (strcmp(*argv, "proto") == 0) {
 			int ret;
@@ -1446,7 +1350,7 @@
 		fprintf(stderr, "Flush state with XFRM-PROTO value \"%s\"\n",
 			strxf_xfrmproto(req.xsf.proto));
 
-	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
 		exit(2);
 
 	rtnl_close(&rth);
diff --git a/lib/Android.mk b/lib/Android.mk
index b6bcb6f..3fa5a5e 100644
--- a/lib/Android.mk
+++ b/lib/Android.mk
@@ -1,13 +1,14 @@
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := utils.c rt_names.c ll_map.c ll_types.c ll_proto.c ll_addr.c i\
-                   inet_proto.c namespace.c json_writer.c json_print.c \
-                   names.c color.c bpf.c exec.c fs.c mpls_ntop.c mpls_pton.c
+LOCAL_SRC_FILES := \
+    color.c utils.c rt_names.c ll_types.c ll_proto.c ll_addr.c inet_proto.c \
+    mpls_pton.c namespace.c names.c libgenl.c libnetlink.c
 LOCAL_MODULE := libiprouteutil
-LOCAL_MODULE_TAGS := eng
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include $(LOCAL_PATH)/../include/uapi
+LOCAL_SYSTEM_SHARED_LIBRARIES := libc
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include
 LOCAL_CFLAGS := -O2 -g -W -Wall \
+	-DCONFDIR=\"/data/misc/net\" \
 	-DHAVE_UNISTD_H \
 	-DHAVE_ERRNO_H \
 	-DHAVE_NETINET_IN_H \
@@ -30,22 +31,22 @@
 	-DHAVE_LINUX_FD_H \
 	-DHAVE_TYPE_SSIZE_T \
 	-DHAVE_SETNS \
-	-DNEED_STRLCPY \
+	-D_GNU_SOURCE \
 	-Wno-pointer-arith \
 	-Wno-sign-compare \
 	-Wno-unused-parameter \
 	-Werror
 
-include $(BUILD_STATIC_LIBRARY)
+# This is a work around for b/18403920
+LOCAL_LDFLAGS := -Wl,--no-gc-sections
+
+include $(BUILD_SHARED_LIBRARY)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := libgenl.c libnetlink.c
+LOCAL_SRC_FILES := ll_map.c libnetlink.c
 LOCAL_MODULE := libnetlink
-LOCAL_MODULE_TAGS := eng
-LOCAL_C_INCLUDES := \
-        $(LOCAL_PATH)/../include \
-        $(LOCAL_PATH)/../include/uapi
-
+LOCAL_SYSTEM_SHARED_LIBRARIES := libc
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include
 LOCAL_CFLAGS := -O2 -g -W -Wall \
 	-DHAVE_UNISTD_H \
 	-DHAVE_ERRNO_H \
@@ -71,7 +72,6 @@
 	-Wno-pointer-arith \
 	-Wno-sign-compare \
 	-Wno-unused-parameter \
-	-Werror \
-	-Wno-\#warnings
+	-Werror
 
-include $(BUILD_STATIC_LIBRARY)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/lib/Makefile b/lib/Makefile
index bab8cbf..9d1307d 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,23 +1,27 @@
-# SPDX-License-Identifier: GPL-2.0
-include ../config.mk
+include ../Config
+
+ifeq ($(IP_CONFIG_SETNS),y)
+	CFLAGS += -DHAVE_SETNS
+endif
 
 CFLAGS += -fPIC
 
-UTILOBJ = utils.o rt_names.o ll_map.o ll_types.o ll_proto.o ll_addr.o \
-	inet_proto.o namespace.o json_writer.o json_print.o \
-	names.o color.o bpf.o exec.o fs.o
+UTILOBJ = utils.o rt_names.o ll_types.o ll_proto.o ll_addr.o \
+	inet_proto.o namespace.o json_writer.o \
+	names.o color.o
 
-NLOBJ=libgenl.o libnetlink.o
+NLOBJ=libgenl.o ll_map.o libnetlink.o
 
 all: libnetlink.a libutil.a
 
 libnetlink.a: $(NLOBJ)
-	$(QUIET_AR)$(AR) rcs $@ $^
+	$(AR) rcs $@ $(NLOBJ)
 
 libutil.a: $(UTILOBJ) $(ADDLIB)
-	$(QUIET_AR)$(AR) rcs $@ $^
+	$(AR) rcs $@ $(UTILOBJ) $(ADDLIB)
 
 install:
 
 clean:
 	rm -f $(NLOBJ) $(UTILOBJ) $(ADDLIB) libnetlink.a libutil.a
+
diff --git a/lib/bpf.c b/lib/bpf.c
deleted file mode 100644
index 10cf9bf..0000000
--- a/lib/bpf.c
+++ /dev/null
@@ -1,3240 +0,0 @@
-/*
- * bpf.c	BPF common code
- *
- *		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:	Daniel Borkmann <daniel@iogearbox.net>
- *		Jiri Pirko <jiri@resnulli.us>
- *		Alexei Starovoitov <ast@kernel.org>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <limits.h>
-#include <assert.h>
-
-#ifdef HAVE_ELF
-#include <libelf.h>
-#include <gelf.h>
-#endif
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/un.h>
-#include <sys/vfs.h>
-#include <sys/mount.h>
-#include <sys/syscall.h>
-#include <sys/sendfile.h>
-#include <sys/resource.h>
-
-#include <arpa/inet.h>
-
-#include "utils.h"
-#include "json_print.h"
-
-#include "bpf_util.h"
-#include "bpf_elf.h"
-#include "bpf_scm.h"
-
-struct bpf_prog_meta {
-	const char *type;
-	const char *subdir;
-	const char *section;
-	bool may_uds_export;
-};
-
-static const enum bpf_prog_type __bpf_types[] = {
-	BPF_PROG_TYPE_SCHED_CLS,
-	BPF_PROG_TYPE_SCHED_ACT,
-	BPF_PROG_TYPE_XDP,
-	BPF_PROG_TYPE_LWT_IN,
-	BPF_PROG_TYPE_LWT_OUT,
-	BPF_PROG_TYPE_LWT_XMIT,
-};
-
-static const struct bpf_prog_meta __bpf_prog_meta[] = {
-	[BPF_PROG_TYPE_SCHED_CLS] = {
-		.type		= "cls",
-		.subdir		= "tc",
-		.section	= ELF_SECTION_CLASSIFIER,
-		.may_uds_export	= true,
-	},
-	[BPF_PROG_TYPE_SCHED_ACT] = {
-		.type		= "act",
-		.subdir		= "tc",
-		.section	= ELF_SECTION_ACTION,
-		.may_uds_export	= true,
-	},
-	[BPF_PROG_TYPE_XDP] = {
-		.type		= "xdp",
-		.subdir		= "xdp",
-		.section	= ELF_SECTION_PROG,
-	},
-	[BPF_PROG_TYPE_LWT_IN] = {
-		.type		= "lwt_in",
-		.subdir		= "ip",
-		.section	= ELF_SECTION_PROG,
-	},
-	[BPF_PROG_TYPE_LWT_OUT] = {
-		.type		= "lwt_out",
-		.subdir		= "ip",
-		.section	= ELF_SECTION_PROG,
-	},
-	[BPF_PROG_TYPE_LWT_XMIT] = {
-		.type		= "lwt_xmit",
-		.subdir		= "ip",
-		.section	= ELF_SECTION_PROG,
-	},
-	[BPF_PROG_TYPE_LWT_SEG6LOCAL] = {
-		.type		= "lwt_seg6local",
-		.subdir		= "ip",
-		.section	= ELF_SECTION_PROG,
-	},
-};
-
-static const char *bpf_prog_to_subdir(enum bpf_prog_type type)
-{
-	assert(type < ARRAY_SIZE(__bpf_prog_meta) &&
-	       __bpf_prog_meta[type].subdir);
-	return __bpf_prog_meta[type].subdir;
-}
-
-const char *bpf_prog_to_default_section(enum bpf_prog_type type)
-{
-	assert(type < ARRAY_SIZE(__bpf_prog_meta) &&
-	       __bpf_prog_meta[type].section);
-	return __bpf_prog_meta[type].section;
-}
-
-#ifdef HAVE_ELF
-static int bpf_obj_open(const char *path, enum bpf_prog_type type,
-			const char *sec, __u32 ifindex, bool verbose);
-#else
-static int bpf_obj_open(const char *path, enum bpf_prog_type type,
-			const char *sec, __u32 ifindex, bool verbose)
-{
-	fprintf(stderr, "No ELF library support compiled in.\n");
-	errno = ENOSYS;
-	return -1;
-}
-#endif
-
-static inline __u64 bpf_ptr_to_u64(const void *ptr)
-{
-	return (__u64)(unsigned long)ptr;
-}
-
-static int bpf(int cmd, union bpf_attr *attr, unsigned int size)
-{
-#ifdef __NR_bpf
-	return syscall(__NR_bpf, cmd, attr, size);
-#else
-	fprintf(stderr, "No bpf syscall, kernel headers too old?\n");
-	errno = ENOSYS;
-	return -1;
-#endif
-}
-
-static int bpf_map_update(int fd, const void *key, const void *value,
-			  uint64_t flags)
-{
-	union bpf_attr attr = {};
-
-	attr.map_fd = fd;
-	attr.key = bpf_ptr_to_u64(key);
-	attr.value = bpf_ptr_to_u64(value);
-	attr.flags = flags;
-
-	return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
-}
-
-static int bpf_prog_fd_by_id(uint32_t id)
-{
-	union bpf_attr attr = {};
-
-	attr.prog_id = id;
-
-	return bpf(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr));
-}
-
-static int bpf_prog_info_by_fd(int fd, struct bpf_prog_info *info,
-			       uint32_t *info_len)
-{
-	union bpf_attr attr = {};
-	int ret;
-
-	attr.info.bpf_fd = fd;
-	attr.info.info = bpf_ptr_to_u64(info);
-	attr.info.info_len = *info_len;
-
-	*info_len = 0;
-	ret = bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr));
-	if (!ret)
-		*info_len = attr.info.info_len;
-
-	return ret;
-}
-
-int bpf_dump_prog_info(FILE *f, uint32_t id)
-{
-	struct bpf_prog_info info = {};
-	uint32_t len = sizeof(info);
-	int fd, ret, dump_ok = 0;
-	SPRINT_BUF(tmp);
-
-	open_json_object("prog");
-	print_uint(PRINT_ANY, "id", "id %u ", id);
-
-	fd = bpf_prog_fd_by_id(id);
-	if (fd < 0)
-		goto out;
-
-	ret = bpf_prog_info_by_fd(fd, &info, &len);
-	if (!ret && len) {
-		int jited = !!info.jited_prog_len;
-
-		print_string(PRINT_ANY, "tag", "tag %s ",
-			     hexstring_n2a(info.tag, sizeof(info.tag),
-					   tmp, sizeof(tmp)));
-		print_uint(PRINT_JSON, "jited", NULL, jited);
-		if (jited && !is_json_context())
-			fprintf(f, "jited ");
-		dump_ok = 1;
-	}
-
-	close(fd);
-out:
-	close_json_object();
-	return dump_ok;
-}
-
-static int bpf_parse_string(char *arg, bool from_file, __u16 *bpf_len,
-			    char **bpf_string, bool *need_release,
-			    const char separator)
-{
-	char sp;
-
-	if (from_file) {
-		size_t tmp_len, op_len = sizeof("65535 255 255 4294967295,");
-		char *tmp_string, *pos, c_prev = ' ';
-		FILE *fp;
-		int c;
-
-		tmp_len = sizeof("4096,") + BPF_MAXINSNS * op_len;
-		tmp_string = pos = calloc(1, tmp_len);
-		if (tmp_string == NULL)
-			return -ENOMEM;
-
-		fp = fopen(arg, "r");
-		if (fp == NULL) {
-			perror("Cannot fopen");
-			free(tmp_string);
-			return -ENOENT;
-		}
-
-		while ((c = fgetc(fp)) != EOF) {
-			switch (c) {
-			case '\n':
-				if (c_prev != ',')
-					*(pos++) = ',';
-				c_prev = ',';
-				break;
-			case ' ':
-			case '\t':
-				if (c_prev != ' ')
-					*(pos++) = c;
-				c_prev = ' ';
-				break;
-			default:
-				*(pos++) = c;
-				c_prev = c;
-			}
-			if (pos - tmp_string == tmp_len)
-				break;
-		}
-
-		if (!feof(fp)) {
-			free(tmp_string);
-			fclose(fp);
-			return -E2BIG;
-		}
-
-		fclose(fp);
-		*pos = 0;
-
-		*need_release = true;
-		*bpf_string = tmp_string;
-	} else {
-		*need_release = false;
-		*bpf_string = arg;
-	}
-
-	if (sscanf(*bpf_string, "%hu%c", bpf_len, &sp) != 2 ||
-	    sp != separator) {
-		if (*need_release)
-			free(*bpf_string);
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int bpf_ops_parse(int argc, char **argv, struct sock_filter *bpf_ops,
-			 bool from_file)
-{
-	char *bpf_string, *token, separator = ',';
-	int ret = 0, i = 0;
-	bool need_release;
-	__u16 bpf_len = 0;
-
-	if (argc < 1)
-		return -EINVAL;
-	if (bpf_parse_string(argv[0], from_file, &bpf_len, &bpf_string,
-			     &need_release, separator))
-		return -EINVAL;
-	if (bpf_len == 0 || bpf_len > BPF_MAXINSNS) {
-		ret = -EINVAL;
-		goto out;
-	}
-
-	token = bpf_string;
-	while ((token = strchr(token, separator)) && (++token)[0]) {
-		if (i >= bpf_len) {
-			fprintf(stderr, "Real program length exceeds encoded length parameter!\n");
-			ret = -EINVAL;
-			goto out;
-		}
-
-		if (sscanf(token, "%hu %hhu %hhu %u,",
-			   &bpf_ops[i].code, &bpf_ops[i].jt,
-			   &bpf_ops[i].jf, &bpf_ops[i].k) != 4) {
-			fprintf(stderr, "Error at instruction %d!\n", i);
-			ret = -EINVAL;
-			goto out;
-		}
-
-		i++;
-	}
-
-	if (i != bpf_len) {
-		fprintf(stderr, "Parsed program length is less than encoded length parameter!\n");
-		ret = -EINVAL;
-		goto out;
-	}
-	ret = bpf_len;
-out:
-	if (need_release)
-		free(bpf_string);
-
-	return ret;
-}
-
-void bpf_print_ops(struct rtattr *bpf_ops, __u16 len)
-{
-	struct sock_filter *ops = RTA_DATA(bpf_ops);
-	int i;
-
-	if (len == 0)
-		return;
-
-	open_json_object("bytecode");
-	print_uint(PRINT_ANY, "length", "bytecode \'%u,", len);
-	open_json_array(PRINT_JSON, "insns");
-
-	for (i = 0; i < len; i++) {
-		open_json_object(NULL);
-		print_hu(PRINT_ANY, "code", "%hu ", ops[i].code);
-		print_hhu(PRINT_ANY, "jt", "%hhu ", ops[i].jt);
-		print_hhu(PRINT_ANY, "jf", "%hhu ", ops[i].jf);
-		if (i == len - 1)
-			print_uint(PRINT_ANY, "k", "%u\'", ops[i].k);
-		else
-			print_uint(PRINT_ANY, "k", "%u,", ops[i].k);
-		close_json_object();
-	}
-
-	close_json_array(PRINT_JSON, NULL);
-	close_json_object();
-}
-
-static void bpf_map_pin_report(const struct bpf_elf_map *pin,
-			       const struct bpf_elf_map *obj)
-{
-	fprintf(stderr, "Map specification differs from pinned file!\n");
-
-	if (obj->type != pin->type)
-		fprintf(stderr, " - Type:         %u (obj) != %u (pin)\n",
-			obj->type, pin->type);
-	if (obj->size_key != pin->size_key)
-		fprintf(stderr, " - Size key:     %u (obj) != %u (pin)\n",
-			obj->size_key, pin->size_key);
-	if (obj->size_value != pin->size_value)
-		fprintf(stderr, " - Size value:   %u (obj) != %u (pin)\n",
-			obj->size_value, pin->size_value);
-	if (obj->max_elem != pin->max_elem)
-		fprintf(stderr, " - Max elems:    %u (obj) != %u (pin)\n",
-			obj->max_elem, pin->max_elem);
-	if (obj->flags != pin->flags)
-		fprintf(stderr, " - Flags:        %#x (obj) != %#x (pin)\n",
-			obj->flags, pin->flags);
-
-	fprintf(stderr, "\n");
-}
-
-struct bpf_prog_data {
-	unsigned int type;
-	unsigned int jited;
-};
-
-struct bpf_map_ext {
-	struct bpf_prog_data owner;
-	unsigned int btf_id_key;
-	unsigned int btf_id_val;
-};
-
-static int bpf_derive_elf_map_from_fdinfo(int fd, struct bpf_elf_map *map,
-					  struct bpf_map_ext *ext)
-{
-	unsigned int val, owner_type = 0, owner_jited = 0;
-	char *file = NULL;
-	char buff[4096];
-	FILE *fp;
-	int ret;
-
-	ret = asprintf(&file, "/proc/%d/fdinfo/%d", getpid(), fd);
-	if (ret < 0) {
-		fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
-		free(file);
-		return ret;
-	}
-	memset(map, 0, sizeof(*map));
-
-	fp = fopen(file, "r");
-	free(file);
-	if (!fp) {
-		fprintf(stderr, "No procfs support?!\n");
-		return -EIO;
-	}
-
-	while (fgets(buff, sizeof(buff), fp)) {
-		if (sscanf(buff, "map_type:\t%u", &val) == 1)
-			map->type = val;
-		else if (sscanf(buff, "key_size:\t%u", &val) == 1)
-			map->size_key = val;
-		else if (sscanf(buff, "value_size:\t%u", &val) == 1)
-			map->size_value = val;
-		else if (sscanf(buff, "max_entries:\t%u", &val) == 1)
-			map->max_elem = val;
-		else if (sscanf(buff, "map_flags:\t%i", &val) == 1)
-			map->flags = val;
-		else if (sscanf(buff, "owner_prog_type:\t%i", &val) == 1)
-			owner_type = val;
-		else if (sscanf(buff, "owner_jited:\t%i", &val) == 1)
-			owner_jited = val;
-	}
-
-	fclose(fp);
-	if (ext) {
-		memset(ext, 0, sizeof(*ext));
-		ext->owner.type  = owner_type;
-		ext->owner.jited = owner_jited;
-	}
-
-	return 0;
-}
-
-static int bpf_map_selfcheck_pinned(int fd, const struct bpf_elf_map *map,
-				    struct bpf_map_ext *ext, int length,
-				    enum bpf_prog_type type)
-{
-	struct bpf_elf_map tmp, zero = {};
-	int ret;
-
-	ret = bpf_derive_elf_map_from_fdinfo(fd, &tmp, ext);
-	if (ret < 0)
-		return ret;
-
-	/* The decision to reject this is on kernel side eventually, but
-	 * at least give the user a chance to know what's wrong.
-	 */
-	if (ext->owner.type && ext->owner.type != type)
-		fprintf(stderr, "Program array map owner types differ: %u (obj) != %u (pin)\n",
-			type, ext->owner.type);
-
-	if (!memcmp(&tmp, map, length)) {
-		return 0;
-	} else {
-		/* If kernel doesn't have eBPF-related fdinfo, we cannot do much,
-		 * so just accept it. We know we do have an eBPF fd and in this
-		 * case, everything is 0. It is guaranteed that no such map exists
-		 * since map type of 0 is unloadable BPF_MAP_TYPE_UNSPEC.
-		 */
-		if (!memcmp(&tmp, &zero, length))
-			return 0;
-
-		bpf_map_pin_report(&tmp, map);
-		return -EINVAL;
-	}
-}
-
-static int bpf_mnt_fs(const char *target)
-{
-	bool bind_done = false;
-
-	while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) {
-		if (errno != EINVAL || bind_done) {
-			fprintf(stderr, "mount --make-private %s failed: %s\n",
-				target,	strerror(errno));
-			return -1;
-		}
-
-		if (mount(target, target, "none", MS_BIND, NULL)) {
-			fprintf(stderr, "mount --bind %s %s failed: %s\n",
-				target,	target, strerror(errno));
-			return -1;
-		}
-
-		bind_done = true;
-	}
-
-	if (mount("bpf", target, "bpf", 0, "mode=0700")) {
-		fprintf(stderr, "mount -t bpf bpf %s failed: %s\n",
-			target,	strerror(errno));
-		return -1;
-	}
-
-	return 0;
-}
-
-static int bpf_mnt_check_target(const char *target)
-{
-	struct stat sb = {};
-	int ret;
-
-	ret = stat(target, &sb);
-	if (ret) {
-		ret = mkdir(target, S_IRWXU);
-		if (ret) {
-			fprintf(stderr, "mkdir %s failed: %s\n", target,
-				strerror(errno));
-			return ret;
-		}
-	}
-
-	return 0;
-}
-
-static int bpf_valid_mntpt(const char *mnt, unsigned long magic)
-{
-	struct statfs st_fs;
-
-	if (statfs(mnt, &st_fs) < 0)
-		return -ENOENT;
-	if ((unsigned long)st_fs.f_type != magic)
-		return -ENOENT;
-
-	return 0;
-}
-
-static const char *bpf_find_mntpt_single(unsigned long magic, char *mnt,
-					 int len, const char *mntpt)
-{
-	int ret;
-
-	ret = bpf_valid_mntpt(mntpt, magic);
-	if (!ret) {
-		strlcpy(mnt, mntpt, len);
-		return mnt;
-	}
-
-	return NULL;
-}
-
-static const char *bpf_find_mntpt(const char *fstype, unsigned long magic,
-				  char *mnt, int len,
-				  const char * const *known_mnts)
-{
-	const char * const *ptr;
-	char type[100];
-	FILE *fp;
-
-	if (known_mnts) {
-		ptr = known_mnts;
-		while (*ptr) {
-			if (bpf_find_mntpt_single(magic, mnt, len, *ptr))
-				return mnt;
-			ptr++;
-		}
-	}
-
-	if (len != PATH_MAX)
-		return NULL;
-
-	fp = fopen("/proc/mounts", "r");
-	if (fp == NULL)
-		return NULL;
-
-	while (fscanf(fp, "%*s %" textify(PATH_MAX) "s %99s %*s %*d %*d\n",
-		      mnt, type) == 2) {
-		if (strcmp(type, fstype) == 0)
-			break;
-	}
-
-	fclose(fp);
-	if (strcmp(type, fstype) != 0)
-		return NULL;
-
-	return mnt;
-}
-
-int bpf_trace_pipe(void)
-{
-	char tracefs_mnt[PATH_MAX] = TRACE_DIR_MNT;
-	static const char * const tracefs_known_mnts[] = {
-		TRACE_DIR_MNT,
-		"/sys/kernel/debug/tracing",
-		"/tracing",
-		"/trace",
-		0,
-	};
-	int fd_in, fd_out = STDERR_FILENO;
-	char *tpipe = NULL;
-	const char *mnt;
-	int ret;
-
-	mnt = bpf_find_mntpt("tracefs", TRACEFS_MAGIC, tracefs_mnt,
-			     sizeof(tracefs_mnt), tracefs_known_mnts);
-	if (!mnt) {
-		fprintf(stderr, "tracefs not mounted?\n");
-		return -1;
-	}
-
-	ret = asprintf(&tpipe, "%s/trace_pipe", mnt);
-	if (ret < 0) {
-		fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
-		free(tpipe);
-		return ret;
-	}
-
-	fd_in = open(tpipe, O_RDONLY);
-	free(tpipe);
-	if (fd_in < 0)
-		return -1;
-
-	fprintf(stderr, "Running! Hang up with ^C!\n\n");
-	while (1) {
-		static char buff[4096];
-		ssize_t ret;
-
-		ret = read(fd_in, buff, sizeof(buff));
-		if (ret > 0 && write(fd_out, buff, ret) == ret)
-			continue;
-		break;
-	}
-
-	close(fd_in);
-	return -1;
-}
-
-static int bpf_gen_global(const char *bpf_sub_dir)
-{
-	char *bpf_glo_dir = NULL;
-	int ret;
-
-	ret = asprintf(&bpf_glo_dir, "%s/%s/", bpf_sub_dir, BPF_DIR_GLOBALS);
-	if (ret < 0) {
-		fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
-		goto out;
-	}
-
-	ret = mkdir(bpf_glo_dir, S_IRWXU);
-	if (ret && errno != EEXIST) {
-		fprintf(stderr, "mkdir %s failed: %s\n", bpf_glo_dir,
-			strerror(errno));
-		goto out;
-	}
-
-	ret = 0;
-out:
-	free(bpf_glo_dir);
-	return ret;
-}
-
-static int bpf_gen_master(const char *base, const char *name)
-{
-	char *bpf_sub_dir = NULL;
-	int ret;
-
-	ret = asprintf(&bpf_sub_dir, "%s%s/", base, name);
-	if (ret < 0) {
-		fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
-		goto out;
-	}
-
-	ret = mkdir(bpf_sub_dir, S_IRWXU);
-	if (ret && errno != EEXIST) {
-		fprintf(stderr, "mkdir %s failed: %s\n", bpf_sub_dir,
-			strerror(errno));
-		goto out;
-	}
-
-	ret = bpf_gen_global(bpf_sub_dir);
-out:
-	free(bpf_sub_dir);
-	return ret;
-}
-
-static int bpf_slave_via_bind_mnt(const char *full_name,
-				  const char *full_link)
-{
-	int ret;
-
-	ret = mkdir(full_name, S_IRWXU);
-	if (ret) {
-		assert(errno != EEXIST);
-		fprintf(stderr, "mkdir %s failed: %s\n", full_name,
-			strerror(errno));
-		return ret;
-	}
-
-	ret = mount(full_link, full_name, "none", MS_BIND, NULL);
-	if (ret) {
-		rmdir(full_name);
-		fprintf(stderr, "mount --bind %s %s failed: %s\n",
-			full_link, full_name, strerror(errno));
-	}
-
-	return ret;
-}
-
-static int bpf_gen_slave(const char *base, const char *name,
-			 const char *link)
-{
-	char *bpf_lnk_dir = NULL;
-	char *bpf_sub_dir = NULL;
-	struct stat sb = {};
-	int ret;
-
-	ret = asprintf(&bpf_lnk_dir, "%s%s/", base, link);
-	if (ret < 0) {
-		fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
-		goto out;
-	}
-
-	ret = asprintf(&bpf_sub_dir, "%s%s",  base, name);
-	if (ret < 0) {
-		fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
-		goto out;
-	}
-
-	ret = symlink(bpf_lnk_dir, bpf_sub_dir);
-	if (ret) {
-		if (errno != EEXIST) {
-			if (errno != EPERM) {
-				fprintf(stderr, "symlink %s failed: %s\n",
-					bpf_sub_dir, strerror(errno));
-				goto out;
-			}
-
-			ret = bpf_slave_via_bind_mnt(bpf_sub_dir, bpf_lnk_dir);
-			goto out;
-		}
-
-		ret = lstat(bpf_sub_dir, &sb);
-		if (ret) {
-			fprintf(stderr, "lstat %s failed: %s\n",
-				bpf_sub_dir, strerror(errno));
-			goto out;
-		}
-
-		if ((sb.st_mode & S_IFMT) != S_IFLNK) {
-			ret = bpf_gen_global(bpf_sub_dir);
-			goto out;
-		}
-	}
-
-out:
-	free(bpf_lnk_dir);
-	free(bpf_sub_dir);
-	return ret;
-}
-
-static int bpf_gen_hierarchy(const char *base)
-{
-	int ret, i;
-
-	ret = bpf_gen_master(base, bpf_prog_to_subdir(__bpf_types[0]));
-	for (i = 1; i < ARRAY_SIZE(__bpf_types) && !ret; i++)
-		ret = bpf_gen_slave(base,
-				    bpf_prog_to_subdir(__bpf_types[i]),
-				    bpf_prog_to_subdir(__bpf_types[0]));
-	return ret;
-}
-
-static const char *bpf_get_work_dir(enum bpf_prog_type type)
-{
-	static char bpf_tmp[PATH_MAX] = BPF_DIR_MNT;
-	static char *bpf_wrk_dir;
-	static const char *mnt;
-	static bool bpf_mnt_cached;
-	const char *mnt_env = getenv(BPF_ENV_MNT);
-	static const char * const bpf_known_mnts[] = {
-		BPF_DIR_MNT,
-		"/bpf",
-		0,
-	};
-	int ret;
-
-	if (bpf_mnt_cached) {
-		const char *out = mnt;
-
-		if (out && type) {
-			snprintf(bpf_tmp, sizeof(bpf_tmp), "%s%s/",
-				 out, bpf_prog_to_subdir(type));
-			out = bpf_tmp;
-		}
-		return out;
-	}
-
-	if (mnt_env)
-		mnt = bpf_find_mntpt_single(BPF_FS_MAGIC, bpf_tmp,
-					    sizeof(bpf_tmp), mnt_env);
-	else
-		mnt = bpf_find_mntpt("bpf", BPF_FS_MAGIC, bpf_tmp,
-				     sizeof(bpf_tmp), bpf_known_mnts);
-	if (!mnt) {
-		mnt = mnt_env ? : BPF_DIR_MNT;
-		ret = bpf_mnt_check_target(mnt);
-		if (!ret)
-			ret = bpf_mnt_fs(mnt);
-		if (ret) {
-			mnt = NULL;
-			goto out;
-		}
-	}
-
-	ret = asprintf(&bpf_wrk_dir, "%s/", mnt);
-	if (ret < 0) {
-		fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
-		free(bpf_wrk_dir);
-		goto out;
-	}
-
-	ret = bpf_gen_hierarchy(bpf_wrk_dir);
-	if (ret) {
-		mnt = NULL;
-		goto out;
-	}
-
-	mnt = bpf_wrk_dir;
-out:
-	bpf_mnt_cached = true;
-	return mnt;
-}
-
-static int bpf_obj_get(const char *pathname, enum bpf_prog_type type)
-{
-	union bpf_attr attr = {};
-	char tmp[PATH_MAX];
-
-	if (strlen(pathname) > 2 && pathname[0] == 'm' &&
-	    pathname[1] == ':' && bpf_get_work_dir(type)) {
-		snprintf(tmp, sizeof(tmp), "%s/%s",
-			 bpf_get_work_dir(type), pathname + 2);
-		pathname = tmp;
-	}
-
-	attr.pathname = bpf_ptr_to_u64(pathname);
-
-	return bpf(BPF_OBJ_GET, &attr, sizeof(attr));
-}
-
-static int bpf_obj_pinned(const char *pathname, enum bpf_prog_type type)
-{
-	int prog_fd = bpf_obj_get(pathname, type);
-
-	if (prog_fd < 0)
-		fprintf(stderr, "Couldn\'t retrieve pinned program \'%s\': %s\n",
-			pathname, strerror(errno));
-	return prog_fd;
-}
-
-static int bpf_do_parse(struct bpf_cfg_in *cfg, const bool *opt_tbl)
-{
-	const char *file, *section, *uds_name;
-	bool verbose = false;
-	int i, ret, argc;
-	char **argv;
-
-	argv = cfg->argv;
-	argc = cfg->argc;
-
-	if (opt_tbl[CBPF_BYTECODE] &&
-	    (matches(*argv, "bytecode") == 0 ||
-	     strcmp(*argv, "bc") == 0)) {
-		cfg->mode = CBPF_BYTECODE;
-	} else if (opt_tbl[CBPF_FILE] &&
-		   (matches(*argv, "bytecode-file") == 0 ||
-		    strcmp(*argv, "bcf") == 0)) {
-		cfg->mode = CBPF_FILE;
-	} else if (opt_tbl[EBPF_OBJECT] &&
-		   (matches(*argv, "object-file") == 0 ||
-		    strcmp(*argv, "obj") == 0)) {
-		cfg->mode = EBPF_OBJECT;
-	} else if (opt_tbl[EBPF_PINNED] &&
-		   (matches(*argv, "object-pinned") == 0 ||
-		    matches(*argv, "pinned") == 0 ||
-		    matches(*argv, "fd") == 0)) {
-		cfg->mode = EBPF_PINNED;
-	} else {
-		fprintf(stderr, "What mode is \"%s\"?\n", *argv);
-		return -1;
-	}
-
-	NEXT_ARG();
-	file = section = uds_name = NULL;
-	if (cfg->mode == EBPF_OBJECT || cfg->mode == EBPF_PINNED) {
-		file = *argv;
-		NEXT_ARG_FWD();
-
-		if (cfg->type == BPF_PROG_TYPE_UNSPEC) {
-			if (argc > 0 && matches(*argv, "type") == 0) {
-				NEXT_ARG();
-				for (i = 0; i < ARRAY_SIZE(__bpf_prog_meta);
-				     i++) {
-					if (!__bpf_prog_meta[i].type)
-						continue;
-					if (!matches(*argv,
-						     __bpf_prog_meta[i].type)) {
-						cfg->type = i;
-						break;
-					}
-				}
-
-				if (cfg->type == BPF_PROG_TYPE_UNSPEC) {
-					fprintf(stderr, "What type is \"%s\"?\n",
-						*argv);
-					return -1;
-				}
-				NEXT_ARG_FWD();
-			} else {
-				cfg->type = BPF_PROG_TYPE_SCHED_CLS;
-			}
-		}
-
-		section = bpf_prog_to_default_section(cfg->type);
-		if (argc > 0 && matches(*argv, "section") == 0) {
-			NEXT_ARG();
-			section = *argv;
-			NEXT_ARG_FWD();
-		}
-
-		if (__bpf_prog_meta[cfg->type].may_uds_export) {
-			uds_name = getenv(BPF_ENV_UDS);
-			if (argc > 0 && !uds_name &&
-			    matches(*argv, "export") == 0) {
-				NEXT_ARG();
-				uds_name = *argv;
-				NEXT_ARG_FWD();
-			}
-		}
-
-		if (argc > 0 && matches(*argv, "verbose") == 0) {
-			verbose = true;
-			NEXT_ARG_FWD();
-		}
-
-		PREV_ARG();
-	}
-
-	if (cfg->mode == CBPF_BYTECODE || cfg->mode == CBPF_FILE) {
-		ret = bpf_ops_parse(argc, argv, cfg->opcodes,
-				    cfg->mode == CBPF_FILE);
-		cfg->n_opcodes = ret;
-	} else if (cfg->mode == EBPF_OBJECT) {
-		ret = 0; /* program will be loaded by load stage */
-	} else if (cfg->mode == EBPF_PINNED) {
-		ret = bpf_obj_pinned(file, cfg->type);
-		cfg->prog_fd = ret;
-	} else {
-		return -1;
-	}
-
-	cfg->object  = file;
-	cfg->section = section;
-	cfg->uds     = uds_name;
-	cfg->argc    = argc;
-	cfg->argv    = argv;
-	cfg->verbose = verbose;
-
-	return ret;
-}
-
-static int bpf_do_load(struct bpf_cfg_in *cfg)
-{
-	if (cfg->mode == EBPF_OBJECT) {
-		cfg->prog_fd = bpf_obj_open(cfg->object, cfg->type,
-					    cfg->section, cfg->ifindex,
-					    cfg->verbose);
-		return cfg->prog_fd;
-	}
-	return 0;
-}
-
-int bpf_load_common(struct bpf_cfg_in *cfg, const struct bpf_cfg_ops *ops,
-		    void *nl)
-{
-	char annotation[256];
-	int ret;
-
-	ret = bpf_do_load(cfg);
-	if (ret < 0)
-		return ret;
-
-	if (cfg->mode == CBPF_BYTECODE || cfg->mode == CBPF_FILE)
-		ops->cbpf_cb(nl, cfg->opcodes, cfg->n_opcodes);
-	if (cfg->mode == EBPF_OBJECT || cfg->mode == EBPF_PINNED) {
-		snprintf(annotation, sizeof(annotation), "%s:[%s]",
-			 basename(cfg->object), cfg->mode == EBPF_PINNED ?
-			 "*fsobj" : cfg->section);
-		ops->ebpf_cb(nl, cfg->prog_fd, annotation);
-	}
-
-	return 0;
-}
-
-int bpf_parse_common(struct bpf_cfg_in *cfg, const struct bpf_cfg_ops *ops)
-{
-	bool opt_tbl[BPF_MODE_MAX] = {};
-
-	if (ops->cbpf_cb) {
-		opt_tbl[CBPF_BYTECODE] = true;
-		opt_tbl[CBPF_FILE]     = true;
-	}
-
-	if (ops->ebpf_cb) {
-		opt_tbl[EBPF_OBJECT]   = true;
-		opt_tbl[EBPF_PINNED]   = true;
-	}
-
-	return bpf_do_parse(cfg, opt_tbl);
-}
-
-int bpf_parse_and_load_common(struct bpf_cfg_in *cfg,
-			      const struct bpf_cfg_ops *ops, void *nl)
-{
-	int ret;
-
-	ret = bpf_parse_common(cfg, ops);
-	if (ret < 0)
-		return ret;
-
-	return bpf_load_common(cfg, ops, nl);
-}
-
-int bpf_graft_map(const char *map_path, uint32_t *key, int argc, char **argv)
-{
-	const bool opt_tbl[BPF_MODE_MAX] = {
-		[EBPF_OBJECT]	= true,
-		[EBPF_PINNED]	= true,
-	};
-	const struct bpf_elf_map test = {
-		.type		= BPF_MAP_TYPE_PROG_ARRAY,
-		.size_key	= sizeof(int),
-		.size_value	= sizeof(int),
-	};
-	struct bpf_cfg_in cfg = {
-		.type		= BPF_PROG_TYPE_UNSPEC,
-		.argc		= argc,
-		.argv		= argv,
-	};
-	struct bpf_map_ext ext = {};
-	int ret, prog_fd, map_fd;
-	uint32_t map_key;
-
-	ret = bpf_do_parse(&cfg, opt_tbl);
-	if (ret < 0)
-		return ret;
-
-	ret = bpf_do_load(&cfg);
-	if (ret < 0)
-		return ret;
-
-	prog_fd = cfg.prog_fd;
-
-	if (key) {
-		map_key = *key;
-	} else {
-		ret = sscanf(cfg.section, "%*i/%i", &map_key);
-		if (ret != 1) {
-			fprintf(stderr, "Couldn\'t infer map key from section name! Please provide \'key\' argument!\n");
-			ret = -EINVAL;
-			goto out_prog;
-		}
-	}
-
-	map_fd = bpf_obj_get(map_path, cfg.type);
-	if (map_fd < 0) {
-		fprintf(stderr, "Couldn\'t retrieve pinned map \'%s\': %s\n",
-			map_path, strerror(errno));
-		ret = map_fd;
-		goto out_prog;
-	}
-
-	ret = bpf_map_selfcheck_pinned(map_fd, &test, &ext,
-				       offsetof(struct bpf_elf_map, max_elem),
-				       cfg.type);
-	if (ret < 0) {
-		fprintf(stderr, "Map \'%s\' self-check failed!\n", map_path);
-		goto out_map;
-	}
-
-	ret = bpf_map_update(map_fd, &map_key, &prog_fd, BPF_ANY);
-	if (ret < 0)
-		fprintf(stderr, "Map update failed: %s\n", strerror(errno));
-out_map:
-	close(map_fd);
-out_prog:
-	close(prog_fd);
-	return ret;
-}
-
-int bpf_prog_attach_fd(int prog_fd, int target_fd, enum bpf_attach_type type)
-{
-	union bpf_attr attr = {};
-
-	attr.target_fd = target_fd;
-	attr.attach_bpf_fd = prog_fd;
-	attr.attach_type = type;
-
-	return bpf(BPF_PROG_ATTACH, &attr, sizeof(attr));
-}
-
-int bpf_prog_detach_fd(int target_fd, enum bpf_attach_type type)
-{
-	union bpf_attr attr = {};
-
-	attr.target_fd = target_fd;
-	attr.attach_type = type;
-
-	return bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
-}
-
-static int bpf_prog_load_dev(enum bpf_prog_type type,
-			     const struct bpf_insn *insns, size_t size_insns,
-			     const char *license, __u32 ifindex,
-			     char *log, size_t size_log)
-{
-	union bpf_attr attr = {};
-
-	attr.prog_type = type;
-	attr.insns = bpf_ptr_to_u64(insns);
-	attr.insn_cnt = size_insns / sizeof(struct bpf_insn);
-	attr.license = bpf_ptr_to_u64(license);
-	attr.prog_ifindex = ifindex;
-
-	if (size_log > 0) {
-		attr.log_buf = bpf_ptr_to_u64(log);
-		attr.log_size = size_log;
-		attr.log_level = 1;
-	}
-
-	return bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
-}
-
-int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns,
-		  size_t size_insns, const char *license, char *log,
-		  size_t size_log)
-{
-	return bpf_prog_load_dev(type, insns, size_insns, license, 0,
-				 log, size_log);
-}
-
-#ifdef HAVE_ELF
-struct bpf_elf_prog {
-	enum bpf_prog_type	type;
-	struct bpf_insn		*insns;
-	unsigned int		insns_num;
-	size_t			size;
-	const char		*license;
-};
-
-struct bpf_hash_entry {
-	unsigned int		pinning;
-	const char		*subpath;
-	struct bpf_hash_entry	*next;
-};
-
-struct bpf_config {
-	unsigned int		jit_enabled;
-};
-
-struct bpf_btf {
-	const struct btf_header	*hdr;
-	const void		*raw;
-	const char		*strings;
-	const struct btf_type	**types;
-	int			types_num;
-};
-
-struct bpf_elf_ctx {
-	struct bpf_config	cfg;
-	Elf			*elf_fd;
-	GElf_Ehdr		elf_hdr;
-	Elf_Data		*sym_tab;
-	Elf_Data		*str_tab;
-	Elf_Data		*btf_data;
-	char			obj_uid[64];
-	int			obj_fd;
-	int			btf_fd;
-	int			map_fds[ELF_MAX_MAPS];
-	struct bpf_elf_map	maps[ELF_MAX_MAPS];
-	struct bpf_map_ext	maps_ext[ELF_MAX_MAPS];
-	struct bpf_elf_prog	prog_text;
-	struct bpf_btf		btf;
-	int			sym_num;
-	int			map_num;
-	int			map_len;
-	bool			*sec_done;
-	int			sec_maps;
-	int			sec_text;
-	int			sec_btf;
-	char			license[ELF_MAX_LICENSE_LEN];
-	enum bpf_prog_type	type;
-	__u32			ifindex;
-	bool			verbose;
-	bool			noafalg;
-	struct bpf_elf_st	stat;
-	struct bpf_hash_entry	*ht[256];
-	char			*log;
-	size_t			log_size;
-};
-
-struct bpf_elf_sec_data {
-	GElf_Shdr		sec_hdr;
-	Elf_Data		*sec_data;
-	const char		*sec_name;
-};
-
-struct bpf_map_data {
-	int			*fds;
-	const char		*obj;
-	struct bpf_elf_st	*st;
-	struct bpf_elf_map	*ent;
-};
-
-static bool bpf_log_has_data(struct bpf_elf_ctx *ctx)
-{
-	return ctx->log && ctx->log[0];
-}
-
-static __check_format_string(2, 3) void
-bpf_dump_error(struct bpf_elf_ctx *ctx, const char *format, ...)
-{
-	va_list vl;
-
-	va_start(vl, format);
-	vfprintf(stderr, format, vl);
-	va_end(vl);
-
-	if (bpf_log_has_data(ctx)) {
-		if (ctx->verbose) {
-			fprintf(stderr, "%s\n", ctx->log);
-		} else {
-			unsigned int off = 0, len = strlen(ctx->log);
-
-			if (len > BPF_MAX_LOG) {
-				off = len - BPF_MAX_LOG;
-				fprintf(stderr, "Skipped %u bytes, use \'verb\' option for the full verbose log.\n[...]\n",
-					off);
-			}
-			fprintf(stderr, "%s\n", ctx->log + off);
-		}
-
-		memset(ctx->log, 0, ctx->log_size);
-	}
-}
-
-static int bpf_log_realloc(struct bpf_elf_ctx *ctx)
-{
-	const size_t log_max = UINT_MAX >> 8;
-	size_t log_size = ctx->log_size;
-	char *ptr;
-
-	if (!ctx->log) {
-		log_size = 65536;
-	} else if (log_size < log_max) {
-		log_size <<= 1;
-		if (log_size > log_max)
-			log_size = log_max;
-	} else {
-		return -EINVAL;
-	}
-
-	ptr = realloc(ctx->log, log_size);
-	if (!ptr)
-		return -ENOMEM;
-
-	ptr[0] = 0;
-	ctx->log = ptr;
-	ctx->log_size = log_size;
-
-	return 0;
-}
-
-static int bpf_map_create(enum bpf_map_type type, uint32_t size_key,
-			  uint32_t size_value, uint32_t max_elem,
-			  uint32_t flags, int inner_fd, int btf_fd,
-			  uint32_t ifindex, uint32_t btf_id_key,
-			  uint32_t btf_id_val)
-{
-	union bpf_attr attr = {};
-
-	attr.map_type = type;
-	attr.key_size = size_key;
-	attr.value_size = inner_fd ? sizeof(int) : size_value;
-	attr.max_entries = max_elem;
-	attr.map_flags = flags;
-	attr.inner_map_fd = inner_fd;
-	attr.map_ifindex = ifindex;
-	attr.btf_fd = btf_fd;
-	attr.btf_key_type_id = btf_id_key;
-	attr.btf_value_type_id = btf_id_val;
-
-	return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
-}
-
-static int bpf_btf_load(void *btf, size_t size_btf,
-			char *log, size_t size_log)
-{
-	union bpf_attr attr = {};
-
-	attr.btf = bpf_ptr_to_u64(btf);
-	attr.btf_size = size_btf;
-
-	if (size_log > 0) {
-		attr.btf_log_buf = bpf_ptr_to_u64(log);
-		attr.btf_log_size = size_log;
-		attr.btf_log_level = 1;
-	}
-
-	return bpf(BPF_BTF_LOAD, &attr, sizeof(attr));
-}
-
-static int bpf_obj_pin(int fd, const char *pathname)
-{
-	union bpf_attr attr = {};
-
-	attr.pathname = bpf_ptr_to_u64(pathname);
-	attr.bpf_fd = fd;
-
-	return bpf(BPF_OBJ_PIN, &attr, sizeof(attr));
-}
-
-static int bpf_obj_hash(const char *object, uint8_t *out, size_t len)
-{
-	struct sockaddr_alg alg = {
-		.salg_family	= AF_ALG,
-		.salg_type	= "hash",
-		.salg_name	= "sha1",
-	};
-	int ret, cfd, ofd, ffd;
-	struct stat stbuff;
-	ssize_t size;
-
-	if (!object || len != 20)
-		return -EINVAL;
-
-	cfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
-	if (cfd < 0)
-		return cfd;
-
-	ret = bind(cfd, (struct sockaddr *)&alg, sizeof(alg));
-	if (ret < 0)
-		goto out_cfd;
-
-	ofd = accept(cfd, NULL, 0);
-	if (ofd < 0) {
-		ret = ofd;
-		goto out_cfd;
-	}
-
-	ffd = open(object, O_RDONLY);
-	if (ffd < 0) {
-		fprintf(stderr, "Error opening object %s: %s\n",
-			object, strerror(errno));
-		ret = ffd;
-		goto out_ofd;
-	}
-
-	ret = fstat(ffd, &stbuff);
-	if (ret < 0) {
-		fprintf(stderr, "Error doing fstat: %s\n",
-			strerror(errno));
-		goto out_ffd;
-	}
-
-	size = sendfile(ofd, ffd, NULL, stbuff.st_size);
-	if (size != stbuff.st_size) {
-		fprintf(stderr, "Error from sendfile (%zd vs %zu bytes): %s\n",
-			size, stbuff.st_size, strerror(errno));
-		ret = -1;
-		goto out_ffd;
-	}
-
-	size = read(ofd, out, len);
-	if (size != len) {
-		fprintf(stderr, "Error from read (%zd vs %zu bytes): %s\n",
-			size, len, strerror(errno));
-		ret = -1;
-	} else {
-		ret = 0;
-	}
-out_ffd:
-	close(ffd);
-out_ofd:
-	close(ofd);
-out_cfd:
-	close(cfd);
-	return ret;
-}
-
-static void bpf_init_env(void)
-{
-	struct rlimit limit = {
-		.rlim_cur = RLIM_INFINITY,
-		.rlim_max = RLIM_INFINITY,
-	};
-
-	/* Don't bother in case we fail! */
-	setrlimit(RLIMIT_MEMLOCK, &limit);
-
-	if (!bpf_get_work_dir(BPF_PROG_TYPE_UNSPEC))
-		fprintf(stderr, "Continuing without mounted eBPF fs. Too old kernel?\n");
-}
-
-static const char *bpf_custom_pinning(const struct bpf_elf_ctx *ctx,
-				      uint32_t pinning)
-{
-	struct bpf_hash_entry *entry;
-
-	entry = ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)];
-	while (entry && entry->pinning != pinning)
-		entry = entry->next;
-
-	return entry ? entry->subpath : NULL;
-}
-
-static bool bpf_no_pinning(const struct bpf_elf_ctx *ctx,
-			   uint32_t pinning)
-{
-	switch (pinning) {
-	case PIN_OBJECT_NS:
-	case PIN_GLOBAL_NS:
-		return false;
-	case PIN_NONE:
-		return true;
-	default:
-		return !bpf_custom_pinning(ctx, pinning);
-	}
-}
-
-static void bpf_make_pathname(char *pathname, size_t len, const char *name,
-			      const struct bpf_elf_ctx *ctx, uint32_t pinning)
-{
-	switch (pinning) {
-	case PIN_OBJECT_NS:
-		snprintf(pathname, len, "%s/%s/%s",
-			 bpf_get_work_dir(ctx->type),
-			 ctx->obj_uid, name);
-		break;
-	case PIN_GLOBAL_NS:
-		snprintf(pathname, len, "%s/%s/%s",
-			 bpf_get_work_dir(ctx->type),
-			 BPF_DIR_GLOBALS, name);
-		break;
-	default:
-		snprintf(pathname, len, "%s/../%s/%s",
-			 bpf_get_work_dir(ctx->type),
-			 bpf_custom_pinning(ctx, pinning), name);
-		break;
-	}
-}
-
-static int bpf_probe_pinned(const char *name, const struct bpf_elf_ctx *ctx,
-			    uint32_t pinning)
-{
-	char pathname[PATH_MAX];
-
-	if (bpf_no_pinning(ctx, pinning) || !bpf_get_work_dir(ctx->type))
-		return 0;
-
-	bpf_make_pathname(pathname, sizeof(pathname), name, ctx, pinning);
-	return bpf_obj_get(pathname, ctx->type);
-}
-
-static int bpf_make_obj_path(const struct bpf_elf_ctx *ctx)
-{
-	char *tmp = NULL;
-	int ret;
-
-	ret = asprintf(&tmp, "%s/%s", bpf_get_work_dir(ctx->type), ctx->obj_uid);
-	if (ret < 0) {
-		fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
-		goto out;
-	}
-
-	ret = mkdir(tmp, S_IRWXU);
-	if (ret && errno != EEXIST) {
-		fprintf(stderr, "mkdir %s failed: %s\n", tmp, strerror(errno));
-		goto out;
-	}
-
-	ret = 0;
-out:
-	free(tmp);
-	return ret;
-}
-
-static int bpf_make_custom_path(const struct bpf_elf_ctx *ctx,
-				const char *todo)
-{
-	char *tmp = NULL;
-	char *rem = NULL;
-	char *sub;
-	int ret;
-
-	ret = asprintf(&tmp, "%s/../", bpf_get_work_dir(ctx->type));
-	if (ret < 0) {
-		fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
-		goto out;
-	}
-
-	ret = asprintf(&rem, "%s/", todo);
-	if (ret < 0) {
-		fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
-		goto out;
-	}
-
-	sub = strtok(rem, "/");
-	while (sub) {
-		if (strlen(tmp) + strlen(sub) + 2 > PATH_MAX)
-			return -EINVAL;
-
-		strcat(tmp, sub);
-		strcat(tmp, "/");
-
-		ret = mkdir(tmp, S_IRWXU);
-		if (ret && errno != EEXIST) {
-			fprintf(stderr, "mkdir %s failed: %s\n", tmp,
-				strerror(errno));
-			goto out;
-		}
-
-		sub = strtok(NULL, "/");
-	}
-
-	ret = 0;
-out:
-	free(rem);
-	free(tmp);
-	return ret;
-}
-
-static int bpf_place_pinned(int fd, const char *name,
-			    const struct bpf_elf_ctx *ctx, uint32_t pinning)
-{
-	char pathname[PATH_MAX];
-	const char *tmp;
-	int ret = 0;
-
-	if (bpf_no_pinning(ctx, pinning) || !bpf_get_work_dir(ctx->type))
-		return 0;
-
-	if (pinning == PIN_OBJECT_NS)
-		ret = bpf_make_obj_path(ctx);
-	else if ((tmp = bpf_custom_pinning(ctx, pinning)))
-		ret = bpf_make_custom_path(ctx, tmp);
-	if (ret < 0)
-		return ret;
-
-	bpf_make_pathname(pathname, sizeof(pathname), name, ctx, pinning);
-	return bpf_obj_pin(fd, pathname);
-}
-
-static void bpf_prog_report(int fd, const char *section,
-			    const struct bpf_elf_prog *prog,
-			    struct bpf_elf_ctx *ctx)
-{
-	unsigned int insns = prog->size / sizeof(struct bpf_insn);
-
-	fprintf(stderr, "\nProg section \'%s\' %s%s (%d)!\n", section,
-		fd < 0 ? "rejected: " : "loaded",
-		fd < 0 ? strerror(errno) : "",
-		fd < 0 ? errno : fd);
-
-	fprintf(stderr, " - Type:         %u\n", prog->type);
-	fprintf(stderr, " - Instructions: %u (%u over limit)\n",
-		insns, insns > BPF_MAXINSNS ? insns - BPF_MAXINSNS : 0);
-	fprintf(stderr, " - License:      %s\n\n", prog->license);
-
-	bpf_dump_error(ctx, "Verifier analysis:\n\n");
-}
-
-static int bpf_prog_attach(const char *section,
-			   const struct bpf_elf_prog *prog,
-			   struct bpf_elf_ctx *ctx)
-{
-	int tries = 0, fd;
-retry:
-	errno = 0;
-	fd = bpf_prog_load_dev(prog->type, prog->insns, prog->size,
-			       prog->license, ctx->ifindex,
-			       ctx->log, ctx->log_size);
-	if (fd < 0 || ctx->verbose) {
-		/* The verifier log is pretty chatty, sometimes so chatty
-		 * on larger programs, that we could fail to dump everything
-		 * into our buffer. Still, try to give a debuggable error
-		 * log for the user, so enlarge it and re-fail.
-		 */
-		if (fd < 0 && (errno == ENOSPC || !ctx->log_size)) {
-			if (tries++ < 10 && !bpf_log_realloc(ctx))
-				goto retry;
-
-			fprintf(stderr, "Log buffer too small to dump verifier log %zu bytes (%d tries)!\n",
-				ctx->log_size, tries);
-			return fd;
-		}
-
-		bpf_prog_report(fd, section, prog, ctx);
-	}
-
-	return fd;
-}
-
-static void bpf_map_report(int fd, const char *name,
-			   const struct bpf_elf_map *map,
-			   struct bpf_elf_ctx *ctx, int inner_fd)
-{
-	fprintf(stderr, "Map object \'%s\' %s%s (%d)!\n", name,
-		fd < 0 ? "rejected: " : "loaded",
-		fd < 0 ? strerror(errno) : "",
-		fd < 0 ? errno : fd);
-
-	fprintf(stderr, " - Type:         %u\n", map->type);
-	fprintf(stderr, " - Identifier:   %u\n", map->id);
-	fprintf(stderr, " - Pinning:      %u\n", map->pinning);
-	fprintf(stderr, " - Size key:     %u\n", map->size_key);
-	fprintf(stderr, " - Size value:   %u\n",
-		inner_fd ? (int)sizeof(int) : map->size_value);
-	fprintf(stderr, " - Max elems:    %u\n", map->max_elem);
-	fprintf(stderr, " - Flags:        %#x\n\n", map->flags);
-}
-
-static int bpf_find_map_id(const struct bpf_elf_ctx *ctx, uint32_t id)
-{
-	int i;
-
-	for (i = 0; i < ctx->map_num; i++) {
-		if (ctx->maps[i].id != id)
-			continue;
-		if (ctx->map_fds[i] < 0)
-			return -EINVAL;
-
-		return ctx->map_fds[i];
-	}
-
-	return -ENOENT;
-}
-
-static void bpf_report_map_in_map(int outer_fd, uint32_t idx)
-{
-	struct bpf_elf_map outer_map;
-	int ret;
-
-	fprintf(stderr, "Cannot insert map into map! ");
-
-	ret = bpf_derive_elf_map_from_fdinfo(outer_fd, &outer_map, NULL);
-	if (!ret) {
-		if (idx >= outer_map.max_elem &&
-		    outer_map.type == BPF_MAP_TYPE_ARRAY_OF_MAPS) {
-			fprintf(stderr, "Outer map has %u elements, index %u is invalid!\n",
-				outer_map.max_elem, idx);
-			return;
-		}
-	}
-
-	fprintf(stderr, "Different map specs used for outer and inner map?\n");
-}
-
-static bool bpf_is_map_in_map_type(const struct bpf_elf_map *map)
-{
-	return map->type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
-	       map->type == BPF_MAP_TYPE_HASH_OF_MAPS;
-}
-
-static bool bpf_map_offload_neutral(enum bpf_map_type type)
-{
-	return type == BPF_MAP_TYPE_PERF_EVENT_ARRAY;
-}
-
-static int bpf_map_attach(const char *name, struct bpf_elf_ctx *ctx,
-			  const struct bpf_elf_map *map, struct bpf_map_ext *ext,
-			  int *have_map_in_map)
-{
-	int fd, ifindex, ret, map_inner_fd = 0;
-	bool retried = false;
-
-probe:
-	fd = bpf_probe_pinned(name, ctx, map->pinning);
-	if (fd > 0) {
-		ret = bpf_map_selfcheck_pinned(fd, map, ext,
-					       offsetof(struct bpf_elf_map,
-							id), ctx->type);
-		if (ret < 0) {
-			close(fd);
-			fprintf(stderr, "Map \'%s\' self-check failed!\n",
-				name);
-			return ret;
-		}
-		if (ctx->verbose)
-			fprintf(stderr, "Map \'%s\' loaded as pinned!\n",
-				name);
-		return fd;
-	}
-
-	if (have_map_in_map && bpf_is_map_in_map_type(map)) {
-		(*have_map_in_map)++;
-		if (map->inner_id)
-			return 0;
-		fprintf(stderr, "Map \'%s\' cannot be created since no inner map ID defined!\n",
-			name);
-		return -EINVAL;
-	}
-
-	if (!have_map_in_map && bpf_is_map_in_map_type(map)) {
-		map_inner_fd = bpf_find_map_id(ctx, map->inner_id);
-		if (map_inner_fd < 0) {
-			fprintf(stderr, "Map \'%s\' cannot be loaded. Inner map with ID %u not found!\n",
-				name, map->inner_id);
-			return -EINVAL;
-		}
-	}
-
-	ifindex = bpf_map_offload_neutral(map->type) ? 0 : ctx->ifindex;
-	errno = 0;
-	fd = bpf_map_create(map->type, map->size_key, map->size_value,
-			    map->max_elem, map->flags, map_inner_fd, ctx->btf_fd,
-			    ifindex, ext->btf_id_key, ext->btf_id_val);
-
-	if (fd < 0 || ctx->verbose) {
-		bpf_map_report(fd, name, map, ctx, map_inner_fd);
-		if (fd < 0)
-			return fd;
-	}
-
-	ret = bpf_place_pinned(fd, name, ctx, map->pinning);
-	if (ret < 0) {
-		close(fd);
-		if (!retried && errno == EEXIST) {
-			retried = true;
-			goto probe;
-		}
-		fprintf(stderr, "Could not pin %s map: %s\n", name,
-			strerror(errno));
-		return ret;
-	}
-
-	return fd;
-}
-
-static const char *bpf_str_tab_name(const struct bpf_elf_ctx *ctx,
-				    const GElf_Sym *sym)
-{
-	return ctx->str_tab->d_buf + sym->st_name;
-}
-
-static int bpf_btf_find(struct bpf_elf_ctx *ctx, const char *name)
-{
-	const struct btf_type *type;
-	const char *res;
-	int id;
-
-	for (id = 1; id < ctx->btf.types_num; id++) {
-		type = ctx->btf.types[id];
-		if (type->name_off >= ctx->btf.hdr->str_len)
-			continue;
-		res = &ctx->btf.strings[type->name_off];
-		if (!strcmp(res, name))
-			return id;
-	}
-
-	return -ENOENT;
-}
-
-static int bpf_btf_find_kv(struct bpf_elf_ctx *ctx, const struct bpf_elf_map *map,
-			   const char *name, uint32_t *id_key, uint32_t *id_val)
-{
-	const struct btf_member *key, *val;
-	const struct btf_type *type;
-	char btf_name[512];
-	const char *res;
-	int id;
-
-	snprintf(btf_name, sizeof(btf_name), "____btf_map_%s", name);
-	id = bpf_btf_find(ctx, btf_name);
-	if (id < 0)
-		return id;
-
-	type = ctx->btf.types[id];
-	if (BTF_INFO_KIND(type->info) != BTF_KIND_STRUCT)
-		return -EINVAL;
-	if (BTF_INFO_VLEN(type->info) != 2)
-		return -EINVAL;
-
-	key = ((void *) type) + sizeof(*type);
-	val = key + 1;
-	if (!key->type || key->type >= ctx->btf.types_num ||
-	    !val->type || val->type >= ctx->btf.types_num)
-		return -EINVAL;
-
-	if (key->name_off >= ctx->btf.hdr->str_len ||
-	    val->name_off >= ctx->btf.hdr->str_len)
-		return -EINVAL;
-
-	res = &ctx->btf.strings[key->name_off];
-	if (strcmp(res, "key"))
-		return -EINVAL;
-
-	res = &ctx->btf.strings[val->name_off];
-	if (strcmp(res, "value"))
-		return -EINVAL;
-
-	*id_key = key->type;
-	*id_val = val->type;
-	return 0;
-}
-
-static void bpf_btf_annotate(struct bpf_elf_ctx *ctx, int which, const char *name)
-{
-	uint32_t id_key = 0, id_val = 0;
-
-	if (!bpf_btf_find_kv(ctx, &ctx->maps[which], name, &id_key, &id_val)) {
-		ctx->maps_ext[which].btf_id_key = id_key;
-		ctx->maps_ext[which].btf_id_val = id_val;
-	}
-}
-
-static const char *bpf_map_fetch_name(struct bpf_elf_ctx *ctx, int which)
-{
-	const char *name;
-	GElf_Sym sym;
-	int i;
-
-	for (i = 0; i < ctx->sym_num; i++) {
-		int type;
-
-		if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
-			continue;
-
-		type = GELF_ST_TYPE(sym.st_info);
-		if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
-		    (type != STT_NOTYPE && type != STT_OBJECT) ||
-		    sym.st_shndx != ctx->sec_maps ||
-		    sym.st_value / ctx->map_len != which)
-			continue;
-
-		name = bpf_str_tab_name(ctx, &sym);
-		bpf_btf_annotate(ctx, which, name);
-		return name;
-	}
-
-	return NULL;
-}
-
-static int bpf_maps_attach_all(struct bpf_elf_ctx *ctx)
-{
-	int i, j, ret, fd, inner_fd, inner_idx, have_map_in_map = 0;
-	const char *map_name;
-
-	for (i = 0; i < ctx->map_num; i++) {
-		if (ctx->maps[i].pinning == PIN_OBJECT_NS &&
-		    ctx->noafalg) {
-			fprintf(stderr, "Missing kernel AF_ALG support for PIN_OBJECT_NS!\n");
-			return -ENOTSUP;
-		}
-
-		map_name = bpf_map_fetch_name(ctx, i);
-		if (!map_name)
-			return -EIO;
-
-		fd = bpf_map_attach(map_name, ctx, &ctx->maps[i],
-				    &ctx->maps_ext[i], &have_map_in_map);
-		if (fd < 0)
-			return fd;
-
-		ctx->map_fds[i] = !fd ? -1 : fd;
-	}
-
-	for (i = 0; have_map_in_map && i < ctx->map_num; i++) {
-		if (ctx->map_fds[i] >= 0)
-			continue;
-
-		map_name = bpf_map_fetch_name(ctx, i);
-		if (!map_name)
-			return -EIO;
-
-		fd = bpf_map_attach(map_name, ctx, &ctx->maps[i],
-				    &ctx->maps_ext[i], NULL);
-		if (fd < 0)
-			return fd;
-
-		ctx->map_fds[i] = fd;
-	}
-
-	for (i = 0; have_map_in_map && i < ctx->map_num; i++) {
-		if (!ctx->maps[i].id ||
-		    ctx->maps[i].inner_id ||
-		    ctx->maps[i].inner_idx == -1)
-			continue;
-
-		inner_fd  = ctx->map_fds[i];
-		inner_idx = ctx->maps[i].inner_idx;
-
-		for (j = 0; j < ctx->map_num; j++) {
-			if (!bpf_is_map_in_map_type(&ctx->maps[j]))
-				continue;
-			if (ctx->maps[j].inner_id != ctx->maps[i].id)
-				continue;
-
-			ret = bpf_map_update(ctx->map_fds[j], &inner_idx,
-					     &inner_fd, BPF_ANY);
-			if (ret < 0) {
-				bpf_report_map_in_map(ctx->map_fds[j],
-						      inner_idx);
-				return ret;
-			}
-		}
-	}
-
-	return 0;
-}
-
-static int bpf_map_num_sym(struct bpf_elf_ctx *ctx)
-{
-	int i, num = 0;
-	GElf_Sym sym;
-
-	for (i = 0; i < ctx->sym_num; i++) {
-		int type;
-
-		if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
-			continue;
-
-		type = GELF_ST_TYPE(sym.st_info);
-		if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
-		    (type != STT_NOTYPE && type != STT_OBJECT) ||
-		    sym.st_shndx != ctx->sec_maps)
-			continue;
-		num++;
-	}
-
-	return num;
-}
-
-static int bpf_fill_section_data(struct bpf_elf_ctx *ctx, int section,
-				 struct bpf_elf_sec_data *data)
-{
-	Elf_Data *sec_edata;
-	GElf_Shdr sec_hdr;
-	Elf_Scn *sec_fd;
-	char *sec_name;
-
-	memset(data, 0, sizeof(*data));
-
-	sec_fd = elf_getscn(ctx->elf_fd, section);
-	if (!sec_fd)
-		return -EINVAL;
-	if (gelf_getshdr(sec_fd, &sec_hdr) != &sec_hdr)
-		return -EIO;
-
-	sec_name = elf_strptr(ctx->elf_fd, ctx->elf_hdr.e_shstrndx,
-			      sec_hdr.sh_name);
-	if (!sec_name || !sec_hdr.sh_size)
-		return -ENOENT;
-
-	sec_edata = elf_getdata(sec_fd, NULL);
-	if (!sec_edata || elf_getdata(sec_fd, sec_edata))
-		return -EIO;
-
-	memcpy(&data->sec_hdr, &sec_hdr, sizeof(sec_hdr));
-
-	data->sec_name = sec_name;
-	data->sec_data = sec_edata;
-	return 0;
-}
-
-struct bpf_elf_map_min {
-	__u32 type;
-	__u32 size_key;
-	__u32 size_value;
-	__u32 max_elem;
-};
-
-static int bpf_fetch_maps_begin(struct bpf_elf_ctx *ctx, int section,
-				struct bpf_elf_sec_data *data)
-{
-	ctx->map_num = data->sec_data->d_size;
-	ctx->sec_maps = section;
-	ctx->sec_done[section] = true;
-
-	if (ctx->map_num > sizeof(ctx->maps)) {
-		fprintf(stderr, "Too many BPF maps in ELF section!\n");
-		return -ENOMEM;
-	}
-
-	memcpy(ctx->maps, data->sec_data->d_buf, ctx->map_num);
-	return 0;
-}
-
-static int bpf_map_verify_all_offs(struct bpf_elf_ctx *ctx, int end)
-{
-	GElf_Sym sym;
-	int off, i;
-
-	for (off = 0; off < end; off += ctx->map_len) {
-		/* Order doesn't need to be linear here, hence we walk
-		 * the table again.
-		 */
-		for (i = 0; i < ctx->sym_num; i++) {
-			int type;
-
-			if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
-				continue;
-
-			type = GELF_ST_TYPE(sym.st_info);
-			if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
-			    (type != STT_NOTYPE && type != STT_OBJECT) ||
-			    sym.st_shndx != ctx->sec_maps)
-				continue;
-			if (sym.st_value == off)
-				break;
-			if (i == ctx->sym_num - 1)
-				return -1;
-		}
-	}
-
-	return off == end ? 0 : -1;
-}
-
-static int bpf_fetch_maps_end(struct bpf_elf_ctx *ctx)
-{
-	struct bpf_elf_map fixup[ARRAY_SIZE(ctx->maps)] = {};
-	int i, sym_num = bpf_map_num_sym(ctx);
-	__u8 *buff;
-
-	if (sym_num == 0 || sym_num > ARRAY_SIZE(ctx->maps)) {
-		fprintf(stderr, "%u maps not supported in current map section!\n",
-			sym_num);
-		return -EINVAL;
-	}
-
-	if (ctx->map_num % sym_num != 0 ||
-	    ctx->map_num % sizeof(__u32) != 0) {
-		fprintf(stderr, "Number BPF map symbols are not multiple of struct bpf_elf_map!\n");
-		return -EINVAL;
-	}
-
-	ctx->map_len = ctx->map_num / sym_num;
-	if (bpf_map_verify_all_offs(ctx, ctx->map_num)) {
-		fprintf(stderr, "Different struct bpf_elf_map in use!\n");
-		return -EINVAL;
-	}
-
-	if (ctx->map_len == sizeof(struct bpf_elf_map)) {
-		ctx->map_num = sym_num;
-		return 0;
-	} else if (ctx->map_len > sizeof(struct bpf_elf_map)) {
-		fprintf(stderr, "struct bpf_elf_map not supported, coming from future version?\n");
-		return -EINVAL;
-	} else if (ctx->map_len < sizeof(struct bpf_elf_map_min)) {
-		fprintf(stderr, "struct bpf_elf_map too small, not supported!\n");
-		return -EINVAL;
-	}
-
-	ctx->map_num = sym_num;
-	for (i = 0, buff = (void *)ctx->maps; i < ctx->map_num;
-	     i++, buff += ctx->map_len) {
-		/* The fixup leaves the rest of the members as zero, which
-		 * is fine currently, but option exist to set some other
-		 * default value as well when needed in future.
-		 */
-		memcpy(&fixup[i], buff, ctx->map_len);
-	}
-
-	memcpy(ctx->maps, fixup, sizeof(fixup));
-	if (ctx->verbose)
-		printf("%zu bytes struct bpf_elf_map fixup performed due to size mismatch!\n",
-		       sizeof(struct bpf_elf_map) - ctx->map_len);
-	return 0;
-}
-
-static int bpf_fetch_license(struct bpf_elf_ctx *ctx, int section,
-			     struct bpf_elf_sec_data *data)
-{
-	if (data->sec_data->d_size > sizeof(ctx->license))
-		return -ENOMEM;
-
-	memcpy(ctx->license, data->sec_data->d_buf, data->sec_data->d_size);
-	ctx->sec_done[section] = true;
-	return 0;
-}
-
-static int bpf_fetch_symtab(struct bpf_elf_ctx *ctx, int section,
-			    struct bpf_elf_sec_data *data)
-{
-	ctx->sym_tab = data->sec_data;
-	ctx->sym_num = data->sec_hdr.sh_size / data->sec_hdr.sh_entsize;
-	ctx->sec_done[section] = true;
-	return 0;
-}
-
-static int bpf_fetch_strtab(struct bpf_elf_ctx *ctx, int section,
-			    struct bpf_elf_sec_data *data)
-{
-	ctx->str_tab = data->sec_data;
-	ctx->sec_done[section] = true;
-	return 0;
-}
-
-static int bpf_fetch_text(struct bpf_elf_ctx *ctx, int section,
-			  struct bpf_elf_sec_data *data)
-{
-	ctx->sec_text = section;
-	ctx->sec_done[section] = true;
-	return 0;
-}
-
-static void bpf_btf_report(int fd, struct bpf_elf_ctx *ctx)
-{
-	fprintf(stderr, "\nBTF debug data section \'.BTF\' %s%s (%d)!\n",
-		fd < 0 ? "rejected: " : "loaded",
-		fd < 0 ? strerror(errno) : "",
-		fd < 0 ? errno : fd);
-
-	fprintf(stderr, " - Length:       %zu\n", ctx->btf_data->d_size);
-
-	bpf_dump_error(ctx, "Verifier analysis:\n\n");
-}
-
-static int bpf_btf_attach(struct bpf_elf_ctx *ctx)
-{
-	int tries = 0, fd;
-retry:
-	errno = 0;
-	fd = bpf_btf_load(ctx->btf_data->d_buf, ctx->btf_data->d_size,
-			  ctx->log, ctx->log_size);
-	if (fd < 0 || ctx->verbose) {
-		if (fd < 0 && (errno == ENOSPC || !ctx->log_size)) {
-			if (tries++ < 10 && !bpf_log_realloc(ctx))
-				goto retry;
-
-			fprintf(stderr, "Log buffer too small to dump verifier log %zu bytes (%d tries)!\n",
-				ctx->log_size, tries);
-			return fd;
-		}
-
-		if (bpf_log_has_data(ctx))
-			bpf_btf_report(fd, ctx);
-	}
-
-	return fd;
-}
-
-static int bpf_fetch_btf_begin(struct bpf_elf_ctx *ctx, int section,
-			       struct bpf_elf_sec_data *data)
-{
-	ctx->btf_data = data->sec_data;
-	ctx->sec_btf = section;
-	ctx->sec_done[section] = true;
-	return 0;
-}
-
-static int bpf_btf_check_header(struct bpf_elf_ctx *ctx)
-{
-	const struct btf_header *hdr = ctx->btf_data->d_buf;
-	const char *str_start, *str_end;
-	unsigned int data_len;
-
-	if (hdr->magic != BTF_MAGIC) {
-		fprintf(stderr, "Object has wrong BTF magic: %x, expected: %x!\n",
-			hdr->magic, BTF_MAGIC);
-		return -EINVAL;
-	}
-
-	if (hdr->version != BTF_VERSION) {
-		fprintf(stderr, "Object has wrong BTF version: %u, expected: %u!\n",
-			hdr->version, BTF_VERSION);
-		return -EINVAL;
-	}
-
-	if (hdr->flags) {
-		fprintf(stderr, "Object has unsupported BTF flags %x!\n",
-			hdr->flags);
-		return -EINVAL;
-	}
-
-	data_len = ctx->btf_data->d_size - sizeof(*hdr);
-	if (data_len < hdr->type_off ||
-	    data_len < hdr->str_off ||
-	    data_len < hdr->type_len + hdr->str_len ||
-	    hdr->type_off >= hdr->str_off ||
-	    hdr->type_off + hdr->type_len != hdr->str_off ||
-	    hdr->str_off + hdr->str_len != data_len ||
-	    (hdr->type_off & (sizeof(uint32_t) - 1))) {
-		fprintf(stderr, "Object has malformed BTF data!\n");
-		return -EINVAL;
-	}
-
-	ctx->btf.hdr = hdr;
-	ctx->btf.raw = hdr + 1;
-
-	str_start = ctx->btf.raw + hdr->str_off;
-	str_end = str_start + hdr->str_len;
-	if (!hdr->str_len ||
-	    hdr->str_len - 1 > BTF_MAX_NAME_OFFSET ||
-	    str_start[0] || str_end[-1]) {
-		fprintf(stderr, "Object has malformed BTF string data!\n");
-		return -EINVAL;
-	}
-
-	ctx->btf.strings = str_start;
-	return 0;
-}
-
-static int bpf_btf_register_type(struct bpf_elf_ctx *ctx,
-				 const struct btf_type *type)
-{
-	int cur = ctx->btf.types_num, num = cur + 1;
-	const struct btf_type **types;
-
-	types = realloc(ctx->btf.types, num * sizeof(type));
-	if (!types) {
-		free(ctx->btf.types);
-		ctx->btf.types = NULL;
-		ctx->btf.types_num = 0;
-		return -ENOMEM;
-	}
-
-	ctx->btf.types = types;
-	ctx->btf.types[cur] = type;
-	ctx->btf.types_num = num;
-	return 0;
-}
-
-static struct btf_type btf_type_void;
-
-static int bpf_btf_prep_type_data(struct bpf_elf_ctx *ctx)
-{
-	const void *type_cur = ctx->btf.raw + ctx->btf.hdr->type_off;
-	const void *type_end = ctx->btf.raw + ctx->btf.hdr->str_off;
-	const struct btf_type *type;
-	uint16_t var_len;
-	int ret, kind;
-
-	ret = bpf_btf_register_type(ctx, &btf_type_void);
-	if (ret < 0)
-		return ret;
-
-	while (type_cur < type_end) {
-		type = type_cur;
-		type_cur += sizeof(*type);
-
-		var_len = BTF_INFO_VLEN(type->info);
-		kind = BTF_INFO_KIND(type->info);
-
-		switch (kind) {
-		case BTF_KIND_INT:
-			type_cur += sizeof(int);
-			break;
-		case BTF_KIND_ARRAY:
-			type_cur += sizeof(struct btf_array);
-			break;
-		case BTF_KIND_STRUCT:
-		case BTF_KIND_UNION:
-			type_cur += var_len * sizeof(struct btf_member);
-			break;
-		case BTF_KIND_ENUM:
-			type_cur += var_len * sizeof(struct btf_enum);
-			break;
-		case BTF_KIND_FUNC_PROTO:
-			type_cur += var_len * sizeof(struct btf_param);
-			break;
-		case BTF_KIND_TYPEDEF:
-		case BTF_KIND_PTR:
-		case BTF_KIND_FWD:
-		case BTF_KIND_VOLATILE:
-		case BTF_KIND_CONST:
-		case BTF_KIND_RESTRICT:
-		case BTF_KIND_FUNC:
-			break;
-		default:
-			fprintf(stderr, "Object has unknown BTF type: %u!\n", kind);
-			return -EINVAL;
-		}
-
-		ret = bpf_btf_register_type(ctx, type);
-		if (ret < 0)
-			return ret;
-	}
-
-	return 0;
-}
-
-static int bpf_btf_prep_data(struct bpf_elf_ctx *ctx)
-{
-	int ret = bpf_btf_check_header(ctx);
-
-	if (!ret)
-		return bpf_btf_prep_type_data(ctx);
-	return ret;
-}
-
-static void bpf_fetch_btf_end(struct bpf_elf_ctx *ctx)
-{
-	int fd = bpf_btf_attach(ctx);
-
-	if (fd < 0)
-		return;
-	ctx->btf_fd = fd;
-	if (bpf_btf_prep_data(ctx) < 0) {
-		close(ctx->btf_fd);
-		ctx->btf_fd = 0;
-	}
-}
-
-static bool bpf_has_map_data(const struct bpf_elf_ctx *ctx)
-{
-	return ctx->sym_tab && ctx->str_tab && ctx->sec_maps;
-}
-
-static bool bpf_has_btf_data(const struct bpf_elf_ctx *ctx)
-{
-	return ctx->sec_btf;
-}
-
-static bool bpf_has_call_data(const struct bpf_elf_ctx *ctx)
-{
-	return ctx->sec_text;
-}
-
-static int bpf_fetch_ancillary(struct bpf_elf_ctx *ctx, bool check_text_sec)
-{
-	struct bpf_elf_sec_data data;
-	int i, ret = -1;
-
-	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
-		ret = bpf_fill_section_data(ctx, i, &data);
-		if (ret < 0)
-			continue;
-
-		if (data.sec_hdr.sh_type == SHT_PROGBITS &&
-		    !strcmp(data.sec_name, ELF_SECTION_MAPS))
-			ret = bpf_fetch_maps_begin(ctx, i, &data);
-		else if (data.sec_hdr.sh_type == SHT_PROGBITS &&
-			 !strcmp(data.sec_name, ELF_SECTION_LICENSE))
-			ret = bpf_fetch_license(ctx, i, &data);
-		else if (data.sec_hdr.sh_type == SHT_PROGBITS &&
-			 (data.sec_hdr.sh_flags & SHF_EXECINSTR) &&
-			 !strcmp(data.sec_name, ".text") &&
-			 check_text_sec)
-			ret = bpf_fetch_text(ctx, i, &data);
-		else if (data.sec_hdr.sh_type == SHT_SYMTAB &&
-			 !strcmp(data.sec_name, ".symtab"))
-			ret = bpf_fetch_symtab(ctx, i, &data);
-		else if (data.sec_hdr.sh_type == SHT_STRTAB &&
-			 !strcmp(data.sec_name, ".strtab"))
-			ret = bpf_fetch_strtab(ctx, i, &data);
-		else if (data.sec_hdr.sh_type == SHT_PROGBITS &&
-			 !strcmp(data.sec_name, ".BTF"))
-			ret = bpf_fetch_btf_begin(ctx, i, &data);
-		if (ret < 0) {
-			fprintf(stderr, "Error parsing section %d! Perhaps check with readelf -a?\n",
-				i);
-			return ret;
-		}
-	}
-
-	if (bpf_has_btf_data(ctx))
-		bpf_fetch_btf_end(ctx);
-	if (bpf_has_map_data(ctx)) {
-		ret = bpf_fetch_maps_end(ctx);
-		if (ret < 0) {
-			fprintf(stderr, "Error fixing up map structure, incompatible struct bpf_elf_map used?\n");
-			return ret;
-		}
-
-		ret = bpf_maps_attach_all(ctx);
-		if (ret < 0) {
-			fprintf(stderr, "Error loading maps into kernel!\n");
-			return ret;
-		}
-	}
-
-	return ret;
-}
-
-static int bpf_fetch_prog(struct bpf_elf_ctx *ctx, const char *section,
-			  bool *sseen)
-{
-	struct bpf_elf_sec_data data;
-	struct bpf_elf_prog prog;
-	int ret, i, fd = -1;
-
-	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
-		if (ctx->sec_done[i])
-			continue;
-
-		ret = bpf_fill_section_data(ctx, i, &data);
-		if (ret < 0 ||
-		    !(data.sec_hdr.sh_type == SHT_PROGBITS &&
-		      (data.sec_hdr.sh_flags & SHF_EXECINSTR) &&
-		      !strcmp(data.sec_name, section)))
-			continue;
-
-		*sseen = true;
-
-		memset(&prog, 0, sizeof(prog));
-		prog.type      = ctx->type;
-		prog.license   = ctx->license;
-		prog.size      = data.sec_data->d_size;
-		prog.insns_num = prog.size / sizeof(struct bpf_insn);
-		prog.insns     = data.sec_data->d_buf;
-
-		fd = bpf_prog_attach(section, &prog, ctx);
-		if (fd < 0)
-			return fd;
-
-		ctx->sec_done[i] = true;
-		break;
-	}
-
-	return fd;
-}
-
-struct bpf_relo_props {
-	struct bpf_tail_call {
-		unsigned int total;
-		unsigned int jited;
-	} tc;
-	int main_num;
-};
-
-static int bpf_apply_relo_map(struct bpf_elf_ctx *ctx, struct bpf_elf_prog *prog,
-			      GElf_Rel *relo, GElf_Sym *sym,
-			      struct bpf_relo_props *props)
-{
-	unsigned int insn_off = relo->r_offset / sizeof(struct bpf_insn);
-	unsigned int map_idx = sym->st_value / ctx->map_len;
-
-	if (insn_off >= prog->insns_num)
-		return -EINVAL;
-	if (prog->insns[insn_off].code != (BPF_LD | BPF_IMM | BPF_DW)) {
-		fprintf(stderr, "ELF contains relo data for non ld64 instruction at offset %u! Compiler bug?!\n",
-			insn_off);
-		return -EINVAL;
-	}
-
-	if (map_idx >= ARRAY_SIZE(ctx->map_fds))
-		return -EINVAL;
-	if (!ctx->map_fds[map_idx])
-		return -EINVAL;
-	if (ctx->maps[map_idx].type == BPF_MAP_TYPE_PROG_ARRAY) {
-		props->tc.total++;
-		if (ctx->maps_ext[map_idx].owner.jited ||
-		    (ctx->maps_ext[map_idx].owner.type == 0 &&
-		     ctx->cfg.jit_enabled))
-			props->tc.jited++;
-	}
-
-	prog->insns[insn_off].src_reg = BPF_PSEUDO_MAP_FD;
-	prog->insns[insn_off].imm = ctx->map_fds[map_idx];
-	return 0;
-}
-
-static int bpf_apply_relo_call(struct bpf_elf_ctx *ctx, struct bpf_elf_prog *prog,
-			       GElf_Rel *relo, GElf_Sym *sym,
-			       struct bpf_relo_props *props)
-{
-	unsigned int insn_off = relo->r_offset / sizeof(struct bpf_insn);
-	struct bpf_elf_prog *prog_text = &ctx->prog_text;
-
-	if (insn_off >= prog->insns_num)
-		return -EINVAL;
-	if (prog->insns[insn_off].code != (BPF_JMP | BPF_CALL) &&
-	    prog->insns[insn_off].src_reg != BPF_PSEUDO_CALL) {
-		fprintf(stderr, "ELF contains relo data for non call instruction at offset %u! Compiler bug?!\n",
-			insn_off);
-		return -EINVAL;
-	}
-
-	if (!props->main_num) {
-		struct bpf_insn *insns = realloc(prog->insns,
-						 prog->size + prog_text->size);
-		if (!insns)
-			return -ENOMEM;
-
-		memcpy(insns + prog->insns_num, prog_text->insns,
-		       prog_text->size);
-		props->main_num = prog->insns_num;
-		prog->insns = insns;
-		prog->insns_num += prog_text->insns_num;
-		prog->size += prog_text->size;
-	}
-
-	prog->insns[insn_off].imm += props->main_num - insn_off;
-	return 0;
-}
-
-static int bpf_apply_relo_data(struct bpf_elf_ctx *ctx,
-			       struct bpf_elf_sec_data *data_relo,
-			       struct bpf_elf_prog *prog,
-			       struct bpf_relo_props *props)
-{
-	GElf_Shdr *rhdr = &data_relo->sec_hdr;
-	int relo_ent, relo_num = rhdr->sh_size / rhdr->sh_entsize;
-
-	for (relo_ent = 0; relo_ent < relo_num; relo_ent++) {
-		GElf_Rel relo;
-		GElf_Sym sym;
-		int ret = -EIO;
-
-		if (gelf_getrel(data_relo->sec_data, relo_ent, &relo) != &relo)
-			return -EIO;
-		if (gelf_getsym(ctx->sym_tab, GELF_R_SYM(relo.r_info), &sym) != &sym)
-			return -EIO;
-
-		if (sym.st_shndx == ctx->sec_maps)
-			ret = bpf_apply_relo_map(ctx, prog, &relo, &sym, props);
-		else if (sym.st_shndx == ctx->sec_text)
-			ret = bpf_apply_relo_call(ctx, prog, &relo, &sym, props);
-		else
-			fprintf(stderr, "ELF contains non-{map,call} related relo data in entry %u pointing to section %u! Compiler bug?!\n",
-				relo_ent, sym.st_shndx);
-		if (ret < 0)
-			return ret;
-	}
-
-	return 0;
-}
-
-static int bpf_fetch_prog_relo(struct bpf_elf_ctx *ctx, const char *section,
-			       bool *lderr, bool *sseen, struct bpf_elf_prog *prog)
-{
-	struct bpf_elf_sec_data data_relo, data_insn;
-	int ret, idx, i, fd = -1;
-
-	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
-		struct bpf_relo_props props = {};
-
-		ret = bpf_fill_section_data(ctx, i, &data_relo);
-		if (ret < 0 || data_relo.sec_hdr.sh_type != SHT_REL)
-			continue;
-
-		idx = data_relo.sec_hdr.sh_info;
-
-		ret = bpf_fill_section_data(ctx, idx, &data_insn);
-		if (ret < 0 ||
-		    !(data_insn.sec_hdr.sh_type == SHT_PROGBITS &&
-		      (data_insn.sec_hdr.sh_flags & SHF_EXECINSTR) &&
-		      !strcmp(data_insn.sec_name, section)))
-			continue;
-		if (sseen)
-			*sseen = true;
-
-		memset(prog, 0, sizeof(*prog));
-		prog->type = ctx->type;
-		prog->license = ctx->license;
-		prog->size = data_insn.sec_data->d_size;
-		prog->insns_num = prog->size / sizeof(struct bpf_insn);
-		prog->insns = malloc(prog->size);
-		if (!prog->insns) {
-			*lderr = true;
-			return -ENOMEM;
-		}
-
-		memcpy(prog->insns, data_insn.sec_data->d_buf, prog->size);
-
-		ret = bpf_apply_relo_data(ctx, &data_relo, prog, &props);
-		if (ret < 0) {
-			*lderr = true;
-			if (ctx->sec_text != idx)
-				free(prog->insns);
-			return ret;
-		}
-		if (ctx->sec_text == idx) {
-			fd = 0;
-			goto out;
-		}
-
-		fd = bpf_prog_attach(section, prog, ctx);
-		free(prog->insns);
-		if (fd < 0) {
-			*lderr = true;
-			if (props.tc.total) {
-				if (ctx->cfg.jit_enabled &&
-				    props.tc.total != props.tc.jited)
-					fprintf(stderr, "JIT enabled, but only %u/%u tail call maps in the program have JITed owner!\n",
-						props.tc.jited, props.tc.total);
-				if (!ctx->cfg.jit_enabled &&
-				    props.tc.jited)
-					fprintf(stderr, "JIT disabled, but %u/%u tail call maps in the program have JITed owner!\n",
-						props.tc.jited, props.tc.total);
-			}
-			return fd;
-		}
-out:
-		ctx->sec_done[i]   = true;
-		ctx->sec_done[idx] = true;
-		break;
-	}
-
-	return fd;
-}
-
-static int bpf_fetch_prog_sec(struct bpf_elf_ctx *ctx, const char *section)
-{
-	bool lderr = false, sseen = false;
-	struct bpf_elf_prog prog;
-	int ret = -1;
-
-	if (bpf_has_call_data(ctx)) {
-		ret = bpf_fetch_prog_relo(ctx, ".text", &lderr, NULL,
-					  &ctx->prog_text);
-		if (ret < 0)
-			return ret;
-	}
-
-	if (bpf_has_map_data(ctx) || bpf_has_call_data(ctx))
-		ret = bpf_fetch_prog_relo(ctx, section, &lderr, &sseen, &prog);
-	if (ret < 0 && !lderr)
-		ret = bpf_fetch_prog(ctx, section, &sseen);
-	if (ret < 0 && !sseen)
-		fprintf(stderr, "Program section \'%s\' not found in ELF file!\n",
-			section);
-	return ret;
-}
-
-static int bpf_find_map_by_id(struct bpf_elf_ctx *ctx, uint32_t id)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++)
-		if (ctx->map_fds[i] && ctx->maps[i].id == id &&
-		    ctx->maps[i].type == BPF_MAP_TYPE_PROG_ARRAY)
-			return i;
-	return -1;
-}
-
-struct bpf_jited_aux {
-	int prog_fd;
-	int map_fd;
-	struct bpf_prog_data prog;
-	struct bpf_map_ext map;
-};
-
-static int bpf_derive_prog_from_fdinfo(int fd, struct bpf_prog_data *prog)
-{
-	char *file = NULL;
-	char buff[4096];
-	unsigned int val;
-	FILE *fp;
-	int ret;
-
-	ret = asprintf(&file, "/proc/%d/fdinfo/%d", getpid(), fd);
-	if (ret < 0) {
-		fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
-		free(file);
-		return ret;
-	}
-
-	memset(prog, 0, sizeof(*prog));
-
-	fp = fopen(file, "r");
-	free(file);
-	if (!fp) {
-		fprintf(stderr, "No procfs support?!\n");
-		return -EIO;
-	}
-
-	while (fgets(buff, sizeof(buff), fp)) {
-		if (sscanf(buff, "prog_type:\t%u", &val) == 1)
-			prog->type = val;
-		else if (sscanf(buff, "prog_jited:\t%u", &val) == 1)
-			prog->jited = val;
-	}
-
-	fclose(fp);
-	return 0;
-}
-
-static int bpf_tail_call_get_aux(struct bpf_jited_aux *aux)
-{
-	struct bpf_elf_map tmp;
-	int ret;
-
-	ret = bpf_derive_elf_map_from_fdinfo(aux->map_fd, &tmp, &aux->map);
-	if (!ret)
-		ret = bpf_derive_prog_from_fdinfo(aux->prog_fd, &aux->prog);
-
-	return ret;
-}
-
-static int bpf_fill_prog_arrays(struct bpf_elf_ctx *ctx)
-{
-	struct bpf_elf_sec_data data;
-	uint32_t map_id, key_id;
-	int fd, i, ret, idx;
-
-	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
-		if (ctx->sec_done[i])
-			continue;
-
-		ret = bpf_fill_section_data(ctx, i, &data);
-		if (ret < 0)
-			continue;
-
-		ret = sscanf(data.sec_name, "%i/%i", &map_id, &key_id);
-		if (ret != 2)
-			continue;
-
-		idx = bpf_find_map_by_id(ctx, map_id);
-		if (idx < 0)
-			continue;
-
-		fd = bpf_fetch_prog_sec(ctx, data.sec_name);
-		if (fd < 0)
-			return -EIO;
-
-		ret = bpf_map_update(ctx->map_fds[idx], &key_id,
-				     &fd, BPF_ANY);
-		if (ret < 0) {
-			struct bpf_jited_aux aux = {};
-
-			ret = -errno;
-			if (errno == E2BIG) {
-				fprintf(stderr, "Tail call key %u for map %u out of bounds?\n",
-					key_id, map_id);
-				return ret;
-			}
-
-			aux.map_fd  = ctx->map_fds[idx];
-			aux.prog_fd = fd;
-
-			if (bpf_tail_call_get_aux(&aux))
-				return ret;
-			if (!aux.map.owner.type)
-				return ret;
-
-			if (aux.prog.type != aux.map.owner.type)
-				fprintf(stderr, "Tail call map owned by prog type %u, but prog type is %u!\n",
-					aux.map.owner.type, aux.prog.type);
-			if (aux.prog.jited != aux.map.owner.jited)
-				fprintf(stderr, "Tail call map %s jited, but prog %s!\n",
-					aux.map.owner.jited ? "is" : "not",
-					aux.prog.jited ? "is" : "not");
-			return ret;
-		}
-
-		ctx->sec_done[i] = true;
-	}
-
-	return 0;
-}
-
-static void bpf_save_finfo(struct bpf_elf_ctx *ctx)
-{
-	struct stat st;
-	int ret;
-
-	memset(&ctx->stat, 0, sizeof(ctx->stat));
-
-	ret = fstat(ctx->obj_fd, &st);
-	if (ret < 0) {
-		fprintf(stderr, "Stat of elf file failed: %s\n",
-			strerror(errno));
-		return;
-	}
-
-	ctx->stat.st_dev = st.st_dev;
-	ctx->stat.st_ino = st.st_ino;
-}
-
-static int bpf_read_pin_mapping(FILE *fp, uint32_t *id, char *path)
-{
-	char buff[PATH_MAX];
-
-	while (fgets(buff, sizeof(buff), fp)) {
-		char *ptr = buff;
-
-		while (*ptr == ' ' || *ptr == '\t')
-			ptr++;
-
-		if (*ptr == '#' || *ptr == '\n' || *ptr == 0)
-			continue;
-
-		if (sscanf(ptr, "%i %s\n", id, path) != 2 &&
-		    sscanf(ptr, "%i %s #", id, path) != 2) {
-			strcpy(path, ptr);
-			return -1;
-		}
-
-		return 1;
-	}
-
-	return 0;
-}
-
-static bool bpf_pinning_reserved(uint32_t pinning)
-{
-	switch (pinning) {
-	case PIN_NONE:
-	case PIN_OBJECT_NS:
-	case PIN_GLOBAL_NS:
-		return true;
-	default:
-		return false;
-	}
-}
-
-static void bpf_hash_init(struct bpf_elf_ctx *ctx, const char *db_file)
-{
-	struct bpf_hash_entry *entry;
-	char subpath[PATH_MAX] = {};
-	uint32_t pinning;
-	FILE *fp;
-	int ret;
-
-	fp = fopen(db_file, "r");
-	if (!fp)
-		return;
-
-	while ((ret = bpf_read_pin_mapping(fp, &pinning, subpath))) {
-		if (ret == -1) {
-			fprintf(stderr, "Database %s is corrupted at: %s\n",
-				db_file, subpath);
-			fclose(fp);
-			return;
-		}
-
-		if (bpf_pinning_reserved(pinning)) {
-			fprintf(stderr, "Database %s, id %u is reserved - ignoring!\n",
-				db_file, pinning);
-			continue;
-		}
-
-		entry = malloc(sizeof(*entry));
-		if (!entry) {
-			fprintf(stderr, "No memory left for db entry!\n");
-			continue;
-		}
-
-		entry->pinning = pinning;
-		entry->subpath = strdup(subpath);
-		if (!entry->subpath) {
-			fprintf(stderr, "No memory left for db entry!\n");
-			free(entry);
-			continue;
-		}
-
-		entry->next = ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)];
-		ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)] = entry;
-	}
-
-	fclose(fp);
-}
-
-static void bpf_hash_destroy(struct bpf_elf_ctx *ctx)
-{
-	struct bpf_hash_entry *entry;
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(ctx->ht); i++) {
-		while ((entry = ctx->ht[i]) != NULL) {
-			ctx->ht[i] = entry->next;
-			free((char *)entry->subpath);
-			free(entry);
-		}
-	}
-}
-
-static int bpf_elf_check_ehdr(const struct bpf_elf_ctx *ctx)
-{
-	if (ctx->elf_hdr.e_type != ET_REL ||
-	    (ctx->elf_hdr.e_machine != EM_NONE &&
-	     ctx->elf_hdr.e_machine != EM_BPF) ||
-	    ctx->elf_hdr.e_version != EV_CURRENT) {
-		fprintf(stderr, "ELF format error, ELF file not for eBPF?\n");
-		return -EINVAL;
-	}
-
-	switch (ctx->elf_hdr.e_ident[EI_DATA]) {
-	default:
-		fprintf(stderr, "ELF format error, wrong endianness info?\n");
-		return -EINVAL;
-	case ELFDATA2LSB:
-		if (htons(1) == 1) {
-			fprintf(stderr,
-				"We are big endian, eBPF object is little endian!\n");
-			return -EIO;
-		}
-		break;
-	case ELFDATA2MSB:
-		if (htons(1) != 1) {
-			fprintf(stderr,
-				"We are little endian, eBPF object is big endian!\n");
-			return -EIO;
-		}
-		break;
-	}
-
-	return 0;
-}
-
-static void bpf_get_cfg(struct bpf_elf_ctx *ctx)
-{
-	static const char *path_jit = "/proc/sys/net/core/bpf_jit_enable";
-	int fd;
-
-	fd = open(path_jit, O_RDONLY);
-	if (fd > 0) {
-		char tmp[16] = {};
-
-		if (read(fd, tmp, sizeof(tmp)) > 0)
-			ctx->cfg.jit_enabled = atoi(tmp);
-		close(fd);
-	}
-}
-
-static int bpf_elf_ctx_init(struct bpf_elf_ctx *ctx, const char *pathname,
-			    enum bpf_prog_type type, __u32 ifindex,
-			    bool verbose)
-{
-	uint8_t tmp[20];
-	int ret;
-
-	if (elf_version(EV_CURRENT) == EV_NONE)
-		return -EINVAL;
-
-	bpf_init_env();
-
-	memset(ctx, 0, sizeof(*ctx));
-	bpf_get_cfg(ctx);
-
-	ret = bpf_obj_hash(pathname, tmp, sizeof(tmp));
-	if (ret)
-		ctx->noafalg = true;
-	else
-		hexstring_n2a(tmp, sizeof(tmp), ctx->obj_uid,
-			      sizeof(ctx->obj_uid));
-
-	ctx->verbose = verbose;
-	ctx->type    = type;
-	ctx->ifindex = ifindex;
-
-	ctx->obj_fd = open(pathname, O_RDONLY);
-	if (ctx->obj_fd < 0)
-		return ctx->obj_fd;
-
-	ctx->elf_fd = elf_begin(ctx->obj_fd, ELF_C_READ, NULL);
-	if (!ctx->elf_fd) {
-		ret = -EINVAL;
-		goto out_fd;
-	}
-
-	if (elf_kind(ctx->elf_fd) != ELF_K_ELF) {
-		ret = -EINVAL;
-		goto out_fd;
-	}
-
-	if (gelf_getehdr(ctx->elf_fd, &ctx->elf_hdr) !=
-	    &ctx->elf_hdr) {
-		ret = -EIO;
-		goto out_elf;
-	}
-
-	ret = bpf_elf_check_ehdr(ctx);
-	if (ret < 0)
-		goto out_elf;
-
-	ctx->sec_done = calloc(ctx->elf_hdr.e_shnum,
-			       sizeof(*(ctx->sec_done)));
-	if (!ctx->sec_done) {
-		ret = -ENOMEM;
-		goto out_elf;
-	}
-
-	if (ctx->verbose && bpf_log_realloc(ctx)) {
-		ret = -ENOMEM;
-		goto out_free;
-	}
-
-	bpf_save_finfo(ctx);
-	bpf_hash_init(ctx, CONFDIR "/bpf_pinning");
-
-	return 0;
-out_free:
-	free(ctx->sec_done);
-out_elf:
-	elf_end(ctx->elf_fd);
-out_fd:
-	close(ctx->obj_fd);
-	return ret;
-}
-
-static int bpf_maps_count(struct bpf_elf_ctx *ctx)
-{
-	int i, count = 0;
-
-	for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++) {
-		if (!ctx->map_fds[i])
-			break;
-		count++;
-	}
-
-	return count;
-}
-
-static void bpf_maps_teardown(struct bpf_elf_ctx *ctx)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++) {
-		if (ctx->map_fds[i])
-			close(ctx->map_fds[i]);
-	}
-
-	if (ctx->btf_fd)
-		close(ctx->btf_fd);
-	free(ctx->btf.types);
-}
-
-static void bpf_elf_ctx_destroy(struct bpf_elf_ctx *ctx, bool failure)
-{
-	if (failure)
-		bpf_maps_teardown(ctx);
-
-	bpf_hash_destroy(ctx);
-
-	free(ctx->prog_text.insns);
-	free(ctx->sec_done);
-	free(ctx->log);
-
-	elf_end(ctx->elf_fd);
-	close(ctx->obj_fd);
-}
-
-static struct bpf_elf_ctx __ctx;
-
-static int bpf_obj_open(const char *pathname, enum bpf_prog_type type,
-			const char *section, __u32 ifindex, bool verbose)
-{
-	struct bpf_elf_ctx *ctx = &__ctx;
-	int fd = 0, ret;
-
-	ret = bpf_elf_ctx_init(ctx, pathname, type, ifindex, verbose);
-	if (ret < 0) {
-		fprintf(stderr, "Cannot initialize ELF context!\n");
-		return ret;
-	}
-
-	ret = bpf_fetch_ancillary(ctx, strcmp(section, ".text"));
-	if (ret < 0) {
-		fprintf(stderr, "Error fetching ELF ancillary data!\n");
-		goto out;
-	}
-
-	fd = bpf_fetch_prog_sec(ctx, section);
-	if (fd < 0) {
-		fprintf(stderr, "Error fetching program/map!\n");
-		ret = fd;
-		goto out;
-	}
-
-	ret = bpf_fill_prog_arrays(ctx);
-	if (ret < 0)
-		fprintf(stderr, "Error filling program arrays!\n");
-out:
-	bpf_elf_ctx_destroy(ctx, ret < 0);
-	if (ret < 0) {
-		if (fd)
-			close(fd);
-		return ret;
-	}
-
-	return fd;
-}
-
-static int
-bpf_map_set_send(int fd, struct sockaddr_un *addr, unsigned int addr_len,
-		 const struct bpf_map_data *aux, unsigned int entries)
-{
-	struct bpf_map_set_msg msg = {
-		.aux.uds_ver = BPF_SCM_AUX_VER,
-		.aux.num_ent = entries,
-	};
-	int *cmsg_buf, min_fd;
-	char *amsg_buf;
-	int i;
-
-	strlcpy(msg.aux.obj_name, aux->obj, sizeof(msg.aux.obj_name));
-	memcpy(&msg.aux.obj_st, aux->st, sizeof(msg.aux.obj_st));
-
-	cmsg_buf = bpf_map_set_init(&msg, addr, addr_len);
-	amsg_buf = (char *)msg.aux.ent;
-
-	for (i = 0; i < entries; i += min_fd) {
-		int ret;
-
-		min_fd = min(BPF_SCM_MAX_FDS * 1U, entries - i);
-		bpf_map_set_init_single(&msg, min_fd);
-
-		memcpy(cmsg_buf, &aux->fds[i], sizeof(aux->fds[0]) * min_fd);
-		memcpy(amsg_buf, &aux->ent[i], sizeof(aux->ent[0]) * min_fd);
-
-		ret = sendmsg(fd, &msg.hdr, 0);
-		if (ret <= 0)
-			return ret ? : -1;
-	}
-
-	return 0;
-}
-
-static int
-bpf_map_set_recv(int fd, int *fds,  struct bpf_map_aux *aux,
-		 unsigned int entries)
-{
-	struct bpf_map_set_msg msg;
-	int *cmsg_buf, min_fd;
-	char *amsg_buf, *mmsg_buf;
-	unsigned int needed = 1;
-	int i;
-
-	cmsg_buf = bpf_map_set_init(&msg, NULL, 0);
-	amsg_buf = (char *)msg.aux.ent;
-	mmsg_buf = (char *)&msg.aux;
-
-	for (i = 0; i < min(entries, needed); i += min_fd) {
-		struct cmsghdr *cmsg;
-		int ret;
-
-		min_fd = min(entries, entries - i);
-		bpf_map_set_init_single(&msg, min_fd);
-
-		ret = recvmsg(fd, &msg.hdr, 0);
-		if (ret <= 0)
-			return ret ? : -1;
-
-		cmsg = CMSG_FIRSTHDR(&msg.hdr);
-		if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS)
-			return -EINVAL;
-		if (msg.hdr.msg_flags & MSG_CTRUNC)
-			return -EIO;
-		if (msg.aux.uds_ver != BPF_SCM_AUX_VER)
-			return -ENOSYS;
-
-		min_fd = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof(fd);
-		if (min_fd > entries || min_fd <= 0)
-			return -EINVAL;
-
-		memcpy(&fds[i], cmsg_buf, sizeof(fds[0]) * min_fd);
-		memcpy(&aux->ent[i], amsg_buf, sizeof(aux->ent[0]) * min_fd);
-		memcpy(aux, mmsg_buf, offsetof(struct bpf_map_aux, ent));
-
-		needed = aux->num_ent;
-	}
-
-	return 0;
-}
-
-int bpf_send_map_fds(const char *path, const char *obj)
-{
-	struct bpf_elf_ctx *ctx = &__ctx;
-	struct sockaddr_un addr = { .sun_family = AF_UNIX };
-	struct bpf_map_data bpf_aux = {
-		.fds = ctx->map_fds,
-		.ent = ctx->maps,
-		.st  = &ctx->stat,
-		.obj = obj,
-	};
-	int fd, ret;
-
-	fd = socket(AF_UNIX, SOCK_DGRAM, 0);
-	if (fd < 0) {
-		fprintf(stderr, "Cannot open socket: %s\n",
-			strerror(errno));
-		return -1;
-	}
-
-	strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
-
-	ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
-	if (ret < 0) {
-		fprintf(stderr, "Cannot connect to %s: %s\n",
-			path, strerror(errno));
-		return -1;
-	}
-
-	ret = bpf_map_set_send(fd, &addr, sizeof(addr), &bpf_aux,
-			       bpf_maps_count(ctx));
-	if (ret < 0)
-		fprintf(stderr, "Cannot send fds to %s: %s\n",
-			path, strerror(errno));
-
-	bpf_maps_teardown(ctx);
-	close(fd);
-	return ret;
-}
-
-int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
-		     unsigned int entries)
-{
-	struct sockaddr_un addr = { .sun_family = AF_UNIX };
-	int fd, ret;
-
-	fd = socket(AF_UNIX, SOCK_DGRAM, 0);
-	if (fd < 0) {
-		fprintf(stderr, "Cannot open socket: %s\n",
-			strerror(errno));
-		return -1;
-	}
-
-	strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
-
-	ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
-	if (ret < 0) {
-		fprintf(stderr, "Cannot bind to socket: %s\n",
-			strerror(errno));
-		return -1;
-	}
-
-	ret = bpf_map_set_recv(fd, fds, aux, entries);
-	if (ret < 0)
-		fprintf(stderr, "Cannot recv fds from %s: %s\n",
-			path, strerror(errno));
-
-	unlink(addr.sun_path);
-	close(fd);
-	return ret;
-}
-#endif /* HAVE_ELF */
diff --git a/lib/color.c b/lib/color.c
index 5997684..8c9a48b 100644
--- a/lib/color.c
+++ b/lib/color.c
@@ -1,17 +1,7 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 #include <stdio.h>
 #include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <linux/if.h>
 
 #include "color.h"
-#include "utils.h"
-
-static void set_color_palette(void);
 
 enum color {
 	C_RED,
@@ -21,13 +11,6 @@
 	C_MAGENTA,
 	C_CYAN,
 	C_WHITE,
-	C_BOLD_RED,
-	C_BOLD_GREEN,
-	C_BOLD_YELLOW,
-	C_BOLD_BLUE,
-	C_BOLD_MAGENTA,
-	C_BOLD_CYAN,
-	C_BOLD_WHITE,
 	C_CLEAR
 };
 
@@ -39,102 +22,26 @@
 	"\e[35m",
 	"\e[36m",
 	"\e[37m",
-	"\e[1;31m",
-	"\e[1;32m",
-	"\e[1;33m",
-	"\e[1;34m",
-	"\e[1;35m",
-	"\e[1;36m",
-	"\e[1;37m",
 	"\e[0m",
 	NULL,
 };
 
-/* light background */
-static enum color attr_colors_light[] = {
+static enum color attr_colors[] = {
 	C_CYAN,
 	C_YELLOW,
 	C_MAGENTA,
 	C_BLUE,
 	C_GREEN,
-	C_RED,
-	C_CLEAR,
+	C_RED
 };
 
-/* dark background */
-static enum color attr_colors_dark[] = {
-	C_BOLD_CYAN,
-	C_BOLD_YELLOW,
-	C_BOLD_MAGENTA,
-	C_BOLD_BLUE,
-	C_BOLD_GREEN,
-	C_BOLD_RED,
-	C_CLEAR
-};
-
-static int is_dark_bg;
 static int color_is_enabled;
 
-static void enable_color(void)
+void enable_color(void)
 {
 	color_is_enabled = 1;
-	set_color_palette();
 }
 
-bool check_enable_color(int color, int json)
-{
-	if (json || color == COLOR_OPT_NEVER)
-		return false;
-
-	if (color == COLOR_OPT_ALWAYS || isatty(fileno(stdout))) {
-		enable_color();
-		return true;
-	}
-	return false;
-}
-
-bool matches_color(const char *arg, int *val)
-{
-	char *dup, *p;
-
-	if (!val)
-		return false;
-
-	dup = strdupa(arg);
-	p = strchrnul(dup, '=');
-	if (*p)
-		*(p++) = '\0';
-
-	if (matches(dup, "-color"))
-		return false;
-
-	if (*p == '\0' || !strcmp(p, "always"))
-		*val = COLOR_OPT_ALWAYS;
-	else if (!strcmp(p, "auto"))
-		*val = COLOR_OPT_AUTO;
-	else if (!strcmp(p, "never"))
-		*val = COLOR_OPT_NEVER;
-	else
-		return false;
-	return true;
-}
-
-static void set_color_palette(void)
-{
-	char *p = getenv("COLORFGBG");
-
-	/*
-	 * COLORFGBG environment variable usually contains either two or three
-	 * values separated by semicolons; we want the last value in either case.
-	 * If this value is 0-6 or 8, background is dark.
-	 */
-	if (p && (p = strrchr(p, ';')) != NULL
-		&& ((p[1] >= '0' && p[1] <= '6') || p[1] == '8')
-		&& p[2] == '\0')
-		is_dark_bg = 1;
-}
-
-__attribute__((format(printf, 3, 4)))
 int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...)
 {
 	int ret = 0;
@@ -142,14 +49,12 @@
 
 	va_start(args, fmt);
 
-	if (!color_is_enabled || attr == COLOR_NONE) {
+	if (!color_is_enabled) {
 		ret = vfprintf(fp, fmt, args);
 		goto end;
 	}
 
-	ret += fprintf(fp, "%s", color_codes[is_dark_bg ?
-		attr_colors_dark[attr] : attr_colors_light[attr]]);
-
+	ret += fprintf(fp, "%s", color_codes[attr_colors[attr]]);
 	ret += vfprintf(fp, fmt, args);
 	ret += fprintf(fp, "%s", color_codes[C_CLEAR]);
 
@@ -157,27 +62,3 @@
 	va_end(args);
 	return ret;
 }
-
-enum color_attr ifa_family_color(__u8 ifa_family)
-{
-	switch (ifa_family) {
-	case AF_INET:
-		return COLOR_INET;
-	case AF_INET6:
-		return COLOR_INET6;
-	default:
-		return COLOR_NONE;
-	}
-}
-
-enum color_attr oper_state_color(__u8 state)
-{
-	switch (state) {
-	case IF_OPER_UP:
-		return COLOR_OPERSTATE_UP;
-	case IF_OPER_DOWN:
-		return COLOR_OPERSTATE_DOWN;
-	default:
-		return COLOR_NONE;
-	}
-}
diff --git a/lib/coverity_model.c b/lib/coverity_model.c
index 1321fe8..c896302 100644
--- a/lib/coverity_model.c
+++ b/lib/coverity_model.c
@@ -15,3 +15,5 @@
  * Coverity Scan doesn't pick up modifications automatically. The model file
  * must be uploaded by an admin.
  */
+
+
diff --git a/lib/dnet_ntop.c b/lib/dnet_ntop.c
new file mode 100644
index 0000000..507a7eb
--- /dev/null
+++ b/lib/dnet_ntop.c
@@ -0,0 +1,102 @@
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "utils.h"
+
+static __inline__ u_int16_t dn_ntohs(u_int16_t addr)
+{
+	union {
+		u_int8_t byte[2];
+		u_int16_t word;
+	} u;
+
+	u.word = addr;
+	return ((u_int16_t)u.byte[0]) | (((u_int16_t)u.byte[1]) << 8);
+}
+
+static __inline__ int do_digit(char *str, u_int16_t *addr, u_int16_t scale, size_t *pos, size_t len, int *started)
+{
+	u_int16_t tmp = *addr / scale;
+
+	if (*pos == len)
+		return 1;
+
+	if (((tmp) > 0) || *started || (scale == 1)) {
+		*str = tmp + '0';
+		*started = 1;
+		(*pos)++;
+		*addr -= (tmp * scale);
+	}
+
+	return 0;
+}
+
+
+static const char *dnet_ntop1(const struct dn_naddr *dna, char *str, size_t len)
+{
+	u_int16_t addr, area;
+	size_t pos = 0;
+	int started = 0;
+
+	memcpy(&addr, dna->a_addr, sizeof(addr));
+	addr = dn_ntohs(addr);
+	area = addr >> 10;
+
+	if (dna->a_len != 2)
+		return NULL;
+
+	addr &= 0x03ff;
+
+	if (len == 0)
+		return str;
+
+	if (do_digit(str + pos, &area, 10, &pos, len, &started))
+		return str;
+
+	if (do_digit(str + pos, &area, 1, &pos, len, &started))
+		return str;
+
+	if (pos == len)
+		return str;
+
+	*(str + pos) = '.';
+	pos++;
+	started = 0;
+
+	if (do_digit(str + pos, &addr, 1000, &pos, len, &started))
+		return str;
+
+	if (do_digit(str + pos, &addr, 100, &pos, len, &started))
+		return str;
+
+	if (do_digit(str + pos, &addr, 10, &pos, len, &started))
+		return str;
+
+	if (do_digit(str + pos, &addr, 1, &pos, len, &started))
+		return str;
+
+	if (pos == len)
+		return str;
+
+	*(str + pos) = 0;
+
+	return str;
+}
+
+
+const char *dnet_ntop(int af, const void *addr, char *str, size_t len)
+{
+	switch(af) {
+		case AF_DECnet:
+			errno = 0;
+			return dnet_ntop1((struct dn_naddr *)addr, str, len);
+		default:
+			errno = EAFNOSUPPORT;
+	}
+
+	return NULL;
+}
+
+
diff --git a/lib/dnet_pton.c b/lib/dnet_pton.c
new file mode 100644
index 0000000..7385756
--- /dev/null
+++ b/lib/dnet_pton.c
@@ -0,0 +1,74 @@
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "utils.h"
+
+static __inline__ u_int16_t dn_htons(u_int16_t addr)
+{
+        union {
+                u_int8_t byte[2];
+                u_int16_t word;
+        } u;
+
+        u.word = addr;
+        return ((u_int16_t)u.byte[0]) | (((u_int16_t)u.byte[1]) << 8);
+}
+
+
+static int dnet_num(const char *src, u_int16_t * dst)
+{
+	int rv = 0;
+	int tmp;
+	*dst = 0;
+
+	while ((tmp = *src++) != 0) {
+		tmp -= '0';
+		if ((tmp < 0) || (tmp > 9))
+			return rv;
+
+		rv++;
+		(*dst) *= 10;
+		(*dst) += tmp;
+	}
+
+	return rv;
+}
+
+static int dnet_pton1(const char *src, struct dn_naddr *dna)
+{
+	u_int16_t addr;
+	u_int16_t area = 0;
+	u_int16_t node = 0;
+	int pos;
+
+	pos = dnet_num(src, &area);
+	if ((pos == 0) || (area > 63) || (*(src + pos) != '.'))
+		return 0;
+	pos = dnet_num(src + pos + 1, &node);
+	if ((pos == 0) || (node > 1023))
+		return 0;
+	dna->a_len = 2;
+	addr = dn_htons((area << 10) | node);
+	memcpy(dna->a_addr, &addr, sizeof(addr));
+
+	return 1;
+}
+
+int dnet_pton(int af, const char *src, void *addr)
+{
+	int err;
+
+	switch (af) {
+	case AF_DECnet:
+		errno = 0;
+		err = dnet_pton1(src, (struct dn_naddr *)addr);
+		break;
+	default:
+		errno = EAFNOSUPPORT;
+		err = -1;
+	}
+
+	return err;
+}
diff --git a/lib/exec.c b/lib/exec.c
deleted file mode 100644
index 9b1c8f4..0000000
--- a/lib/exec.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <sys/wait.h>
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include "utils.h"
-#include "namespace.h"
-
-int cmd_exec(const char *cmd, char **argv, bool do_fork,
-	     int (*setup)(void *), void *arg)
-{
-	fflush(stdout);
-	if (do_fork) {
-		int status;
-		pid_t pid;
-
-		pid = fork();
-		if (pid < 0) {
-			perror("fork");
-			exit(1);
-		}
-
-		if (pid != 0) {
-			/* Parent  */
-			if (waitpid(pid, &status, 0) < 0) {
-				perror("waitpid");
-				exit(1);
-			}
-
-			if (WIFEXITED(status)) {
-				return WEXITSTATUS(status);
-			}
-
-			exit(1);
-		}
-	}
-
-	if (setup && setup(arg))
-		return -1;
-
-	if (execvp(cmd, argv)  < 0)
-		fprintf(stderr, "exec of \"%s\" failed: %s\n",
-				cmd, strerror(errno));
-	_exit(1);
-}
diff --git a/lib/fs.c b/lib/fs.c
deleted file mode 100644
index 86efd4e..0000000
--- a/lib/fs.c
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * fs.c         filesystem APIs
- *
- *		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:	David Ahern <dsa@cumulusnetworks.com>
- *
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/mount.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <limits.h>
-
-#include "utils.h"
-
-#define CGROUP2_FS_NAME "cgroup2"
-
-/* if not already mounted cgroup2 is mounted here for iproute2's use */
-#define MNT_CGRP2_PATH  "/var/run/cgroup2"
-
-/* return mount path of first occurrence of given fstype */
-static char *find_fs_mount(const char *fs_to_find)
-{
-	char path[4096];
-	char fstype[128];    /* max length of any filesystem name */
-	char *mnt = NULL;
-	FILE *fp;
-
-	fp = fopen("/proc/mounts", "r");
-	if (!fp) {
-		fprintf(stderr,
-			"Failed to open mounts file: %s\n", strerror(errno));
-		return NULL;
-	}
-
-	while (fscanf(fp, "%*s %4095s %127s %*s %*d %*d\n",
-		      path, fstype) == 2) {
-		if (strcmp(fstype, fs_to_find) == 0) {
-			mnt = strdup(path);
-			break;
-		}
-	}
-
-	fclose(fp);
-
-	return mnt;
-}
-
-/* caller needs to free string returned */
-char *find_cgroup2_mount(void)
-{
-	char *mnt = find_fs_mount(CGROUP2_FS_NAME);
-
-	if (mnt)
-		return mnt;
-
-	mnt = strdup(MNT_CGRP2_PATH);
-	if (!mnt) {
-		fprintf(stderr, "Failed to allocate memory for cgroup2 path\n");
-		return NULL;
-
-	}
-
-	if (make_path(mnt, 0755)) {
-		fprintf(stderr, "Failed to setup vrf cgroup2 directory\n");
-		free(mnt);
-		return NULL;
-	}
-
-	if (mount("none", mnt, CGROUP2_FS_NAME, 0, NULL)) {
-		/* EBUSY means already mounted */
-		if (errno == EBUSY)
-			goto out;
-
-		if (errno == ENODEV) {
-			fprintf(stderr,
-				"Failed to mount cgroup2. Are CGROUPS enabled in your kernel?\n");
-		} else {
-			fprintf(stderr,
-				"Failed to mount cgroup2: %s\n",
-				strerror(errno));
-		}
-		free(mnt);
-		return NULL;
-	}
-out:
-	return mnt;
-}
-
-int make_path(const char *path, mode_t mode)
-{
-	char *dir, *delim;
-	int rc = -1;
-
-	delim = dir = strdup(path);
-	if (dir == NULL) {
-		fprintf(stderr, "strdup failed copying path");
-		return -1;
-	}
-
-	/* skip '/' -- it had better exist */
-	if (*delim == '/')
-		delim++;
-
-	while (1) {
-		delim = strchr(delim, '/');
-		if (delim)
-			*delim = '\0';
-
-		rc = mkdir(dir, mode);
-		if (mkdir(dir, mode) != 0 && errno != EEXIST) {
-			fprintf(stderr, "mkdir failed for %s: %s\n",
-				dir, strerror(errno));
-			goto out;
-		}
-
-		if (delim == NULL)
-			break;
-
-		*delim = '/';
-		delim++;
-		if (*delim == '\0')
-			break;
-	}
-	rc = 0;
-out:
-	free(dir);
-
-	return rc;
-}
-
-int get_command_name(const char *pid, char *comm, size_t len)
-{
-	char path[PATH_MAX];
-	char line[128];
-	FILE *fp;
-
-	if (snprintf(path, sizeof(path),
-		     "/proc/%s/status", pid) >= sizeof(path)) {
-		return -1;
-	}
-
-	fp = fopen(path, "r");
-	if (!fp)
-		return -1;
-
-	comm[0] = '\0';
-	while (fgets(line, sizeof(line), fp)) {
-		char *nl, *name;
-
-		name = strstr(line, "Name:");
-		if (!name)
-			continue;
-
-		name += 5;
-		while (isspace(*name))
-			name++;
-
-		nl = strchr(name, '\n');
-		if (nl)
-			*nl = '\0';
-
-		strlcpy(comm, name, len);
-		break;
-	}
-
-	fclose(fp);
-
-	return 0;
-}
diff --git a/lib/inet_proto.c b/lib/inet_proto.c
index 41e2e8b..57a8351 100644
--- a/lib/inet_proto.c
+++ b/lib/inet_proto.c
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -24,7 +25,7 @@
 
 const char *inet_proto_n2a(int proto, char *buf, int len)
 {
-	static char *ncache;
+	static char ncache[16];
 	static int icache = -1;
 	struct protoent *pe;
 
@@ -32,12 +33,10 @@
 		return ncache;
 
 	pe = getprotobynumber(proto);
-	if (pe && !numeric) {
-		if (icache != -1)
-			free(ncache);
+	if (pe) {
 		icache = proto;
-		ncache = strdup(pe->p_name);
-		strlcpy(buf, pe->p_name, len);
+		strncpy(ncache, pe->p_name, 16);
+		strncpy(buf, pe->p_name, len);
 		return buf;
 	}
 	snprintf(buf, len, "ipproto-%d", proto);
@@ -46,24 +45,27 @@
 
 int inet_proto_a2n(const char *buf)
 {
-	static char *ncache;
+	static char ncache[16];
 	static int icache = -1;
 	struct protoent *pe;
-	__u8 ret;
 
-	if (icache != -1 && strcmp(ncache, buf) == 0)
+	if (icache>=0 && strcmp(ncache, buf) == 0)
 		return icache;
 
-	if (!get_u8(&ret, buf, 10))
+	if (buf[0] >= '0' && buf[0] <= '9') {
+		__u8 ret;
+		if (get_u8(&ret, buf, 10))
+			return -1;
 		return ret;
+	}
 
 	pe = getprotobyname(buf);
 	if (pe) {
-		if (icache != -1)
-			free(ncache);
 		icache = pe->p_proto;
-		ncache = strdup(pe->p_name);
+		strncpy(ncache, pe->p_name, 16);
 		return pe->p_proto;
 	}
 	return -1;
 }
+
+
diff --git a/lib/ipx_ntop.c b/lib/ipx_ntop.c
new file mode 100644
index 0000000..1e46bc2
--- /dev/null
+++ b/lib/ipx_ntop.c
@@ -0,0 +1,72 @@
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "utils.h"
+
+static __inline__ int do_digit(char *str, u_int32_t addr, u_int32_t scale, size_t *pos, size_t len)
+{
+	u_int32_t tmp = addr >> (scale * 4);
+
+	if (*pos == len)
+		return 1;
+
+	tmp &= 0x0f;
+	if (tmp > 9)
+		*str = tmp + 'A' - 10;
+	else
+		*str = tmp + '0';
+	(*pos)++;
+
+	return 0;
+}
+
+static const char *ipx_ntop1(const struct ipx_addr *addr, char *str, size_t len)
+{
+	int i;
+	size_t pos = 0;
+
+	if (len == 0)
+		return str;
+
+	for(i = 7; i >= 0; i--)
+		if (do_digit(str + pos, ntohl(addr->ipx_net), i, &pos, len))
+			return str;
+
+	if (pos == len)
+		return str;
+
+	*(str + pos) = '.';
+	pos++;
+
+	for(i = 0; i < 6; i++) {
+		if (do_digit(str + pos, addr->ipx_node[i], 1, &pos, len))
+			return str;
+		if (do_digit(str + pos, addr->ipx_node[i], 0, &pos, len))
+			return str;
+	}
+
+	if (pos == len)
+		return str;
+
+	*(str + pos) = 0;
+
+	return str;
+}
+
+
+const char *ipx_ntop(int af, const void *addr, char *str, size_t len)
+{
+	switch(af) {
+		case AF_IPX:
+			errno = 0;
+			return ipx_ntop1((struct ipx_addr *)addr, str, len);
+		default:
+			errno = EAFNOSUPPORT;
+	}
+
+	return NULL;
+}
+
+
diff --git a/lib/ipx_pton.c b/lib/ipx_pton.c
new file mode 100644
index 0000000..3dca271
--- /dev/null
+++ b/lib/ipx_pton.c
@@ -0,0 +1,108 @@
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "utils.h"
+
+static u_int32_t hexget(char c)
+{
+	if (c >= 'A' && c <= 'F')
+		return c - 'A' + 10;
+	if (c >= 'a' && c <= 'f')
+		return c - 'a' + 10;
+	if (c >= '0' && c <= '9')
+		return c - '0';
+
+	return 0xf0;
+}
+
+static int ipx_getnet(u_int32_t *net, const char *str)
+{
+	int i;
+	u_int32_t tmp;
+
+	for(i = 0; *str && (i < 8); i++) {
+
+		if ((tmp = hexget(*str)) & 0xf0) {
+			if (*str == '.')
+				return 0;
+			else
+				return -1;
+		}
+
+		str++;
+		(*net) <<= 4;
+		(*net) |= tmp;
+	}
+
+	if (*str == 0)
+		return 0;
+
+	return -1;
+}
+
+static int ipx_getnode(u_int8_t *node, const char *str)
+{
+	int i;
+	u_int32_t tmp;
+
+	for(i = 0; i < 6; i++) {
+		if ((tmp = hexget(*str++)) & 0xf0)
+			return -1;
+		node[i] = (u_int8_t)tmp;
+		node[i] <<= 4;
+		if ((tmp = hexget(*str++)) & 0xf0)
+			return -1;
+		node[i] |= (u_int8_t)tmp;
+		if (*str == ':')
+			str++;
+	}
+
+	return 0;
+}
+
+static int ipx_pton1(const char *src, struct ipx_addr *addr)
+{
+	char *sep = (char *)src;
+	int no_node = 0;
+
+	memset(addr, 0, sizeof(struct ipx_addr));
+
+	while(*sep && (*sep != '.'))
+		sep++;
+
+	if (*sep != '.')
+		no_node = 1;
+
+	if (ipx_getnet(&addr->ipx_net, src))
+		return 0;
+
+	addr->ipx_net = htonl(addr->ipx_net);
+
+	if (no_node)
+		return 1;
+
+	if (ipx_getnode(addr->ipx_node, sep + 1))
+		return 0;
+
+	return 1;
+}
+
+int ipx_pton(int af, const char *src, void *addr)
+{
+	int err;
+
+	switch (af) {
+	case AF_IPX:
+		errno = 0;
+		err = ipx_pton1(src, (struct ipx_addr *)addr);
+		break;
+	default:
+		errno = EAFNOSUPPORT;
+		err = -1;
+	}
+
+	return err;
+}
diff --git a/lib/json_print.c b/lib/json_print.c
deleted file mode 100644
index 43ea69b..0000000
--- a/lib/json_print.c
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * json_print.c		"print regular or json output, based on json_writer".
- *
- *             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:    Julien Fortin, <julien@cumulusnetworks.com>
- */
-
-#include <stdarg.h>
-#include <stdio.h>
-
-#include "utils.h"
-#include "json_print.h"
-
-static json_writer_t *_jw;
-
-#define _IS_JSON_CONTEXT(type) ((type & PRINT_JSON || type & PRINT_ANY) && _jw)
-#define _IS_FP_CONTEXT(type) (!_jw && (type & PRINT_FP || type & PRINT_ANY))
-
-void new_json_obj(int json)
-{
-	if (json) {
-		_jw = jsonw_new(stdout);
-		if (!_jw) {
-			perror("json object");
-			exit(1);
-		}
-		if (pretty)
-			jsonw_pretty(_jw, true);
-		jsonw_start_array(_jw);
-	}
-}
-
-void delete_json_obj(void)
-{
-	if (_jw) {
-		jsonw_end_array(_jw);
-		jsonw_destroy(&_jw);
-	}
-}
-
-bool is_json_context(void)
-{
-	return _jw != NULL;
-}
-
-json_writer_t *get_json_writer(void)
-{
-	return _jw;
-}
-
-void open_json_object(const char *str)
-{
-	if (_IS_JSON_CONTEXT(PRINT_JSON)) {
-		if (str)
-			jsonw_name(_jw, str);
-		jsonw_start_object(_jw);
-	}
-}
-
-void close_json_object(void)
-{
-	if (_IS_JSON_CONTEXT(PRINT_JSON))
-		jsonw_end_object(_jw);
-}
-
-/*
- * Start json array or string array using
- * the provided string as json key (if not null)
- * or as array delimiter in non-json context.
- */
-void open_json_array(enum output_type type, const char *str)
-{
-	if (_IS_JSON_CONTEXT(type)) {
-		if (str)
-			jsonw_name(_jw, str);
-		jsonw_start_array(_jw);
-	} else if (_IS_FP_CONTEXT(type)) {
-		printf("%s", str);
-	}
-}
-
-/*
- * End json array or string array
- */
-void close_json_array(enum output_type type, const char *str)
-{
-	if (_IS_JSON_CONTEXT(type)) {
-		jsonw_end_array(_jw);
-	} else if (_IS_FP_CONTEXT(type)) {
-		printf("%s", str);
-	}
-}
-
-/*
- * pre-processor directive to generate similar
- * functions handling different types
- */
-#define _PRINT_FUNC(type_name, type)					\
-	__attribute__((format(printf, 4, 0)))				\
-	void print_color_##type_name(enum output_type t,		\
-				     enum color_attr color,		\
-				     const char *key,			\
-				     const char *fmt,			\
-				     type value)			\
-	{								\
-		if (_IS_JSON_CONTEXT(t)) {				\
-			if (!key)					\
-				jsonw_##type_name(_jw, value);		\
-			else						\
-				jsonw_##type_name##_field(_jw, key, value); \
-		} else if (_IS_FP_CONTEXT(t)) {				\
-			color_fprintf(stdout, color, fmt, value);          \
-		}							\
-	}
-_PRINT_FUNC(int, int);
-_PRINT_FUNC(s64, int64_t);
-_PRINT_FUNC(hhu, unsigned char);
-_PRINT_FUNC(hu, unsigned short);
-_PRINT_FUNC(uint, unsigned int);
-_PRINT_FUNC(u64, uint64_t);
-_PRINT_FUNC(luint, unsigned long);
-_PRINT_FUNC(lluint, unsigned long long);
-_PRINT_FUNC(float, double);
-#undef _PRINT_FUNC
-
-void print_color_string(enum output_type type,
-			enum color_attr color,
-			const char *key,
-			const char *fmt,
-			const char *value)
-{
-	if (_IS_JSON_CONTEXT(type)) {
-		if (key && !value)
-			jsonw_name(_jw, key);
-		else if (!key && value)
-			jsonw_string(_jw, value);
-		else
-			jsonw_string_field(_jw, key, value);
-	} else if (_IS_FP_CONTEXT(type)) {
-		color_fprintf(stdout, color, fmt, value);
-	}
-}
-
-/*
- * value's type is bool. When using this function in FP context you can't pass
- * a value to it, you will need to use "is_json_context()" to have different
- * branch for json and regular output. grep -r "print_bool" for example
- */
-void print_color_bool(enum output_type type,
-		      enum color_attr color,
-		      const char *key,
-		      const char *fmt,
-		      bool value)
-{
-	if (_IS_JSON_CONTEXT(type)) {
-		if (key)
-			jsonw_bool_field(_jw, key, value);
-		else
-			jsonw_bool(_jw, value);
-	} else if (_IS_FP_CONTEXT(type)) {
-		color_fprintf(stdout, color, fmt, value ? "true" : "false");
-	}
-}
-
-/*
- * In JSON context uses hardcode %#x format: 42 -> 0x2a
- */
-void print_color_0xhex(enum output_type type,
-		       enum color_attr color,
-		       const char *key,
-		       const char *fmt,
-		       unsigned long long hex)
-{
-	if (_IS_JSON_CONTEXT(type)) {
-		SPRINT_BUF(b1);
-
-		snprintf(b1, sizeof(b1), "%#llx", hex);
-		print_string(PRINT_JSON, key, NULL, b1);
-	} else if (_IS_FP_CONTEXT(type)) {
-		color_fprintf(stdout, color, fmt, hex);
-	}
-}
-
-void print_color_hex(enum output_type type,
-		     enum color_attr color,
-		     const char *key,
-		     const char *fmt,
-		     unsigned int hex)
-{
-	if (_IS_JSON_CONTEXT(type)) {
-		SPRINT_BUF(b1);
-
-		snprintf(b1, sizeof(b1), "%x", hex);
-		if (key)
-			jsonw_string_field(_jw, key, b1);
-		else
-			jsonw_string(_jw, b1);
-	} else if (_IS_FP_CONTEXT(type)) {
-		color_fprintf(stdout, color, fmt, hex);
-	}
-}
-
-/*
- * In JSON context we don't use the argument "value" we simply call jsonw_null
- * whereas FP context can use "value" to output anything
- */
-void print_color_null(enum output_type type,
-		      enum color_attr color,
-		      const char *key,
-		      const char *fmt,
-		      const char *value)
-{
-	if (_IS_JSON_CONTEXT(type)) {
-		if (key)
-			jsonw_null_field(_jw, key);
-		else
-			jsonw_null(_jw);
-	} else if (_IS_FP_CONTEXT(type)) {
-		color_fprintf(stdout, color, fmt, value);
-	}
-}
-
-/* Print line separator (if not in JSON mode) */
-void print_nl(void)
-{
-	if (!_jw)
-		printf("%s", _SL_);
-}
diff --git a/lib/json_writer.c b/lib/json_writer.c
index 88c5eb8..2af16e1 100644
--- a/lib/json_writer.c
+++ b/lib/json_writer.c
@@ -1,10 +1,14 @@
-/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
 /*
  * Simple streaming JSON writer
  *
  * This takes care of the annoying bits of JSON syntax like the commas
  * after elements
  *
+ * 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 <stephen@networkplumber.org>
  */
 
@@ -29,7 +33,7 @@
 static void jsonw_indent(json_writer_t *self)
 {
 	unsigned i;
-	for (i = 0; i < self->depth; ++i)
+	for (i = 0; i <= self->depth; ++i)
 		fputs("    ", self->out);
 }
 
@@ -75,7 +79,7 @@
 			fputs("\\b", self->out);
 			break;
 		case '\\':
-			fputs("\\\\", self->out);
+			fputs("\\n", self->out);
 			break;
 		case '"':
 			fputs("\\\"", self->out);
@@ -98,6 +102,7 @@
 		self->depth = 0;
 		self->pretty = false;
 		self->sep = '\0';
+		putc('{', self->out);
 	}
 	return self;
 }
@@ -108,7 +113,8 @@
 	json_writer_t *self = *self_p;
 
 	assert(self->depth == 0);
-	fputs("\n", self->out);
+	jsonw_eol(self);
+	fputs("}\n", self->out);
 	fflush(self->out);
 	free(self);
 	*self_p = NULL;
@@ -152,8 +158,7 @@
 		putc(' ', self->out);
 }
 
-__attribute__((format(printf, 2, 3)))
-void jsonw_printf(json_writer_t *self, const char *fmt, ...)
+static void jsonw_printf(json_writer_t *self, const char *fmt, ...)
 {
 	va_list ap;
 
@@ -177,15 +182,10 @@
 void jsonw_start_array(json_writer_t *self)
 {
 	jsonw_begin(self, '[');
-	if (self->pretty)
-		putc(' ', self->out);
 }
 
 void jsonw_end_array(json_writer_t *self)
 {
-	if (self->pretty && self->sep)
-		putc(' ', self->out);
-	self->sep = '\0';
 	jsonw_end(self, ']');
 }
 
@@ -201,6 +201,7 @@
 	jsonw_printf(self, "%s", val ? "true" : "false");
 }
 
+#ifdef notused
 void jsonw_null(json_writer_t *self)
 {
 	jsonw_printf(self, "null");
@@ -210,48 +211,14 @@
 {
 	jsonw_printf(self, "%g", num);
 }
+#endif
 
-void jsonw_hhu(json_writer_t *self, unsigned char num)
-{
-	jsonw_printf(self, "%hhu", num);
-}
-
-void jsonw_hu(json_writer_t *self, unsigned short num)
-{
-	jsonw_printf(self, "%hu", num);
-}
-
-void jsonw_uint(json_writer_t *self, unsigned int num)
-{
-	jsonw_printf(self, "%u", num);
-}
-
-void jsonw_u64(json_writer_t *self, uint64_t num)
+void jsonw_uint(json_writer_t *self, uint64_t num)
 {
 	jsonw_printf(self, "%"PRIu64, num);
 }
 
-void jsonw_xint(json_writer_t *self, uint64_t num)
-{
-	jsonw_printf(self, "%"PRIx64, num);
-}
-
-void jsonw_luint(json_writer_t *self, unsigned long num)
-{
-	jsonw_printf(self, "%lu", num);
-}
-
-void jsonw_lluint(json_writer_t *self, unsigned long long num)
-{
-	jsonw_printf(self, "%llu", num);
-}
-
-void jsonw_int(json_writer_t *self, int num)
-{
-	jsonw_printf(self, "%d", num);
-}
-
-void jsonw_s64(json_writer_t *self, int64_t num)
+void jsonw_int(json_writer_t *self, int64_t num)
 {
 	jsonw_printf(self, "%"PRId64, num);
 }
@@ -269,82 +236,39 @@
 	jsonw_bool(self, val);
 }
 
+#ifdef notused
 void jsonw_float_field(json_writer_t *self, const char *prop, double val)
 {
 	jsonw_name(self, prop);
 	jsonw_float(self, val);
 }
+#endif
 
-void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num)
+void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num)
 {
 	jsonw_name(self, prop);
 	jsonw_uint(self, num);
 }
 
-void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num)
-{
-	jsonw_name(self, prop);
-	jsonw_u64(self, num);
-}
-
-void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num)
-{
-	jsonw_name(self, prop);
-	jsonw_xint(self, num);
-}
-
-void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num)
-{
-	jsonw_name(self, prop);
-	jsonw_hhu(self, num);
-}
-
-void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
-{
-	jsonw_name(self, prop);
-	jsonw_hu(self, num);
-}
-
-void jsonw_luint_field(json_writer_t *self,
-			const char *prop,
-			unsigned long num)
-{
-	jsonw_name(self, prop);
-	jsonw_luint(self, num);
-}
-
-void jsonw_lluint_field(json_writer_t *self,
-			const char *prop,
-			unsigned long long num)
-{
-	jsonw_name(self, prop);
-	jsonw_lluint(self, num);
-}
-
-void jsonw_int_field(json_writer_t *self, const char *prop, int num)
+void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num)
 {
 	jsonw_name(self, prop);
 	jsonw_int(self, num);
 }
 
-void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num)
-{
-	jsonw_name(self, prop);
-	jsonw_s64(self, num);
-}
-
+#ifdef notused
 void jsonw_null_field(json_writer_t *self, const char *prop)
 {
 	jsonw_name(self, prop);
 	jsonw_null(self);
 }
+#endif
 
 #ifdef TEST
 int main(int argc, char **argv)
 {
 	json_writer_t *wr = jsonw_new(stdout);
 
-	jsonw_start_object(wr);
 	jsonw_pretty(wr, true);
 	jsonw_name(wr, "Vyatta");
 	jsonw_start_object(wr);
@@ -381,7 +305,6 @@
 
 	jsonw_end_object(wr);
 
-	jsonw_end_object(wr);
 	jsonw_destroy(&wr);
 	return 0;
 }
diff --git a/lib/libgenl.c b/lib/libgenl.c
index f2ce698..acb1478 100644
--- a/lib/libgenl.c
+++ b/lib/libgenl.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * libgenl.c	GENL library
  */
@@ -50,37 +49,15 @@
 {
 	GENL_REQUEST(req, 1024, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY,
 		     NLM_F_REQUEST);
-	struct nlmsghdr *answer;
-	int fnum;
 
 	addattr_l(&req.n, sizeof(req), CTRL_ATTR_FAMILY_NAME,
 		  family, strlen(family) + 1);
 
-	if (rtnl_talk(grth, &req.n, &answer) < 0) {
+	if (rtnl_talk(grth, &req.n, &req.n, sizeof(req)) < 0) {
 		fprintf(stderr, "Error talking to the kernel\n");
 		return -2;
 	}
 
-	fnum = genl_parse_getfamily(answer);
-	free(answer);
-
-	return fnum;
+	return genl_parse_getfamily(&req.n);
 }
 
-int genl_init_handle(struct rtnl_handle *grth, const char *family,
-		     int *genl_family)
-{
-	if (*genl_family >= 0)
-		return 0;
-
-	if (rtnl_open_byproto(grth, 0, NETLINK_GENERIC) < 0) {
-		fprintf(stderr, "Cannot open generic netlink socket\n");
-		return -1;
-	}
-
-	*genl_family = genl_resolve_family(grth, family);
-	if (*genl_family < 0)
-		return -1;
-
-	return 0;
-}
diff --git a/lib/libnetlink.c b/lib/libnetlink.c
index e02d629..d6b5fd3 100644
--- a/lib/libnetlink.c
+++ b/lib/libnetlink.c
@@ -12,8 +12,8 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdbool.h>
 #include <unistd.h>
+#include <syslog.h>
 #include <fcntl.h>
 #include <net/if_arp.h>
 #include <sys/socket.h>
@@ -22,15 +22,9 @@
 #include <errno.h>
 #include <time.h>
 #include <sys/uio.h>
-#include <linux/fib_rules.h>
-#include <linux/if_addrlabel.h>
-#include <linux/if_bridge.h>
-#include <linux/nexthop.h>
 
 #include "libnetlink.h"
 
-#define __aligned(x)		__attribute__((aligned(x)))
-
 #ifndef SOL_NETLINK
 #define SOL_NETLINK 270
 #endif
@@ -41,145 +35,6 @@
 
 int rcvbuf = 1024 * 1024;
 
-#ifdef HAVE_LIBMNL
-#include <libmnl/libmnl.h>
-
-static const enum mnl_attr_data_type extack_policy[NLMSGERR_ATTR_MAX + 1] = {
-	[NLMSGERR_ATTR_MSG]	= MNL_TYPE_NUL_STRING,
-	[NLMSGERR_ATTR_OFFS]	= MNL_TYPE_U32,
-};
-
-static int err_attr_cb(const struct nlattr *attr, void *data)
-{
-	const struct nlattr **tb = data;
-	uint16_t type;
-
-	if (mnl_attr_type_valid(attr, NLMSGERR_ATTR_MAX) < 0) {
-		fprintf(stderr, "Invalid extack attribute\n");
-		return MNL_CB_ERROR;
-	}
-
-	type = mnl_attr_get_type(attr);
-	if (mnl_attr_validate(attr, extack_policy[type]) < 0) {
-		fprintf(stderr, "extack attribute %d failed validation\n",
-			type);
-		return MNL_CB_ERROR;
-	}
-
-	tb[type] = attr;
-	return MNL_CB_OK;
-}
-
-static void print_ext_ack_msg(bool is_err, const char *msg)
-{
-	fprintf(stderr, "%s: %s", is_err ? "Error" : "Warning", msg);
-	if (msg[strlen(msg) - 1] != '.')
-		fprintf(stderr, ".");
-	fprintf(stderr, "\n");
-}
-
-/* dump netlink extended ack error message */
-int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
-{
-	struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
-	const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
-	const struct nlmsghdr *err_nlh = NULL;
-	unsigned int hlen = sizeof(*err);
-	const char *msg = NULL;
-	uint32_t off = 0;
-
-	/* no TLVs, nothing to do here */
-	if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
-		return 0;
-
-	/* if NLM_F_CAPPED is set then the inner err msg was capped */
-	if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
-		hlen += mnl_nlmsg_get_payload_len(&err->msg);
-
-	if (mnl_attr_parse(nlh, hlen, err_attr_cb, tb) != MNL_CB_OK)
-		return 0;
-
-	if (tb[NLMSGERR_ATTR_MSG])
-		msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
-
-	if (tb[NLMSGERR_ATTR_OFFS]) {
-		off = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
-
-		if (off > nlh->nlmsg_len) {
-			fprintf(stderr,
-				"Invalid offset for NLMSGERR_ATTR_OFFS\n");
-			off = 0;
-		} else if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
-			err_nlh = &err->msg;
-	}
-
-	if (errfn)
-		return errfn(msg, off, err_nlh);
-
-	if (msg && *msg != '\0') {
-		bool is_err = !!err->error;
-
-		print_ext_ack_msg(is_err, msg);
-		return is_err ? 1 : 0;
-	}
-
-	return 0;
-}
-
-int nl_dump_ext_ack_done(const struct nlmsghdr *nlh, int error)
-{
-	struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
-	unsigned int hlen = sizeof(int);
-	const char *msg = NULL;
-
-	if (mnl_attr_parse(nlh, hlen, err_attr_cb, tb) != MNL_CB_OK)
-		return 0;
-
-	if (tb[NLMSGERR_ATTR_MSG])
-		msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
-
-	if (msg && *msg != '\0') {
-		bool is_err = !!error;
-
-		print_ext_ack_msg(is_err, msg);
-		return is_err ? 1 : 0;
-	}
-
-	return 0;
-}
-#else
-#warning "libmnl required for error support"
-
-/* No extended error ack without libmnl */
-int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
-{
-	return 0;
-}
-
-int nl_dump_ext_ack_done(const struct nlmsghdr *nlh, int error)
-{
-	return 0;
-}
-#endif
-
-/* Older kernels may not support strict dump and filtering */
-void rtnl_set_strict_dump(struct rtnl_handle *rth)
-{
-	int one = 1;
-
-	if (setsockopt(rth->fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK,
-		       &one, sizeof(one)) < 0)
-		return;
-
-	rth->flags |= RTNL_HANDLE_F_STRICT_CHK;
-}
-
-int rtnl_add_nl_group(struct rtnl_handle *rth, unsigned int group)
-{
-	return setsockopt(rth->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
-			  &group, sizeof(group));
-}
-
 void rtnl_close(struct rtnl_handle *rth)
 {
 	if (rth->fd >= 0) {
@@ -188,12 +43,11 @@
 	}
 }
 
-int rtnl_open_byproto(struct rtnl_handle *rth, unsigned int subscriptions,
+int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
 		      int protocol)
 {
 	socklen_t addr_len;
 	int sndbuf = 32768;
-	int one = 1;
 
 	memset(rth, 0, sizeof(*rth));
 
@@ -204,34 +58,26 @@
 		return -1;
 	}
 
-	if (setsockopt(rth->fd, SOL_SOCKET, SO_SNDBUF,
-		       &sndbuf, sizeof(sndbuf)) < 0) {
+	if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
 		perror("SO_SNDBUF");
 		return -1;
 	}
 
-	if (setsockopt(rth->fd, SOL_SOCKET, SO_RCVBUF,
-		       &rcvbuf, sizeof(rcvbuf)) < 0) {
+	if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) {
 		perror("SO_RCVBUF");
 		return -1;
 	}
 
-	/* Older kernels may no support extended ACK reporting */
-	setsockopt(rth->fd, SOL_NETLINK, NETLINK_EXT_ACK,
-		   &one, sizeof(one));
-
 	memset(&rth->local, 0, sizeof(rth->local));
 	rth->local.nl_family = AF_NETLINK;
 	rth->local.nl_groups = subscriptions;
 
-	if (bind(rth->fd, (struct sockaddr *)&rth->local,
-		 sizeof(rth->local)) < 0) {
+	if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
 		perror("Cannot bind netlink socket");
 		return -1;
 	}
 	addr_len = sizeof(rth->local);
-	if (getsockname(rth->fd, (struct sockaddr *)&rth->local,
-			&addr_len) < 0) {
+	if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
 		perror("Cannot getsockname");
 		return -1;
 	}
@@ -240,351 +86,47 @@
 		return -1;
 	}
 	if (rth->local.nl_family != AF_NETLINK) {
-		fprintf(stderr, "Wrong address family %d\n",
-			rth->local.nl_family);
+		fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
 		return -1;
 	}
 	rth->seq = time(NULL);
 	return 0;
 }
 
-int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions)
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
 {
 	return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
 }
 
-int rtnl_nexthopdump_req(struct rtnl_handle *rth, int family,
-			 req_filter_fn_t filter_fn)
+int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
 {
-	struct {
-		struct nlmsghdr nlh;
-		struct nhmsg nhm;
-		char buf[128];
-	} req = {
-		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
-		.nlh.nlmsg_type = RTM_GETNEXTHOP,
-		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-		.nlh.nlmsg_seq = rth->dump = ++rth->seq,
-		.nhm.nh_family = family,
-	};
-
-	if (filter_fn) {
-		int err;
-
-		err = filter_fn(&req.nlh, sizeof(req));
-		if (err)
-			return err;
-	}
-
-	return send(rth->fd, &req, sizeof(req), 0);
+	return rtnl_wilddump_req_filter(rth, family, type, RTEXT_FILTER_VF);
 }
 
-int rtnl_addrdump_req(struct rtnl_handle *rth, int family,
-		      req_filter_fn_t filter_fn)
-{
-	struct {
-		struct nlmsghdr nlh;
-		struct ifaddrmsg ifm;
-		char buf[128];
-	} req = {
-		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
-		.nlh.nlmsg_type = RTM_GETADDR,
-		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-		.nlh.nlmsg_seq = rth->dump = ++rth->seq,
-		.ifm.ifa_family = family,
-	};
-
-	if (filter_fn) {
-		int err;
-
-		err = filter_fn(&req.nlh, sizeof(req));
-		if (err)
-			return err;
-	}
-
-	return send(rth->fd, &req, sizeof(req), 0);
-}
-
-int rtnl_addrlbldump_req(struct rtnl_handle *rth, int family)
-{
-	struct {
-		struct nlmsghdr nlh;
-		struct ifaddrlblmsg ifal;
-	} req = {
-		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrlblmsg)),
-		.nlh.nlmsg_type = RTM_GETADDRLABEL,
-		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-		.nlh.nlmsg_seq = rth->dump = ++rth->seq,
-		.ifal.ifal_family = family,
-	};
-
-	return send(rth->fd, &req, sizeof(req), 0);
-}
-
-int rtnl_routedump_req(struct rtnl_handle *rth, int family,
-		       req_filter_fn_t filter_fn)
-{
-	struct {
-		struct nlmsghdr nlh;
-		struct rtmsg rtm;
-		char buf[128];
-	} req = {
-		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
-		.nlh.nlmsg_type = RTM_GETROUTE,
-		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-		.nlh.nlmsg_seq = rth->dump = ++rth->seq,
-		.rtm.rtm_family = family,
-	};
-
-	if (filter_fn) {
-		int err;
-
-		err = filter_fn(&req.nlh, sizeof(req));
-		if (err)
-			return err;
-	}
-
-	return send(rth->fd, &req, sizeof(req), 0);
-}
-
-int rtnl_ruledump_req(struct rtnl_handle *rth, int family)
-{
-	struct {
-		struct nlmsghdr nlh;
-		struct fib_rule_hdr frh;
-	} req = {
-		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct fib_rule_hdr)),
-		.nlh.nlmsg_type = RTM_GETRULE,
-		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-		.nlh.nlmsg_seq = rth->dump = ++rth->seq,
-		.frh.family = family
-	};
-
-	return send(rth->fd, &req, sizeof(req), 0);
-}
-
-int rtnl_neighdump_req(struct rtnl_handle *rth, int family,
-		       req_filter_fn_t filter_fn)
-{
-	struct {
-		struct nlmsghdr nlh;
-		struct ndmsg ndm;
-		char buf[256];
-	} req = {
-		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
-		.nlh.nlmsg_type = RTM_GETNEIGH,
-		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-		.nlh.nlmsg_seq = rth->dump = ++rth->seq,
-		.ndm.ndm_family = family,
-	};
-
-	if (filter_fn) {
-		int err;
-
-		err = filter_fn(&req.nlh, sizeof(req));
-		if (err)
-			return err;
-	}
-
-	return send(rth->fd, &req, sizeof(req), 0);
-}
-
-int rtnl_neightbldump_req(struct rtnl_handle *rth, int family)
-{
-	struct {
-		struct nlmsghdr nlh;
-		struct ndtmsg ndtmsg;
-	} req = {
-		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndtmsg)),
-		.nlh.nlmsg_type = RTM_GETNEIGHTBL,
-		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-		.nlh.nlmsg_seq = rth->dump = ++rth->seq,
-		.ndtmsg.ndtm_family = family,
-	};
-
-	return send(rth->fd, &req, sizeof(req), 0);
-}
-
-int rtnl_mdbdump_req(struct rtnl_handle *rth, int family)
-{
-	struct {
-		struct nlmsghdr nlh;
-		struct br_port_msg bpm;
-	} req = {
-		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
-		.nlh.nlmsg_type = RTM_GETMDB,
-		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-		.nlh.nlmsg_seq = rth->dump = ++rth->seq,
-		.bpm.family = family,
-	};
-
-	return send(rth->fd, &req, sizeof(req), 0);
-}
-
-int rtnl_netconfdump_req(struct rtnl_handle *rth, int family)
-{
-	struct {
-		struct nlmsghdr nlh;
-		struct netconfmsg ncm;
-		char buf[0] __aligned(NLMSG_ALIGNTO);
-	} req = {
-		.nlh.nlmsg_len = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct netconfmsg))),
-		.nlh.nlmsg_type = RTM_GETNETCONF,
-		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-		.nlh.nlmsg_seq = rth->dump = ++rth->seq,
-		.ncm.ncm_family = family,
-	};
-
-	return send(rth->fd, &req, sizeof(req), 0);
-}
-
-int rtnl_nsiddump_req_filter_fn(struct rtnl_handle *rth, int family,
-				req_filter_fn_t filter_fn)
-{
-	struct {
-		struct nlmsghdr nlh;
-		struct rtgenmsg rtm;
-		char buf[1024];
-	} req = {
-		.nlh.nlmsg_len = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct rtgenmsg))),
-		.nlh.nlmsg_type = RTM_GETNSID,
-		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-		.nlh.nlmsg_seq = rth->dump = ++rth->seq,
-		.rtm.rtgen_family = family,
-	};
-	int err;
-
-	if (!filter_fn)
-		return -EINVAL;
-
-	err = filter_fn(&req.nlh, sizeof(req));
-	if (err)
-		return err;
-
-	return send(rth->fd, &req, req.nlh.nlmsg_len, 0);
-}
-
-static int __rtnl_linkdump_req(struct rtnl_handle *rth, int family)
-{
-	struct {
-		struct nlmsghdr nlh;
-		struct ifinfomsg ifm;
-	} req = {
-		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-		.nlh.nlmsg_type = RTM_GETLINK,
-		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-		.nlh.nlmsg_seq = rth->dump = ++rth->seq,
-		.ifm.ifi_family = family,
-	};
-
-	return send(rth->fd, &req, sizeof(req), 0);
-}
-
-int rtnl_linkdump_req(struct rtnl_handle *rth, int family)
-{
-	if (family == AF_UNSPEC)
-		return rtnl_linkdump_req_filter(rth, family, RTEXT_FILTER_VF);
-
-	return __rtnl_linkdump_req(rth, family);
-}
-
-int rtnl_linkdump_req_filter(struct rtnl_handle *rth, int family,
+int rtnl_wilddump_req_filter(struct rtnl_handle *rth, int family, int type,
 			    __u32 filt_mask)
 {
-	if (family == AF_UNSPEC || family == AF_BRIDGE) {
-		struct {
-			struct nlmsghdr nlh;
-			struct ifinfomsg ifm;
-			/* attribute has to be NLMSG aligned */
-			struct rtattr ext_req __aligned(NLMSG_ALIGNTO);
-			__u32 ext_filter_mask;
-		} req = {
-			.nlh.nlmsg_len = sizeof(req),
-			.nlh.nlmsg_type = RTM_GETLINK,
-			.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-			.nlh.nlmsg_seq = rth->dump = ++rth->seq,
-			.ifm.ifi_family = family,
-			.ext_req.rta_type = IFLA_EXT_MASK,
-			.ext_req.rta_len = RTA_LENGTH(sizeof(__u32)),
-			.ext_filter_mask = filt_mask,
-		};
-
-		return send(rth->fd, &req, sizeof(req), 0);
-	}
-
-	return __rtnl_linkdump_req(rth, family);
-}
-
-int rtnl_linkdump_req_filter_fn(struct rtnl_handle *rth, int family,
-				req_filter_fn_t filter_fn)
-{
-	if (family == AF_UNSPEC || family == AF_PACKET) {
-		struct {
-			struct nlmsghdr nlh;
-			struct ifinfomsg ifm;
-			char buf[1024];
-		} req = {
-			.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-			.nlh.nlmsg_type = RTM_GETLINK,
-			.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-			.nlh.nlmsg_seq = rth->dump = ++rth->seq,
-			.ifm.ifi_family = family,
-		};
-		int err;
-
-		if (!filter_fn)
-			return -EINVAL;
-
-		err = filter_fn(&req.nlh, sizeof(req));
-		if (err)
-			return err;
-
-		return send(rth->fd, &req, req.nlh.nlmsg_len, 0);
-	}
-
-	return __rtnl_linkdump_req(rth, family);
-}
-
-int rtnl_fdb_linkdump_req_filter_fn(struct rtnl_handle *rth,
-				    req_filter_fn_t filter_fn)
-{
 	struct {
 		struct nlmsghdr nlh;
 		struct ifinfomsg ifm;
-		char buf[128];
-	} req = {
-		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-		.nlh.nlmsg_type = RTM_GETNEIGH,
-		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-		.nlh.nlmsg_seq = rth->dump = ++rth->seq,
-		.ifm.ifi_family = PF_BRIDGE,
-	};
-	int err;
-
-	err = filter_fn(&req.nlh, sizeof(req));
-	if (err)
-		return err;
-
-	return send(rth->fd, &req, sizeof(req), 0);
-}
-
-int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask)
-{
-	struct {
-		struct nlmsghdr nlh;
-		struct if_stats_msg ifsm;
+		/* attribute has to be NLMSG aligned */
+		struct rtattr ext_req __attribute__ ((aligned(NLMSG_ALIGNTO)));
+		__u32 ext_filter_mask;
 	} req;
 
 	memset(&req, 0, sizeof(req));
-	req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct if_stats_msg));
-	req.nlh.nlmsg_type = RTM_GETSTATS;
+	req.nlh.nlmsg_len = sizeof(req);
+	req.nlh.nlmsg_type = type;
 	req.nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST;
 	req.nlh.nlmsg_pid = 0;
 	req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
-	req.ifsm.family = fam;
-	req.ifsm.filter_mask = filt_mask;
+	req.ifm.ifi_family = family;
 
-	return send(rth->fd, &req, sizeof(req), 0);
+	req.ext_req.rta_type = IFLA_EXT_MASK;
+	req.ext_req.rta_len = RTA_LENGTH(sizeof(__u32));
+	req.ext_filter_mask = filt_mask;
+
+	return send(rth->fd, (void*)&req, sizeof(req), 0);
 }
 
 int rtnl_send(struct rtnl_handle *rth, const void *buf, int len)
@@ -613,11 +155,10 @@
 	for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, status);
 	     h = NLMSG_NEXT(h, status)) {
 		if (h->nlmsg_type == NLMSG_ERROR) {
-			struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
-
+			struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
 			if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
 				fprintf(stderr, "ERROR truncated\n");
-			else
+			else 
 				errno = -err->error;
 			return -1;
 		}
@@ -628,12 +169,7 @@
 
 int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
 {
-	struct nlmsghdr nlh = {
-		.nlmsg_len = NLMSG_LENGTH(len),
-		.nlmsg_type = type,
-		.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-		.nlmsg_seq = rth->dump = ++rth->seq,
-	};
+	struct nlmsghdr nlh;
 	struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
 	struct iovec iov[2] = {
 		{ .iov_base = &nlh, .iov_len = sizeof(nlh) },
@@ -641,11 +177,17 @@
 	};
 	struct msghdr msg = {
 		.msg_name = &nladdr,
-		.msg_namelen = sizeof(nladdr),
+		.msg_namelen = 	sizeof(nladdr),
 		.msg_iov = iov,
 		.msg_iovlen = 2,
 	};
 
+	nlh.nlmsg_len = NLMSG_LENGTH(len);
+	nlh.nlmsg_type = type;
+	nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST;
+	nlh.nlmsg_pid = 0;
+	nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
 	return sendmsg(rth->fd, &msg, 0);
 }
 
@@ -653,7 +195,7 @@
 {
 	struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
 	struct iovec iov = {
-		.iov_base = n,
+		.iov_base = (void*) n,
 		.iov_len = n->nlmsg_len
 	};
 	struct msghdr msg = {
@@ -670,123 +212,8 @@
 	return sendmsg(rth->fd, &msg, 0);
 }
 
-static int rtnl_dump_done(struct nlmsghdr *h)
-{
-	int len = *(int *)NLMSG_DATA(h);
-
-	if (h->nlmsg_len < NLMSG_LENGTH(sizeof(int))) {
-		fprintf(stderr, "DONE truncated\n");
-		return -1;
-	}
-
-	if (len < 0) {
-		/* check for any messages returned from kernel */
-		if (nl_dump_ext_ack_done(h, len))
-			return len;
-
-		errno = -len;
-		switch (errno) {
-		case ENOENT:
-		case EOPNOTSUPP:
-			return -1;
-		case EMSGSIZE:
-			fprintf(stderr,
-				"Error: Buffer too small for object.\n");
-			break;
-		default:
-			perror("RTNETLINK answers");
-		}
-		return len;
-	}
-
-	/* check for any messages returned from kernel */
-	nl_dump_ext_ack(h, NULL);
-
-	return 0;
-}
-
-static void rtnl_dump_error(const struct rtnl_handle *rth,
-			    struct nlmsghdr *h)
-{
-
-	if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
-		fprintf(stderr, "ERROR truncated\n");
-	} else {
-		const struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
-
-		errno = -err->error;
-		if (rth->proto == NETLINK_SOCK_DIAG &&
-		    (errno == ENOENT ||
-		     errno == EOPNOTSUPP))
-			return;
-
-		if (!(rth->flags & RTNL_HANDLE_F_SUPPRESS_NLERR))
-			perror("RTNETLINK answers");
-	}
-}
-
-static int __rtnl_recvmsg(int fd, struct msghdr *msg, int flags)
-{
-	int len;
-
-	do {
-		len = recvmsg(fd, msg, flags);
-	} while (len < 0 && (errno == EINTR || errno == EAGAIN));
-
-	if (len < 0) {
-		fprintf(stderr, "netlink receive error %s (%d)\n",
-			strerror(errno), errno);
-		return -errno;
-	}
-
-	if (len == 0) {
-		fprintf(stderr, "EOF on netlink\n");
-		return -ENODATA;
-	}
-
-	return len;
-}
-
-static int rtnl_recvmsg(int fd, struct msghdr *msg, char **answer)
-{
-	struct iovec *iov = msg->msg_iov;
-	char *buf;
-	int len;
-
-	iov->iov_base = NULL;
-	iov->iov_len = 0;
-
-	len = __rtnl_recvmsg(fd, msg, MSG_PEEK | MSG_TRUNC);
-	if (len < 0)
-		return len;
-
-	if (len < 32768)
-		len = 32768;
-	buf = malloc(len);
-	if (!buf) {
-		fprintf(stderr, "malloc error: not enough buffer\n");
-		return -ENOMEM;
-	}
-
-	iov->iov_base = buf;
-	iov->iov_len = len;
-
-	len = __rtnl_recvmsg(fd, msg, 0);
-	if (len < 0) {
-		free(buf);
-		return len;
-	}
-
-	if (answer)
-		*answer = buf;
-	else
-		free(buf);
-
-	return len;
-}
-
-static int rtnl_dump_filter_l(struct rtnl_handle *rth,
-			      const struct rtnl_dump_filter_arg *arg)
+int rtnl_dump_filter_l(struct rtnl_handle *rth,
+		       const struct rtnl_dump_filter_arg *arg)
 {
 	struct sockaddr_nl nladdr;
 	struct iovec iov;
@@ -796,25 +223,37 @@
 		.msg_iov = &iov,
 		.msg_iovlen = 1,
 	};
-	char *buf;
+	char buf[16384];
 	int dump_intr = 0;
 
+	iov.iov_base = buf;
 	while (1) {
 		int status;
 		const struct rtnl_dump_filter_arg *a;
 		int found_done = 0;
 		int msglen = 0;
 
-		status = rtnl_recvmsg(rth->fd, &msg, &buf);
-		if (status < 0)
-			return status;
+		iov.iov_len = sizeof(buf);
+		status = recvmsg(rth->fd, &msg, 0);
+
+		if (status < 0) {
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			fprintf(stderr, "netlink receive error %s (%d)\n",
+				strerror(errno), errno);
+			return -1;
+		}
+
+		if (status == 0) {
+			fprintf(stderr, "EOF on netlink\n");
+			return -1;
+		}
 
 		if (rth->dump_fp)
 			fwrite(buf, 1, NLMSG_ALIGN(status), rth->dump_fp);
 
 		for (a = arg; a->filter; a++) {
-			struct nlmsghdr *h = (struct nlmsghdr *)buf;
-
+			struct nlmsghdr *h = (struct nlmsghdr*)buf;
 			msglen = status;
 
 			while (NLMSG_OK(h, msglen)) {
@@ -831,35 +270,36 @@
 					dump_intr = 1;
 
 				if (h->nlmsg_type == NLMSG_DONE) {
-					err = rtnl_dump_done(h);
-					if (err < 0) {
-						free(buf);
-						return -1;
-					}
-
 					found_done = 1;
 					break; /* process next filter */
 				}
-
 				if (h->nlmsg_type == NLMSG_ERROR) {
-					rtnl_dump_error(rth, h);
-					free(buf);
+					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 (rth->proto == NETLINK_SOCK_DIAG &&
+						    (errno == ENOENT ||
+						     errno == EOPNOTSUPP))
+							return -1;
+
+						perror("RTNETLINK answers");
+					}
 					return -1;
 				}
 
 				if (!rth->dump_fp) {
-					err = a->filter(h, a->arg1);
-					if (err < 0) {
-						free(buf);
+					err = a->filter(&nladdr, h, a->arg1);
+					if (err < 0)
 						return err;
-					}
 				}
 
 skip_it:
 				h = NLMSG_NEXT(h, msglen);
 			}
 		}
-		free(buf);
 
 		if (found_done) {
 			if (dump_intr)
@@ -891,40 +331,32 @@
 	return rtnl_dump_filter_l(rth, a);
 }
 
-static void rtnl_talk_error(struct nlmsghdr *h, struct nlmsgerr *err,
-			    nl_ext_ack_fn_t errfn)
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
+	      struct nlmsghdr *answer, size_t maxlen)
 {
-	if (nl_dump_ext_ack(h, errfn))
-		return;
-
-	fprintf(stderr, "RTNETLINK answers: %s\n",
-		strerror(-err->error));
-}
-
-
-static int __rtnl_talk_iov(struct rtnl_handle *rtnl, struct iovec *iov,
-			   size_t iovlen, struct nlmsghdr **answer,
-			   bool show_rtnl_err, nl_ext_ack_fn_t errfn)
-{
-	struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
-	struct iovec riov;
+	int status;
+	unsigned seq;
+	struct nlmsghdr *h;
+	struct sockaddr_nl nladdr;
+	struct iovec iov = {
+		.iov_base = (void*) n,
+		.iov_len = n->nlmsg_len
+	};
 	struct msghdr msg = {
 		.msg_name = &nladdr,
 		.msg_namelen = sizeof(nladdr),
-		.msg_iov = iov,
-		.msg_iovlen = iovlen,
+		.msg_iov = &iov,
+		.msg_iovlen = 1,
 	};
-	unsigned int seq = 0;
-	struct nlmsghdr *h;
-	int i, status;
-	char *buf;
+	char   buf[32768];
 
-	for (i = 0; i < iovlen; i++) {
-		h = iov[i].iov_base;
-		h->nlmsg_seq = seq = ++rtnl->seq;
-		if (answer == NULL)
-			h->nlmsg_flags |= NLM_F_ACK;
-	}
+	memset(&nladdr, 0, sizeof(nladdr));
+	nladdr.nl_family = AF_NETLINK;
+
+	n->nlmsg_seq = seq = ++rtnl->seq;
+
+	if (answer == NULL)
+		n->nlmsg_flags |= NLM_F_ACK;
 
 	status = sendmsg(rtnl->fd, &msg, 0);
 	if (status < 0) {
@@ -932,91 +364,80 @@
 		return -1;
 	}
 
-	/* change msg to use the response iov */
-	msg.msg_iov = &riov;
-	msg.msg_iovlen = 1;
-	i = 0;
+	memset(buf,0,sizeof(buf));
+
+	iov.iov_base = buf;
 	while (1) {
-next:
-		status = rtnl_recvmsg(rtnl->fd, &msg, &buf);
-		++i;
+		iov.iov_len = sizeof(buf);
+		status = recvmsg(rtnl->fd, &msg, 0);
 
-		if (status < 0)
-			return status;
-
+		if (status < 0) {
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			fprintf(stderr, "netlink receive error %s (%d)\n",
+				strerror(errno), errno);
+			return -1;
+		}
+		if (status == 0) {
+			fprintf(stderr, "EOF on netlink\n");
+			return -1;
+		}
 		if (msg.msg_namelen != sizeof(nladdr)) {
-			fprintf(stderr,
-				"sender address length == %d\n",
-				msg.msg_namelen);
+			fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
 			exit(1);
 		}
-		for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) {
+		for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
 			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");
-					free(buf);
 					return -1;
 				}
-				fprintf(stderr,
-					"!!!malformed message: len=%d\n",
-					len);
+				fprintf(stderr, "!!!malformed message: len=%d\n", len);
 				exit(1);
 			}
 
 			if (nladdr.nl_pid != 0 ||
 			    h->nlmsg_pid != rtnl->local.nl_pid ||
-			    h->nlmsg_seq > seq || h->nlmsg_seq < seq - iovlen) {
+			    h->nlmsg_seq != seq) {
 				/* Don't forget to skip that message. */
 				status -= NLMSG_ALIGN(len);
-				h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
+				h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
 				continue;
 			}
 
 			if (h->nlmsg_type == NLMSG_ERROR) {
-				struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
-				int error = err->error;
-
+				struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
 				if (l < sizeof(struct nlmsgerr)) {
 					fprintf(stderr, "ERROR truncated\n");
-					free(buf);
-					return -1;
+				} else if (!err->error) {
+					if (answer)
+						memcpy(answer, h,
+						       MIN(maxlen, h->nlmsg_len));
+					return 0;
 				}
 
-				if (!error) {
-					/* check messages from kernel */
-					nl_dump_ext_ack(h, errfn);
-				} else {
-					errno = -error;
-
-					if (rtnl->proto != NETLINK_SOCK_DIAG &&
-					    show_rtnl_err)
-						rtnl_talk_error(h, err, errfn);
-				}
-
-				if (answer)
-					*answer = (struct nlmsghdr *)buf;
-				else
-					free(buf);
-
-				if (i < iovlen)
-					goto next;
-				return error ? -i : 0;
+				if (rtnl->proto != NETLINK_SOCK_DIAG)
+					fprintf(stderr,
+						"RTNETLINK answers: %s\n",
+						strerror(-err->error));
+				errno = -err->error;
+				return -1;
 			}
 
 			if (answer) {
-				*answer = (struct nlmsghdr *)buf;
+				memcpy(answer, h,
+				       MIN(maxlen, h->nlmsg_len));
 				return 0;
 			}
 
 			fprintf(stderr, "Unexpected reply!!!\n");
 
 			status -= NLMSG_ALIGN(len);
-			h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
+			h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
 		}
-		free(buf);