Merge tag 'libnl3_5_0' into HEAD

3.5.0 release

Bug: 150744338
Change-Id: Ib8e350897499ec1acc90b6ed15f6bc3c4efbc906
diff --git a/.gitignore b/.gitignore
index 5f5f1cc..c22a603 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@
 defs.h.in
 defs.h.in~
 /lib/stamp-h1
+test-suite.log
 
 /libnl-1.pc
 /lib/defs.h
@@ -24,4 +25,3 @@
 /configure
 /libtool
 /*.pc
-/libnl.sym
diff --git a/.indent.pro b/.indent.pro
new file mode 100644
index 0000000..ee5d177
--- /dev/null
+++ b/.indent.pro
@@ -0,0 +1 @@
+-linux -il0 -cs -lp -cbi0
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..efc8ae5
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,8 @@
+language: c
+compiler:
+  - gcc
+  - clang
+
+before_install:
+
+script: ./.travis/run.sh
diff --git a/.travis/run.sh b/.travis/run.sh
new file mode 100755
index 0000000..afa1702
--- /dev/null
+++ b/.travis/run.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+
+CFLAGS="-Werror -Wall -Wdeclaration-after-statement -Wvla"
+
+if [ "$CC" = "clang" ]; then
+	CFLAGS="$CFLAGS -Wno-error=unused-command-line-argument -Wno-error=unused-function"
+fi
+
+CFLAGS="$CFLAGS -DNL_MORE_ASSERTS=1000"
+
+export CFLAGS
+./autogen.sh
+./configure
+make -j 5
+make -j 5 check
diff --git a/Android.bp b/Android.bp
index 0f6aec6..892f3f1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11,39 +11,28 @@
         darwin: {
             enabled: false,
         },
+        host: {
+            local_include_dirs: [
+                "include/linux-private",
+            ],
+        },
     },
 
     srcs: [
-        "lib/cache.c",
-        "lib/data.c",
-        "lib/nl.c",
-        "lib/cache_mngr.c",
-        "lib/addr.c",
-        "lib/socket.c",
+        "lib/*.c",
         "lib/fib_lookup/lookup.c",
         "lib/fib_lookup/request.c",
-        "lib/msg.c",
-        "lib/object.c",
-        "lib/attr.c",
-        "lib/utils.c",
-        "lib/cache_mngt.c",
-        "lib/handlers.c",
         "lib/genl/ctrl.c",
-        "lib/genl/mngt.c",
         "lib/genl/family.c",
         "lib/genl/genl.c",
-        "lib/route/rtnl.c",
-        "lib/route/route_utils.c",
+        "lib/genl/mngt.c",
         "lib/netfilter/nfnl.c",
-        "lib/error.c",
-        "lib/version.c",
-        "lib/hash.c",
-        "lib/hashtable.c",
+        "lib/route/route_utils.c",
+        "lib/route/rtnl.c",
     ],
 
     local_include_dirs: [
         "include",
-        "include/linux-private",
     ],
     export_include_dirs: ["include"],
     cflags: [
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..d24842f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+COPYING
\ No newline at end of file
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d40785c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,18 @@
+name: "libnl"
+description: "Netlink Library Suite"
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://github.com/thom311/libnl"
+  }
+  url {
+    type: GIT
+    value: "https://github.com/thom311/libnl.git"
+  }
+  version: "libnl3_5_0"
+  last_upgrade_date {
+    year: 2020
+    month: 3
+    day: 10
+  }
+}
diff --git a/Makefile.am b/Makefile.am
index bc4266d..b2e8737 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,21 +2,1022 @@
 
 ACLOCAL_AMFLAGS = -I m4
 
-SUBDIRS = include lib man python tests
+lib_LTLIBRARIES =
+noinst_LTLIBRARIES =
+check_LTLIBRARIES =
 
-pkgconfig_DATA = libnl-3.0.pc \
-		 libnl-route-3.0.pc \
-		 libnl-genl-3.0.pc \
-		 libnl-nf-3.0.pc
+check_PROGRAMS =
+check_programs =
+
+bin_PROGRAMS =
+sbin_PROGRAMS =
+noinst_PROGRAMS =
+
+TESTS =
+
+CLEANFILES =
+EXTRA_DIST =
+
+DISTCHECK_CONFIGURE_FLAGS = \
+	--disable-dependency-tracking
+
+pkgconfig_DATA = \
+	libnl-3.0.pc \
+	libnl-genl-3.0.pc \
+	libnl-idiag-3.0.pc \
+	libnl-nf-3.0.pc \
+	libnl-route-3.0.pc \
+	libnl-xfrm-3.0.pc \
+	$(NULL)
+
+warn_cppflags = \
+	-Wall \
+	-Wextra \
+	-Wmissing-prototypes \
+	-Wno-unused-parameter \
+	-Wno-sign-compare \
+	-Wno-missing-field-initializers \
+	-Wpointer-arith \
+	$(NULL)
+
+###############################################################################
+
+libnlincludedir = $(includedir)/libnl@MAJ_VERSION@
+
+libnlinclude_netlinkdir = $(libnlincludedir)/netlink
+libnlinclude_netlink_HEADERS = \
+	include/netlink/addr.h \
+	include/netlink/attr.h \
+	include/netlink/cache-api.h \
+	include/netlink/cache.h \
+	include/netlink/data.h \
+	include/netlink/errno.h \
+	include/netlink/handlers.h \
+	include/netlink/hash.h \
+	include/netlink/hashtable.h \
+	include/netlink/list.h \
+	include/netlink/msg.h \
+	include/netlink/netlink-compat.h \
+	include/netlink/netlink-kernel.h \
+	include/netlink/netlink.h \
+	include/netlink/object-api.h \
+	include/netlink/object.h \
+	include/netlink/socket.h \
+	include/netlink/types.h \
+	include/netlink/utils.h \
+	include/netlink/version.h \
+	$(NULL)
+libnlinclude_netlink_fib_lookupdir = $(libnlincludedir)/netlink/fib_lookup
+libnlinclude_netlink_fib_lookup_HEADERS = \
+	include/netlink/fib_lookup/lookup.h \
+	include/netlink/fib_lookup/request.h \
+	$(NULL)
+libnlinclude_netlink_genldir = $(libnlincludedir)/netlink/genl
+libnlinclude_netlink_genl_HEADERS = \
+	include/netlink/genl/ctrl.h \
+	include/netlink/genl/family.h \
+	include/netlink/genl/genl.h \
+	include/netlink/genl/mngt.h \
+	$(NULL)
+libnlinclude_netlink_idiagdir = $(libnlincludedir)/netlink/idiag
+libnlinclude_netlink_idiag_HEADERS = \
+	include/netlink/idiag/idiagnl.h \
+	include/netlink/idiag/meminfo.h \
+	include/netlink/idiag/msg.h \
+	include/netlink/idiag/req.h \
+	include/netlink/idiag/vegasinfo.h \
+	$(NULL)
+libnlinclude_netlink_netfilterdir = $(libnlincludedir)/netlink/netfilter
+libnlinclude_netlink_netfilter_HEADERS = \
+	include/netlink/netfilter/ct.h \
+	include/netlink/netfilter/exp.h \
+	include/netlink/netfilter/log.h \
+	include/netlink/netfilter/log_msg.h \
+	include/netlink/netfilter/netfilter.h \
+	include/netlink/netfilter/nfnl.h \
+	include/netlink/netfilter/queue.h \
+	include/netlink/netfilter/queue_msg.h \
+	$(NULL)
+libnlinclude_netlink_routedir = $(libnlincludedir)/netlink/route
+libnlinclude_netlink_route_HEADERS = \
+	include/netlink/route/action.h \
+	include/netlink/route/addr.h \
+	include/netlink/route/class.h \
+	include/netlink/route/classifier.h \
+	include/netlink/route/link.h \
+	include/netlink/route/neighbour.h \
+	include/netlink/route/neightbl.h \
+	include/netlink/route/netconf.h \
+	include/netlink/route/nexthop.h \
+	include/netlink/route/pktloc.h \
+	include/netlink/route/qdisc.h \
+	include/netlink/route/route.h \
+	include/netlink/route/rtnl.h \
+	include/netlink/route/rule.h \
+	include/netlink/route/tc-api.h \
+	include/netlink/route/tc.h \
+	$(NULL)
+libnlinclude_netlink_route_actdir = $(libnlincludedir)/netlink/route/act
+libnlinclude_netlink_route_act_HEADERS = \
+	include/netlink/route/act/gact.h \
+	include/netlink/route/act/mirred.h \
+	include/netlink/route/act/skbedit.h \
+	include/netlink/route/act/vlan.h \
+	$(NULL)
+libnlinclude_netlink_route_clsdir = $(libnlincludedir)/netlink/route/cls
+libnlinclude_netlink_route_cls_HEADERS = \
+	include/netlink/route/cls/basic.h \
+	include/netlink/route/cls/cgroup.h \
+	include/netlink/route/cls/ematch.h \
+	include/netlink/route/cls/fw.h \
+	include/netlink/route/cls/matchall.h \
+	include/netlink/route/cls/police.h \
+	include/netlink/route/cls/u32.h \
+	$(NULL)
+libnlinclude_netlink_route_cls_ematchdir = $(libnlincludedir)/netlink/route/cls/ematch
+libnlinclude_netlink_route_cls_ematch_HEADERS = \
+	include/netlink/route/cls/ematch/cmp.h \
+	include/netlink/route/cls/ematch/meta.h \
+	include/netlink/route/cls/ematch/nbyte.h \
+	include/netlink/route/cls/ematch/text.h \
+	$(NULL)
+libnlinclude_netlink_route_linkdir = $(libnlincludedir)/netlink/route/link
+libnlinclude_netlink_route_link_HEADERS = \
+	include/netlink/route/link/api.h \
+	include/netlink/route/link/bonding.h \
+	include/netlink/route/link/bridge.h \
+	include/netlink/route/link/can.h \
+	include/netlink/route/link/geneve.h \
+	include/netlink/route/link/inet.h \
+	include/netlink/route/link/inet6.h \
+	include/netlink/route/link/info-api.h \
+	include/netlink/route/link/ip6tnl.h \
+	include/netlink/route/link/ipgre.h \
+	include/netlink/route/link/ipip.h \
+	include/netlink/route/link/ipvlan.h \
+	include/netlink/route/link/ipvti.h \
+	include/netlink/route/link/macsec.h \
+	include/netlink/route/link/macvlan.h \
+	include/netlink/route/link/macvtap.h \
+	include/netlink/route/link/ppp.h \
+	include/netlink/route/link/sit.h \
+	include/netlink/route/link/sriov.h \
+	include/netlink/route/link/veth.h \
+	include/netlink/route/link/vlan.h \
+	include/netlink/route/link/vrf.h \
+	include/netlink/route/link/vxlan.h \
+	include/netlink/route/link/xfrmi.h \
+	$(NULL)
+libnlinclude_netlink_route_qdiscdir = $(libnlincludedir)/netlink/route/qdisc
+libnlinclude_netlink_route_qdisc_HEADERS = \
+	include/netlink/route/qdisc/cbq.h \
+	include/netlink/route/qdisc/dsmark.h \
+	include/netlink/route/qdisc/fifo.h \
+	include/netlink/route/qdisc/fq_codel.h \
+	include/netlink/route/qdisc/hfsc.h \
+	include/netlink/route/qdisc/htb.h \
+	include/netlink/route/qdisc/mqprio.h \
+	include/netlink/route/qdisc/netem.h \
+	include/netlink/route/qdisc/plug.h \
+	include/netlink/route/qdisc/prio.h \
+	include/netlink/route/qdisc/red.h \
+	include/netlink/route/qdisc/sfq.h \
+	include/netlink/route/qdisc/tbf.h \
+	$(NULL)
+libnlinclude_netlink_xfrmdir = $(libnlincludedir)/netlink/xfrm
+libnlinclude_netlink_xfrm_HEADERS = \
+	include/netlink/xfrm/ae.h \
+	include/netlink/xfrm/lifetime.h \
+	include/netlink/xfrm/sa.h \
+	include/netlink/xfrm/selector.h \
+	include/netlink/xfrm/sp.h \
+	include/netlink/xfrm/template.h \
+	$(NULL)
 
 if ENABLE_CLI
-SUBDIRS += src
+libnlinclude_netlink_clidir = $(libnlincludedir)/netlink/cli
+libnlinclude_netlink_cli_HEADERS = \
+	include/netlink/cli/addr.h \
+	include/netlink/cli/class.h \
+	include/netlink/cli/cls.h \
+	include/netlink/cli/ct.h \
+	include/netlink/cli/exp.h \
+	include/netlink/cli/link.h \
+	include/netlink/cli/neigh.h \
+	include/netlink/cli/qdisc.h \
+	include/netlink/cli/route.h \
+	include/netlink/cli/rule.h \
+	include/netlink/cli/tc.h \
+	include/netlink/cli/utils.h \
+	$(NULL)
+endif
+
+noinst_HEADERS = \
+	include/linux-private/linux/can/netlink.h \
+	include/linux-private/linux/fib_rules.h \
+	include/linux-private/linux/gen_stats.h \
+	include/linux-private/linux/genetlink.h \
+	include/linux-private/linux/if.h \
+	include/linux-private/linux/if_addr.h \
+	include/linux-private/linux/if_arp.h \
+	include/linux-private/linux/if_bridge.h \
+	include/linux-private/linux/if_ether.h \
+	include/linux-private/linux/if_link.h \
+	include/linux-private/linux/if_macsec.h \
+	include/linux-private/linux/if_tunnel.h \
+	include/linux-private/linux/if_vlan.h \
+	include/linux-private/linux/in.h \
+	include/linux-private/linux/in6.h \
+	include/linux-private/linux/inet_diag.h \
+	include/linux-private/linux/ip.h \
+	include/linux-private/linux/ipv6.h \
+	include/linux-private/linux/libc-compat.h \
+	include/linux-private/linux/lwtunnel.h \
+	include/linux-private/linux/mpls.h \
+	include/linux-private/linux/mpls_iptunnel.h \
+	include/linux-private/linux/neighbour.h \
+	include/linux-private/linux/netconf.h \
+	include/linux-private/linux/netfilter.h \
+	include/linux-private/linux/netfilter/nf_conntrack_common.h \
+	include/linux-private/linux/netfilter/nfnetlink.h \
+	include/linux-private/linux/netfilter/nfnetlink_compat.h \
+	include/linux-private/linux/netfilter/nfnetlink_conntrack.h \
+	include/linux-private/linux/netfilter/nfnetlink_log.h \
+	include/linux-private/linux/netfilter/nfnetlink_queue.h \
+	include/linux-private/linux/netlink.h \
+	include/linux-private/linux/pkt_cls.h \
+	include/linux-private/linux/pkt_sched.h \
+	include/linux-private/linux/rtnetlink.h \
+	include/linux-private/linux/snmp.h \
+	include/linux-private/linux/sock_diag.h \
+	include/linux-private/linux/socket.h \
+	include/linux-private/linux/tc_act/tc_gact.h \
+	include/linux-private/linux/tc_act/tc_mirred.h \
+	include/linux-private/linux/tc_act/tc_skbedit.h \
+	include/linux-private/linux/tc_act/tc_vlan.h \
+	include/linux-private/linux/tc_ematch/tc_em_meta.h \
+	include/linux-private/linux/veth.h \
+	include/linux-private/linux/xfrm.h \
+	include/netlink-private/cache-api.h \
+	include/netlink-private/genl.h \
+	include/netlink-private/netlink.h \
+	include/netlink-private/object-api.h \
+	include/netlink-private/route/link/api.h \
+	include/netlink-private/route/link/sriov.h \
+	include/netlink-private/route/mpls.h \
+	include/netlink-private/route/nexthop-encap.h \
+	include/netlink-private/route/tc-api.h \
+	include/netlink-private/socket.h \
+	include/netlink-private/tc.h \
+	include/netlink-private/types.h \
+	include/netlink-private/utils.h \
+	$(NULL)
+
+###############################################################################
+
+# Hack to avoid using ylwrap. It does not function correctly in combination
+# with --header-file=
+
+lib/route/pktloc_grammar.h: lib/route/pktloc_grammar.c
+	@true
+
+lib/route/pktloc_grammar.c: lib/route/pktloc_grammar.l lib/route/.dirstamp
+	$(AM_V_GEN) $(FLEX) --header-file=lib/route/pktloc_grammar.h $(LFLAGS) -o $@ $<
+
+lib/route/pktloc_syntax.h: lib/route/pktloc_syntax.c
+	@true
+
+lib/route/pktloc_syntax.c: lib/route/pktloc_syntax.y lib/route/.dirstamp
+	$(AM_V_GEN) $(YACC) -d $(YFLAGS) -o $@ $<
+
+lib/route/cls/ematch_grammar.h: lib/route/cls/ematch_grammar.c
+	@true
+
+lib/route/cls/ematch_grammar.c: lib/route/cls/ematch_grammar.l lib/route/cls/.dirstamp
+	$(AM_V_GEN) $(FLEX) --header-file=lib/route/cls/ematch_grammar.h $(LFLAGS) -o $@ $<
+
+lib/route/cls/ematch_syntax.h: lib/route/cls/ematch_syntax.c
+	@true
+
+lib/route/cls/ematch_syntax.c: lib/route/cls/ematch_syntax.y lib/route/cls/.dirstamp
+	$(AM_V_GEN) $(YACC) -d $(YFLAGS) -o $@ $<
+
+grammar_files_sources = \
+	lib/route/cls/ematch_grammar.c \
+	lib/route/cls/ematch_syntax.c \
+	lib/route/pktloc_grammar.c \
+	lib/route/pktloc_syntax.c \
+	$(NULL)
+
+grammar_files_headers = $(grammar_files_sources:%.c=%.h)
+
+CLEANFILES += \
+	$(grammar_files_sources) \
+	$(grammar_files_headers)
+
+EXTRA_DIST += \
+	lib/route/cls/ematch_grammar.l \
+	lib/route/cls/ematch_syntax.y \
+	lib/route/pktloc_grammar.l \
+	lib/route/pktloc_syntax.y \
+	$(NULL)
+
+###############################################################################
+
+lib_cppflags = \
+	$(warn_cppflags) \
+	-D_GNU_SOURCE \
+	-DSYSCONFDIR=\"$(sysconfdir)/libnl\" \
+	-I$(srcdir)/include/linux-private \
+	-I$(srcdir)/include \
+	-I$(builddir)/include \
+	-I$(builddir)/lib/route \
+	-I$(builddir)/lib/route/cls
+
+lib_LTLIBRARIES += lib/libnl-3.la
+
+lib_libnl_3_la_SOURCES = \
+	lib/addr.c \
+	lib/attr.c \
+	lib/cache.c \
+	lib/cache_mngr.c \
+	lib/cache_mngt.c \
+	lib/data.c \
+	lib/error.c \
+	lib/handlers.c \
+	lib/hash.c \
+	lib/hashtable.c \
+	lib/mpls.c \
+	lib/msg.c \
+	lib/nl.c \
+	lib/object.c \
+	lib/socket.c \
+	lib/utils.c \
+	lib/version.c \
+	$(NULL)
+EXTRA_lib_libnl_3_la_DEPENDENCIES = \
+	libnl-3.sym
+lib_libnl_3_la_CPPFLAGS = \
+	$(lib_cppflags)
+lib_libnl_3_la_LDFLAGS = \
+	-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
+	-Wl,--version-script=$(srcdir)/libnl-3.sym
+
+lib_LTLIBRARIES += lib/libnl-route-3.la
+
+lib_libnl_route_3_la_SOURCES = \
+	lib/fib_lookup/lookup.c \
+	lib/fib_lookup/request.c \
+	lib/route/act.c \
+	lib/route/act/gact.c \
+	lib/route/act/mirred.c \
+	lib/route/act/skbedit.c \
+	lib/route/act/vlan.c \
+	lib/route/addr.c \
+	lib/route/class.c \
+	lib/route/classid.c \
+	lib/route/cls.c \
+	lib/route/cls/basic.c \
+	lib/route/cls/cgroup.c \
+	lib/route/cls/ematch.c \
+	lib/route/cls/ematch/cmp.c \
+	lib/route/cls/ematch/container.c \
+	lib/route/cls/ematch/meta.c \
+	lib/route/cls/ematch/nbyte.c \
+	lib/route/cls/ematch/text.c \
+	lib/route/cls/fw.c \
+	lib/route/cls/mall.c \
+	lib/route/cls/police.c \
+	lib/route/cls/u32.c \
+	lib/route/link.c \
+	lib/route/link/api.c \
+	lib/route/link/bonding.c \
+	lib/route/link/bridge.c \
+	lib/route/link/can.c \
+	lib/route/link/dummy.c \
+	lib/route/link/geneve.c \
+	lib/route/link/ifb.c \
+	lib/route/link/inet.c \
+	lib/route/link/inet6.c \
+	lib/route/link/ip6tnl.c \
+	lib/route/link/ipgre.c \
+	lib/route/link/ipip.c \
+	lib/route/link/ipvlan.c \
+	lib/route/link/ipvti.c \
+	lib/route/link/macsec.c \
+	lib/route/link/macvlan.c \
+	lib/route/link/ppp.c \
+	lib/route/link/sit.c \
+	lib/route/link/sriov.c \
+	lib/route/link/veth.c \
+	lib/route/link/vlan.c \
+	lib/route/link/vrf.c \
+	lib/route/link/vxlan.c \
+	lib/route/link/xfrmi.c \
+	lib/route/neigh.c \
+	lib/route/neightbl.c \
+	lib/route/netconf.c \
+	lib/route/nexthop.c \
+	lib/route/nexthop_encap.c \
+	lib/route/nh_encap_mpls.c \
+	lib/route/pktloc.c \
+	lib/route/qdisc.c \
+	lib/route/qdisc/blackhole.c \
+	lib/route/qdisc/cbq.c \
+	lib/route/qdisc/dsmark.c \
+	lib/route/qdisc/fifo.c \
+	lib/route/qdisc/fq_codel.c \
+	lib/route/qdisc/hfsc.c \
+	lib/route/qdisc/htb.c \
+	lib/route/qdisc/ingress.c \
+	lib/route/qdisc/mqprio.c \
+	lib/route/qdisc/netem.c \
+	lib/route/qdisc/plug.c \
+	lib/route/qdisc/prio.c \
+	lib/route/qdisc/red.c \
+	lib/route/qdisc/sfq.c \
+	lib/route/qdisc/tbf.c \
+	lib/route/route.c \
+	lib/route/route_obj.c \
+	lib/route/route_utils.c \
+	lib/route/rtnl.c \
+	lib/route/rule.c \
+	lib/route/tc.c \
+	$(NULL)
+nodist_lib_libnl_route_3_la_SOURCES = \
+	$(grammar_files_sources)
+EXTRA_lib_libnl_route_3_la_DEPENDENCIES = \
+	libnl-route-3.sym
+lib_libnl_route_3_la_CPPFLAGS = \
+	$(lib_cppflags)
+lib_libnl_route_3_la_LDFLAGS = \
+	-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
+	-Wl,--version-script=$(srcdir)/libnl-route-3.sym
+lib_libnl_route_3_la_LIBADD = \
+	lib/libnl-3.la
+
+$(lib_libnl_route_3_la_OBJECTS): $(grammar_files_headers)
+
+lib_LTLIBRARIES += lib/libnl-idiag-3.la
+
+lib_libnl_idiag_3_la_SOURCES = \
+	lib/idiag/idiag.c \
+	lib/idiag/idiag_meminfo_obj.c \
+	lib/idiag/idiag_msg_obj.c \
+	lib/idiag/idiag_req_obj.c \
+	lib/idiag/idiag_vegasinfo_obj.c \
+	$(NULL)
+EXTRA_lib_libnl_idiag_3_la_DEPENDENCIES = \
+	libnl-idiag-3.sym
+lib_libnl_idiag_3_la_CPPFLAGS = \
+	$(lib_cppflags)
+lib_libnl_idiag_3_la_LDFLAGS = \
+	-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
+	-Wl,--version-script=$(srcdir)/libnl-idiag-3.sym
+lib_libnl_idiag_3_la_LIBADD = \
+	lib/libnl-3.la
+
+lib_LTLIBRARIES += lib/libnl-genl-3.la
+
+lib_libnl_genl_3_la_SOURCES = \
+	lib/genl/ctrl.c \
+	lib/genl/family.c \
+	lib/genl/genl.c \
+	lib/genl/mngt.c \
+	$(NULL)
+EXTRA_lib_libnl_genl_3_la_DEPENDENCIES = \
+	libnl-genl-3.sym
+lib_libnl_genl_3_la_CPPFLAGS = \
+	$(lib_cppflags)
+lib_libnl_genl_3_la_LDFLAGS = \
+	-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
+	-Wl,--version-script=$(srcdir)/libnl-genl-3.sym
+lib_libnl_genl_3_la_LIBADD = \
+	lib/libnl-3.la
+
+lib_LTLIBRARIES += lib/libnl-nf-3.la
+
+lib_libnl_nf_3_la_SOURCES = \
+	lib/netfilter/ct.c \
+	lib/netfilter/ct_obj.c \
+	lib/netfilter/exp.c \
+	lib/netfilter/exp_obj.c \
+	lib/netfilter/log.c \
+	lib/netfilter/log_msg.c \
+	lib/netfilter/log_msg_obj.c \
+	lib/netfilter/log_obj.c \
+	lib/netfilter/netfilter.c \
+	lib/netfilter/nfnl.c \
+	lib/netfilter/queue.c \
+	lib/netfilter/queue_msg.c \
+	lib/netfilter/queue_msg_obj.c \
+	lib/netfilter/queue_obj.c \
+	$(NULL)
+lib_libnl_nf_3_la_CPPFLAGS = \
+	$(lib_cppflags)
+EXTRA_lib_libnl_nf_3_la_DEPENDENCIES = \
+	libnl-nf-3.sym
+lib_libnl_nf_3_la_LDFLAGS = \
+	-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
+	-Wl,--version-script=$(srcdir)/libnl-nf-3.sym
+lib_libnl_nf_3_la_LIBADD  = \
+	lib/libnl-3.la \
+	lib/libnl-route-3.la
+
+lib_LTLIBRARIES += lib/libnl-xfrm-3.la
+
+lib_libnl_xfrm_3_la_SOURCES = \
+	lib/xfrm/ae.c \
+	lib/xfrm/lifetime.c \
+	lib/xfrm/sa.c \
+	lib/xfrm/selector.c \
+	lib/xfrm/sp.c \
+	lib/xfrm/template.c \
+	$(NULL)
+lib_libnl_xfrm_3_la_CPPFLAGS = \
+	$(lib_cppflags)
+lib_libnl_xfrm_3_la_LDFLAGS = \
+	-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
+	-Wl,--version-script=$(srcdir)/libnl-xfrm-3.sym
+EXTRA_lib_libnl_xfrm_3_la_DEPENDENCIES = \
+	libnl-xfrm-3.sym
+lib_libnl_xfrm_3_la_LIBADD = \
+	lib/libnl-3.la
+
+lib_cli_ltlibraries_cls = \
+	lib/cli/cls/basic.la \
+	lib/cli/cls/cgroup.la \
+	$(NULL)
+lib_cli_ltlibraries_qdisc = \
+	lib/cli/qdisc/bfifo.la \
+	lib/cli/qdisc/blackhole.la \
+	lib/cli/qdisc/fq_codel.la \
+	lib/cli/qdisc/hfsc.la \
+	lib/cli/qdisc/htb.la \
+	lib/cli/qdisc/ingress.la \
+	lib/cli/qdisc/pfifo.la \
+	lib/cli/qdisc/plug.la \
+	$(NULL)
+
+if ENABLE_CLI
+pkglib_clsdir = $(pkglibdir)/cli/cls
+pkglib_qdiscdir = $(pkglibdir)/cli/qdisc
+pkglib_cls_LTLIBRARIES = $(lib_cli_ltlibraries_cls)
+pkglib_qdisc_LTLIBRARIES = $(lib_cli_ltlibraries_qdisc)
+else
+check_LTLIBRARIES += \
+	$(lib_cli_ltlibraries_cls) \
+	$(lib_cli_ltlibraries_qdisc)
+endif
+
+lib_cli_ldflags = \
+	-module -avoid-version
+
+lib_cli_cls_basic_la_CPPFLAGS       = $(lib_cppflags)
+lib_cli_cls_basic_la_LDFLAGS        = $(lib_cli_ldflags)
+lib_cli_cls_cgroup_la_CPPFLAGS      = $(lib_cppflags)
+lib_cli_cls_cgroup_la_LDFLAGS       = $(lib_cli_ldflags)
+lib_cli_qdisc_bfifo_la_CPPFLAGS     = $(lib_cppflags)
+lib_cli_qdisc_bfifo_la_LDFLAGS      = $(lib_cli_ldflags)
+lib_cli_qdisc_blackhole_la_CPPFLAGS = $(lib_cppflags)
+lib_cli_qdisc_blackhole_la_LDFLAGS  = $(lib_cli_ldflags)
+lib_cli_qdisc_fq_codel_la_CPPFLAGS  = $(lib_cppflags)
+lib_cli_qdisc_fq_codel_la_LDFLAGS   = $(lib_cli_ldflags)
+lib_cli_qdisc_hfsc_la_CPPFLAGS      = $(lib_cppflags)
+lib_cli_qdisc_hfsc_la_LDFLAGS       = $(lib_cli_ldflags)
+lib_cli_qdisc_htb_la_CPPFLAGS       = $(lib_cppflags)
+lib_cli_qdisc_htb_la_LDFLAGS        = $(lib_cli_ldflags)
+lib_cli_qdisc_ingress_la_CPPFLAGS   = $(lib_cppflags)
+lib_cli_qdisc_ingress_la_LDFLAGS    = $(lib_cli_ldflags)
+lib_cli_qdisc_pfifo_la_CPPFLAGS     = $(lib_cppflags)
+lib_cli_qdisc_pfifo_la_LDFLAGS      = $(lib_cli_ldflags)
+lib_cli_qdisc_plug_la_CPPFLAGS      = $(lib_cppflags)
+lib_cli_qdisc_plug_la_LDFLAGS       = $(lib_cli_ldflags)
+
+###############################################################################
+
+src_lib_ldflags =
+
+if ENABLE_CLI
+lib_LTLIBRARIES += src/lib/libnl-cli-3.la
+src_lib_ldflags += -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
+else
+check_LTLIBRARIES += src/lib/libnl-cli-3.la
+endif
+
+src_lib_libnl_cli_3_la_SOURCES = \
+	src/lib/addr.c \
+	src/lib/class.c \
+	src/lib/cls.c \
+	src/lib/ct.c \
+	src/lib/exp.c \
+	src/lib/link.c \
+	src/lib/neigh.c \
+	src/lib/qdisc.c \
+	src/lib/route.c \
+	src/lib/rule.c \
+	src/lib/tc.c \
+	src/lib/utils.c \
+	$(NULL)
+EXTRA_src_lib_libnl_cli_3_la_DEPENDENCIES = \
+	libnl-cli-3.sym
+src_lib_libnl_cli_3_la_CPPFLAGS = \
+	$(warn_cppflags) \
+	-D_GNU_SOURCE \
+	-DPKGLIBDIR=\"$(pkglibdir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-I$(srcdir)/include/linux-private \
+	-I$(srcdir)/include \
+	-I$(builddir)/include
+src_lib_libnl_cli_3_la_LDFLAGS = \
+	$(src_lib_ldflags) \
+	-Wl,--version-script=$(srcdir)/libnl-cli-3.sym
+src_lib_libnl_cli_3_la_LIBADD = \
+	lib/libnl-3.la \
+	lib/libnl-route-3.la \
+	lib/libnl-nf-3.la \
+	lib/libnl-genl-3.la \
+	-ldl \
+	$(NULL)
+
+###############################################################################
+
+src_cppflags = \
+	$(warn_cppflags) \
+	-D_GNU_SOURCE \
+	-DSYSCONFDIR=\"$(sysconfdir)/libnl\" \
+	-I$(srcdir)/include/linux-private \
+	-I$(srcdir)/include \
+	-I$(builddir)/include
+
+src_ldadd = \
+	src/lib/libnl-cli-3.la \
+	lib/libnl-3.la \
+	lib/libnl-nf-3.la \
+	lib/libnl-genl-3.la \
+	lib/libnl-route-3.la \
+	lib/libnl-idiag-3.la \
+	$(NULL)
+
+cli_programs = \
+	src/genl-ctrl-list \
+	src/idiag-socket-details \
+	src/nf-ct-add \
+	src/nf-ct-events \
+	src/nf-ct-list \
+	src/nf-exp-add \
+	src/nf-exp-delete \
+	src/nf-exp-list \
+	src/nf-log \
+	src/nf-monitor \
+	src/nf-queue \
+	src/nl-addr-add \
+	src/nl-addr-delete \
+	src/nl-addr-list \
+	src/nl-class-add \
+	src/nl-class-delete \
+	src/nl-class-list \
+	src/nl-classid-lookup \
+	src/nl-cls-add \
+	src/nl-cls-delete \
+	src/nl-cls-list \
+	src/nl-fib-lookup \
+	src/nl-link-enslave \
+	src/nl-link-ifindex2name \
+	src/nl-link-list \
+	src/nl-link-name2ifindex \
+	src/nl-link-release \
+	src/nl-link-set \
+	src/nl-link-stats \
+	src/nl-list-caches \
+	src/nl-list-sockets \
+	src/nl-monitor \
+	src/nl-neigh-add \
+	src/nl-neigh-delete \
+	src/nl-neigh-list \
+	src/nl-neightbl-list \
+	src/nl-pktloc-lookup \
+	src/nl-qdisc-add \
+	src/nl-qdisc-delete \
+	src/nl-qdisc-list \
+	src/nl-route-add \
+	src/nl-route-delete \
+	src/nl-route-get \
+	src/nl-route-list \
+	src/nl-rule-list \
+	src/nl-tctree-list \
+	src/nl-util-addr \
+	$(NULL)
+
+if ENABLE_CLI
+if ENABLE_CLI_INSTALL_BIN
+bin_PROGRAMS += $(cli_programs)
+else
+if ENABLE_CLI_INSTALL_SBIN
+sbin_PROGRAMS += $(cli_programs)
+else
+noinst_PROGRAMS += $(cli_programs)
+endif
+endif
+else
+check_PROGRAMS += $(cli_programs)
+endif
+
+src_genl_ctrl_list_CPPFLAGS =       $(src_cppflags)
+src_genl_ctrl_list_LDADD =          $(src_ldadd)
+src_idiag_socket_details_CPPFLAGS = $(src_cppflags)
+src_idiag_socket_details_LDADD =    $(src_ldadd)
+src_nf_ct_add_CPPFLAGS =            $(src_cppflags)
+src_nf_ct_add_LDADD =               $(src_ldadd)
+src_nf_ct_events_CPPFLAGS =         $(src_cppflags)
+src_nf_ct_events_LDADD =            $(src_ldadd)
+src_nf_ct_list_CPPFLAGS =           $(src_cppflags)
+src_nf_ct_list_LDADD =              $(src_ldadd)
+src_nf_exp_add_CPPFLAGS =           $(src_cppflags)
+src_nf_exp_add_LDADD =              $(src_ldadd)
+src_nf_exp_delete_CPPFLAGS =        $(src_cppflags)
+src_nf_exp_delete_LDADD =           $(src_ldadd)
+src_nf_exp_list_CPPFLAGS =          $(src_cppflags)
+src_nf_exp_list_LDADD =             $(src_ldadd)
+src_nf_log_CPPFLAGS =               $(src_cppflags)
+src_nf_log_LDADD =                  $(src_ldadd)
+src_nf_monitor_CPPFLAGS =           $(src_cppflags)
+src_nf_monitor_LDADD =              $(src_ldadd)
+src_nf_queue_CPPFLAGS =             $(src_cppflags)
+src_nf_queue_LDADD =                $(src_ldadd)
+src_nl_addr_add_CPPFLAGS =          $(src_cppflags)
+src_nl_addr_add_LDADD =             $(src_ldadd)
+src_nl_addr_delete_CPPFLAGS =       $(src_cppflags)
+src_nl_addr_delete_LDADD =          $(src_ldadd)
+src_nl_addr_list_CPPFLAGS =         $(src_cppflags)
+src_nl_addr_list_LDADD =            $(src_ldadd)
+src_nl_class_add_CPPFLAGS =         $(src_cppflags)
+src_nl_class_add_LDADD =            $(src_ldadd)
+src_nl_class_delete_CPPFLAGS =      $(src_cppflags)
+src_nl_class_delete_LDADD =         $(src_ldadd)
+src_nl_class_list_CPPFLAGS =        $(src_cppflags)
+src_nl_class_list_LDADD =           $(src_ldadd)
+src_nl_classid_lookup_CPPFLAGS =    $(src_cppflags)
+src_nl_classid_lookup_LDADD =       $(src_ldadd)
+src_nl_cls_add_CPPFLAGS =           $(src_cppflags)
+src_nl_cls_add_LDADD =              $(src_ldadd)
+src_nl_cls_delete_CPPFLAGS =        $(src_cppflags)
+src_nl_cls_delete_LDADD =           $(src_ldadd)
+src_nl_cls_list_CPPFLAGS =          $(src_cppflags)
+src_nl_cls_list_LDADD =             $(src_ldadd)
+src_nl_fib_lookup_CPPFLAGS =        $(src_cppflags)
+src_nl_fib_lookup_LDADD =           $(src_ldadd)
+src_nl_link_enslave_CPPFLAGS =      $(src_cppflags)
+src_nl_link_enslave_LDADD =         $(src_ldadd)
+src_nl_link_ifindex2name_CPPFLAGS = $(src_cppflags)
+src_nl_link_ifindex2name_LDADD =    $(src_ldadd)
+src_nl_link_list_CPPFLAGS =         $(src_cppflags)
+src_nl_link_list_LDADD =            $(src_ldadd)
+src_nl_link_name2ifindex_CPPFLAGS = $(src_cppflags)
+src_nl_link_name2ifindex_LDADD =    $(src_ldadd)
+src_nl_link_release_CPPFLAGS =      $(src_cppflags)
+src_nl_link_release_LDADD =         $(src_ldadd)
+src_nl_link_set_CPPFLAGS =          $(src_cppflags)
+src_nl_link_set_LDADD =             $(src_ldadd)
+src_nl_link_stats_CPPFLAGS =        $(src_cppflags)
+src_nl_link_stats_LDADD =           $(src_ldadd)
+src_nl_list_caches_CPPFLAGS =       $(src_cppflags)
+src_nl_list_caches_LDADD =          $(src_ldadd)
+src_nl_list_sockets_CPPFLAGS =      $(src_cppflags)
+src_nl_list_sockets_LDADD =         $(src_ldadd)
+src_nl_monitor_CPPFLAGS =           $(src_cppflags)
+src_nl_monitor_LDADD =              $(src_ldadd)
+src_nl_neigh_add_CPPFLAGS =         $(src_cppflags)
+src_nl_neigh_add_LDADD =            $(src_ldadd)
+src_nl_neigh_delete_CPPFLAGS =      $(src_cppflags)
+src_nl_neigh_delete_LDADD =         $(src_ldadd)
+src_nl_neigh_list_CPPFLAGS =        $(src_cppflags)
+src_nl_neigh_list_LDADD =           $(src_ldadd)
+src_nl_neightbl_list_CPPFLAGS =     $(src_cppflags)
+src_nl_neightbl_list_LDADD =        $(src_ldadd)
+src_nl_pktloc_lookup_CPPFLAGS =     $(src_cppflags)
+src_nl_pktloc_lookup_LDADD =        $(src_ldadd)
+src_nl_qdisc_add_CPPFLAGS =         $(src_cppflags)
+src_nl_qdisc_add_LDADD =            $(src_ldadd)
+src_nl_qdisc_delete_CPPFLAGS =      $(src_cppflags)
+src_nl_qdisc_delete_LDADD =         $(src_ldadd)
+src_nl_qdisc_list_CPPFLAGS =        $(src_cppflags)
+src_nl_qdisc_list_LDADD =           $(src_ldadd)
+src_nl_route_add_CPPFLAGS =         $(src_cppflags)
+src_nl_route_add_LDADD =            $(src_ldadd)
+src_nl_route_delete_CPPFLAGS =      $(src_cppflags)
+src_nl_route_delete_LDADD =         $(src_ldadd)
+src_nl_route_get_CPPFLAGS =         $(src_cppflags)
+src_nl_route_get_LDADD =            $(src_ldadd)
+src_nl_route_list_CPPFLAGS =        $(src_cppflags)
+src_nl_route_list_LDADD =           $(src_ldadd)
+src_nl_rule_list_CPPFLAGS =         $(src_cppflags)
+src_nl_rule_list_LDADD =            $(src_ldadd)
+src_nl_tctree_list_CPPFLAGS =       $(src_cppflags)
+src_nl_tctree_list_LDADD =          $(src_ldadd)
+src_nl_util_addr_CPPFLAGS =         $(src_cppflags)
+src_nl_util_addr_LDADD =            $(src_ldadd)
+
+###############################################################################
+
+tests_cppflags = \
+	$(warn_cppflags) \
+	-D_GNU_SOURCE \
+	-DSYSCONFDIR=\"$(sysconfdir)/libnl\" \
+	-I$(srcdir)/include/linux-private \
+	-I$(srcdir)/include \
+	-I$(builddir)/include
+
+tests_ldadd = \
+	lib/libnl-3.la \
+	lib/libnl-nf-3.la \
+	lib/libnl-genl-3.la \
+	lib/libnl-route-3.la
+
+check_PROGRAMS += \
+	tests/test-complex-HTB-with-hash-filters \
+	tests/test-create-bond \
+	tests/test-create-bridge \
+	tests/test-create-geneve \
+	tests/test-create-ifb \
+	tests/test-create-ip6tnl \
+	tests/test-create-ipgre \
+	tests/test-create-ipgretap \
+	tests/test-create-ipip \
+	tests/test-create-ipvlan \
+	tests/test-create-ipvti \
+	tests/test-create-macsec \
+	tests/test-create-macvlan \
+	tests/test-create-macvtap \
+	tests/test-create-sit \
+	tests/test-create-veth \
+	tests/test-create-vlan \
+	tests/test-create-vrf \
+	tests/test-create-vxlan \
+	tests/test-create-xfrmi \
+	tests/test-delete-link \
+	tests/test-loopback-up-down \
+	tests/test-socket-creation \
+	tests/test-u32-filter-with-actions \
+	$(NULL)
+
+tests_test_complex_HTB_with_hash_filters_CPPFLAGS = $(tests_cppflags)
+tests_test_complex_HTB_with_hash_filters_LDADD    = $(tests_ldadd)
+tests_test_create_bond_CPPFLAGS                   = $(tests_cppflags)
+tests_test_create_bond_LDADD                      = $(tests_ldadd)
+tests_test_create_bridge_CPPFLAGS                 = $(tests_cppflags)
+tests_test_create_bridge_LDADD                    = $(tests_ldadd)
+tests_test_create_geneve_CPPFLAGS                 = $(tests_cppflags)
+tests_test_create_geneve_LDADD                    = $(tests_ldadd)
+tests_test_create_ifb_CPPFLAGS                    = $(tests_cppflags)
+tests_test_create_ifb_LDADD                       = $(tests_ldadd)
+tests_test_create_ip6tnl_CPPFLAGS                 = $(tests_cppflags)
+tests_test_create_ip6tnl_LDADD                    = $(tests_ldadd)
+tests_test_create_ipgre_CPPFLAGS                  = $(tests_cppflags)
+tests_test_create_ipgre_LDADD                     = $(tests_ldadd)
+tests_test_create_ipgretap_CPPFLAGS               = $(tests_cppflags)
+tests_test_create_ipgretap_LDADD                  = $(tests_ldadd)
+tests_test_create_ipip_CPPFLAGS                   = $(tests_cppflags)
+tests_test_create_ipip_LDADD                      = $(tests_ldadd)
+tests_test_create_ipvlan_CPPFLAGS                 = $(tests_cppflags)
+tests_test_create_ipvlan_LDADD                    = $(tests_ldadd)
+tests_test_create_ipvti_CPPFLAGS                  = $(tests_cppflags)
+tests_test_create_ipvti_LDADD                     = $(tests_ldadd)
+tests_test_create_macsec_CPPFLAGS                 = $(tests_cppflags)
+tests_test_create_macsec_LDADD                    = $(tests_ldadd)
+tests_test_create_macvlan_CPPFLAGS                = $(tests_cppflags)
+tests_test_create_macvlan_LDADD                   = $(tests_ldadd)
+tests_test_create_macvtap_CPPFLAGS                = $(tests_cppflags)
+tests_test_create_macvtap_LDADD                   = $(tests_ldadd)
+tests_test_create_sit_CPPFLAGS                    = $(tests_cppflags)
+tests_test_create_sit_LDADD                       = $(tests_ldadd)
+tests_test_create_veth_CPPFLAGS                   = $(tests_cppflags)
+tests_test_create_veth_LDADD                      = $(tests_ldadd)
+tests_test_create_vlan_CPPFLAGS                   = $(tests_cppflags)
+tests_test_create_vlan_LDADD                      = $(tests_ldadd)
+tests_test_create_vrf_CPPFLAGS                    = $(tests_cppflags)
+tests_test_create_vrf_LDADD                       = $(tests_ldadd)
+tests_test_create_vxlan_CPPFLAGS                  = $(tests_cppflags)
+tests_test_create_vxlan_LDADD                     = $(tests_ldadd)
+tests_test_create_xfrmi_CPPFLAGS                  = $(tests_cppflags)
+tests_test_create_xfrmi_LDADD                     = $(tests_ldadd)
+tests_test_delete_link_CPPFLAGS                   = $(tests_cppflags)
+tests_test_delete_link_LDADD                      = $(tests_ldadd)
+tests_test_loopback_up_down_CPPFLAGS              = $(tests_cppflags)
+tests_test_loopback_up_down_LDADD                 = $(tests_ldadd)
+tests_test_socket_creation_CPPFLAGS               = $(tests_cppflags)
+tests_test_socket_creation_LDADD                  = $(tests_ldadd)
+tests_test_u32_filter_with_actions_CPPFLAGS       = $(tests_cppflags)
+tests_test_u32_filter_with_actions_LDADD          = $(tests_ldadd)
+
+check_PROGRAMS += \
+	tests/test-cache-mngr \
+	tests/test-genl \
+	tests/test-nf-cache-mngr
+
+tests_cli_ldadd = \
+	$(tests_ldadd) \
+	src/lib/libnl-cli-3.la
+
+tests_test_cache_mngr_CPPFLAGS                    = $(tests_cppflags)
+tests_test_cache_mngr_LDADD                       = $(tests_cli_ldadd)
+tests_test_genl_CPPFLAGS                          = $(tests_cppflags)
+tests_test_genl_LDADD                             = $(tests_cli_ldadd)
+tests_test_nf_cache_mngr_CPPFLAGS                 = $(tests_cppflags)
+tests_test_nf_cache_mngr_LDADD                    = $(tests_cli_ldadd)
+
+
+if WITH_CHECK
+check_programs += tests/check-all
+endif
+
+tests_check_all_SOURCES = \
+	tests/check-addr.c \
+	tests/check-all.c \
+	tests/check-attr.c \
+	tests/check-ematch-tree-clone.c \
+	tests/util.h \
+	$(NULL)
+
+tests_check_all_CPPFLAGS = \
+	$(tests_cppflags) \
+	$(CHECK_CFLAGS)
+
+tests_check_all_LDADD = \
+	$(tests_ldadd) \
+	$(CHECK_LIBS)
+
+###############################################################################
+
+dist_man8_MANS = \
+	man/genl-ctrl-list.8 \
+	man/nl-classid-lookup.8 \
+	man/nl-pktloc-lookup.8 \
+	man/nl-qdisc-add.8 \
+	man/nl-qdisc-delete.8 \
+	man/nl-qdisc-list.8 \
+	$(NULL)
+
+###############################################################################
+
+EXTRA_DIST += \
+	python/README \
+	\
+	python/doc/conf.py \
+	python/doc/core.rst \
+	python/doc/index.rst \
+	python/doc/route_addr.rst \
+	python/doc/route.rst \
+	\
+	python/examples/iface.py \
+	python/examples/nl80211.py \
+	python/examples/wiphy.py \
+	\
+	python/netlink/capi.i \
+	python/netlink/fixes.h \
+	python/netlink/__init__.py \
+	python/netlink/core.py \
+	python/netlink/util.py \
+	python/netlink/utils.h \
+	\
+	python/netlink/genl/capi.i \
+	python/netlink/genl/__init__.py \
+	\
+	python/netlink/route/capi.i \
+	python/netlink/route/__init__.py \
+	python/netlink/route/address.py \
+	python/netlink/route/link.py \
+	python/netlink/route/tc.py \
+	python/netlink/route/links/__init__.py \
+	python/netlink/route/links/dummy.py \
+	python/netlink/route/links/inet.py \
+	python/netlink/route/links/vlan.py \
+	python/netlink/route/qdisc/__init__.py \
+	python/netlink/route/qdisc/htb.py \
+	\
+	python/tests/test-create-bridge.py
+
+###############################################################################
+
+check_PROGRAMS += $(check_programs)
+TESTS += $(check_programs)
+
+if ENABLE_CLI
 pkgconfig_DATA += libnl-cli-3.0.pc
 endif
 
+pkgsysconfdir = $(sysconfdir)/libnl
+pkgsysconf_DATA = \
+	etc/pktloc \
+	etc/classid
 
-pkgsysconfdir = ${sysconfdir}/libnl
-pkgsysconf_DATA = etc/pktloc etc/classid
-
-EXTRA_DIST = \
-	$(pkgsysconf_DATA)
+EXTRA_DIST += \
+	$(pkgsysconf_DATA) \
+	libnl-3.sym \
+	libnl-cli-3.sym \
+	libnl-genl-3.sym \
+	libnl-idiag-3.sym \
+	libnl-nf-3.sym \
+	libnl-route-3.sym \
+	libnl-xfrm-3.sym \
+	$(NULL)
diff --git a/NOTICE b/NOTICE
deleted file mode 100644
index fab48ed..0000000
--- a/NOTICE
+++ /dev/null
@@ -1,506 +0,0 @@
-		  GNU LESSER GENERAL PUBLIC LICENSE
-		       Version 2.1, February 1999
-
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
-     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-[This is the first released version of the Lesser GPL.  It also counts
- as the successor of the GNU Library Public License, version 2, hence
- the version number 2.1.]
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
-
-  This license, the Lesser General Public License, applies to some
-specially designated software packages--typically libraries--of the
-Free Software Foundation and other authors who decide to use it.  You
-can use it too, but we suggest you first think carefully about whether
-this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations below.
-
-  When we speak of free software, we are referring to freedom of use,
-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 and use pieces of
-it in new free programs; and that you are informed that you can do
-these things.
-
-  To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights.  These restrictions translate to certain responsibilities for
-you if you distribute copies of the library or if you modify it.
-
-  For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you.  You must make sure that they, too, receive or can get the source
-code.  If you link other code with the library, you must provide
-complete object files to the recipients, so that they can relink them
-with the library after making changes to the library and recompiling
-it.  And you must show them these terms so they know their rights.
-
-  We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
-
-  To protect each distributor, we want to make it very clear that
-there is no warranty for the free library.  Also, if the library is
-modified by someone else and passed on, the recipients should know
-that what they have is not the original version, so that the original
-author's reputation will not be affected by problems that might be
-introduced by others.
-
-  Finally, software patents pose a constant threat to the existence of
-any free program.  We wish to make sure that a company cannot
-effectively restrict the users of a free program by obtaining a
-restrictive license from a patent holder.  Therefore, we insist that
-any patent license obtained for a version of the library must be
-consistent with the full freedom of use specified in this license.
-
-  Most GNU software, including some libraries, is covered by the
-ordinary GNU General Public License.  This license, the GNU Lesser
-General Public License, applies to certain designated libraries, and
-is quite different from the ordinary General Public License.  We use
-this license for certain libraries in order to permit linking those
-libraries into non-free programs.
-
-  When a program is linked with a library, whether statically or using
-a shared library, the combination of the two is legally speaking a
-combined work, a derivative of the original library.  The ordinary
-General Public License therefore permits such linking only if the
-entire combination fits its criteria of freedom.  The Lesser General
-Public License permits more lax criteria for linking other code with
-the library.
-
-  We call this license the "Lesser" General Public License because it
-does Less to protect the user's freedom than the ordinary General
-Public License.  It also provides other free software developers Less
-of an advantage over competing non-free programs.  These disadvantages
-are the reason we use the ordinary General Public License for many
-libraries.  However, the Lesser license provides advantages in certain
-special circumstances.
-
-  For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it becomes
-a de-facto standard.  To achieve this, non-free programs must be
-allowed to use the library.  A more frequent case is that a free
-library does the same job as widely used non-free libraries.  In this
-case, there is little to gain by limiting the free library to free
-software only, so we use the Lesser General Public License.
-
-  In other cases, permission to use a particular library in non-free
-programs enables a greater number of people to use a large body of
-free software.  For example, permission to use the GNU C Library in
-non-free programs enables many more people to use the whole GNU
-operating system, as well as its variant, the GNU/Linux operating
-system.
-
-  Although the Lesser General Public License is Less protective of the
-users' freedom, it does ensure that the user of a program that is
-linked with the Library has the freedom and the wherewithal to run
-that program using a modified version of the Library.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.  Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library".  The
-former contains code derived from the library, whereas the latter must
-be combined with the library in order to run.
-
-		  GNU LESSER GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License Agreement applies to any software library or other
-program which contains a notice placed by the copyright holder or
-other authorized party saying it may be distributed under the terms of
-this Lesser General Public License (also called "this License").
-Each licensee is addressed as "you".
-
-  A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
-
-  The "Library", below, refers to any such software library or work
-which has been distributed under these terms.  A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language.  (Hereinafter, translation is
-included without limitation in the term "modification".)
-
-  "Source code" for a work means the preferred form of the work for
-making modifications to it.  For a library, 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 library.
-
-  Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it).  Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-  
-  1. You may copy and distribute verbatim copies of the Library's
-complete 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 distribute a copy of this License along with the
-Library.
-
-  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 Library or any portion
-of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
-
-    b) You must cause the files modified to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    c) You must cause the whole of the work to be licensed at no
-    charge to all third parties under the terms of this License.
-
-    d) If a facility in the modified Library refers to a function or a
-    table of data to be supplied by an application program that uses
-    the facility, other than as an argument passed when the facility
-    is invoked, then you must make a good faith effort to ensure that,
-    in the event an application does not supply such function or
-    table, the facility still operates, and performs whatever part of
-    its purpose remains meaningful.
-
-    (For example, a function in a library to compute square roots has
-    a purpose that is entirely well-defined independent of the
-    application.  Therefore, Subsection 2d requires that any
-    application-supplied function or table used by this function must
-    be optional: if the application does not supply it, the square
-    root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Library,
-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 Library, 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 Library.
-
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library.  To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License.  (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.)  Do not make any other change in
-these notices.
-
-  Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
-
-  This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
-
-  4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you 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.
-
-  If distribution of 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 satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library".  Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
-
-  However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library".  The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
-  When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library.  The
-threshold for this to be true is not precisely defined by law.
-
-  If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work.  (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
-
-  Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
-
-  6. As an exception to the Sections above, you may also combine or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
-
-  You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License.  You must supply a copy of this License.  If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License.  Also, you must do one
-of these things:
-
-    a) Accompany the work with the complete corresponding
-    machine-readable source code for the Library including whatever
-    changes were used in the work (which must be distributed under
-    Sections 1 and 2 above); and, if the work is an executable linked
-    with the Library, with the complete machine-readable "work that
-    uses the Library", as object code and/or source code, so that the
-    user can modify the Library and then relink to produce a modified
-    executable containing the modified Library.  (It is understood
-    that the user who changes the contents of definitions files in the
-    Library will not necessarily be able to recompile the application
-    to use the modified definitions.)
-
-    b) Use a suitable shared library mechanism for linking with the
-    Library.  A suitable mechanism is one that (1) uses at run time a
-    copy of the library already present on the user's computer system,
-    rather than copying library functions into the executable, and (2)
-    will operate properly with a modified version of the library, if
-    the user installs one, as long as the modified version is
-    interface-compatible with the version that the work was made with.
-
-    c) Accompany the work with a written offer, valid for at
-    least three years, to give the same user the materials
-    specified in Subsection 6a, above, for a charge no more
-    than the cost of performing this distribution.
-
-    d) If distribution of the work is made by offering access to copy
-    from a designated place, offer equivalent access to copy the above
-    specified materials from the same place.
-
-    e) Verify that the user has already received a copy of these
-    materials or that you have already sent this user a copy.
-
-  For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it.  However, as a special exception,
-the materials to be 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.
-
-  It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system.  Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-
-  7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
-
-    a) Accompany the combined library with a copy of the same work
-    based on the Library, uncombined with any other library
-    facilities.  This must be distributed under the terms of the
-    Sections above.
-
-    b) Give prominent notice with the combined library of the fact
-    that part of it is a work based on the Library, and explaining
-    where to find the accompanying uncombined form of the same work.
-
-  8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License.  Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library 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.
-
-  9. 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 Library or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
-
-  10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-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 with
-this License.
-
-  11. 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 Library at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Library 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 Library.
-
-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.
-
-  12. If the distribution and/or use of the Library is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library 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.
-
-  13. The Free Software Foundation may publish revised and/or new
-versions of the Lesser 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 Library
-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 Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-
-  14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-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
-
-  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "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
-LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. 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 LIBRARY 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
-LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
-
-  If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change.  You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms of the
-ordinary General Public License).
-
-  To apply these terms, attach the following notices to the library.  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 library's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
-
-    This library 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
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the
-  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
-
-  <signature of Ty Coon>, 1 April 1990
-  Ty Coon, President of Vice
-
-That's all there is to it!
-
-
-
-
diff --git a/NOTICE b/NOTICE
new file mode 120000
index 0000000..7a694c9
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1 @@
+LICENSE
\ No newline at end of file
diff --git a/README.version b/README.version
deleted file mode 100644
index d52ad3a..0000000
--- a/README.version
+++ /dev/null
@@ -1,3 +0,0 @@
-URL: http://www.infradead.org/~tgr/libnl/files/libnl-2.0.tar.gz
-Version: 2.0
-BugComponent: 99150
diff --git a/configure.ac b/configure.ac
index c6f064e..ee44d89 100644
--- a/configure.ac
+++ b/configure.ac
@@ -12,8 +12,8 @@
 
 # copied from glib
 m4_define([libnl_major_version], [3])
-m4_define([libnl_minor_version], [2])
-m4_define([libnl_micro_version], [25])
+m4_define([libnl_minor_version], [5])
+m4_define([libnl_micro_version], [0])
 m4_define([libnl_git_sha], [m4_esyscmd([ ( [ -d ./.git/ ] && [ "$(readlink -f ./.git/)" = "$(readlink -f "$(git rev-parse --git-dir 2>/dev/null)" 2>/dev/null)" ] && git rev-parse --verify -q HEAD 2>/dev/null ) || true ])])
 
 
@@ -35,9 +35,9 @@
 # 3. Programs may need to be changed, recompiled, relinked in order to use
 #    the new version. Bump current, set revision and age to 0.
 
-m4_define([libnl_lt_current],    [220])
-m4_define([libnl_lt_revision],	 [0])
-m4_define([libnl_lt_age],        [20])
+m4_define([libnl_lt_current],    [226])
+m4_define([libnl_lt_revision],   [0])
+m4_define([libnl_lt_age],        [26])
 
 m4_define([libnl_version],
 	  [libnl_major_version.libnl_minor_version.libnl_micro_version])
@@ -79,11 +79,10 @@
 AC_C_INLINE
 
 PKG_CHECK_MODULES([CHECK], [check >= 0.9.0],
-	[enable_unit_tests="yes"],
+	[has_check="yes"],
 	[AC_MSG_WARN([*** Disabling building of unit tests])
-	 enable_unit_tests="no"])
-
-AM_CONDITIONAL([ENABLE_UNIT_TESTS], [test "$enable_unit_tests" = "yes"])
+	 has_check="no"])
+AM_CONDITIONAL(WITH_CHECK, [test "$has_check" = 'yes'])
 
 AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH],
 	[Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]),
@@ -91,56 +90,50 @@
 AC_SUBST([pkgconfigdir])
 
 AC_ARG_ENABLE([cli],
-	AS_HELP_STRING([--disable-cli], [Do not build command line interface utils]),
+	AS_HELP_STRING([--enable-cli=yes|no|no-inst|bin|sbin], [Whether to build command line interface utils. Defaults to 'yes' which is a synonym for 'bin'. 'no-inst' means only build, not installing. 'bin'/'sbin' means installing to bin/sbin directory]),
 	[enable_cli="$enableval"], [enable_cli="yes"])
-AM_CONDITIONAL([ENABLE_CLI], [test "$enable_cli" = "yes"])
+if test "$enable_cli" != "no" &&
+   test "$enable_cli" != "no-inst" &&
+   test "$enable_cli" != "sbin"; then
+	enable_cli="bin"
+fi
+AM_CONDITIONAL([ENABLE_CLI], [test "$enable_cli" != "no"])
+AM_CONDITIONAL([ENABLE_CLI_INSTALL_BIN], [test "$enable_cli" = "bin"])
+AM_CONDITIONAL([ENABLE_CLI_INSTALL_SBIN], [test "$enable_cli" = "sbin"])
+
+AC_CHECK_HEADERS(dlfcn.h, [], [])
 
 AC_ARG_ENABLE([pthreads],
 	AS_HELP_STRING([--disable-pthreads], [Disable pthreads support]),
 	[enable_pthreads="$enableval"], [enable_pthreads="yes"])
 AM_CONDITIONAL([DISABLE_PTHREADS], [test "$enable_pthreads" = "no"])
-
-AC_ARG_ENABLE([debug],
-	AS_HELP_STRING([--disable-debug], [Do not include debugging statements]),
-	[enable_debug="$enableval"], [enable_debug="yes"])
-AM_CONDITIONAL([ENABLE_DEBUG], [test "$enable_debug" = "no" ])
-
-AC_CHECK_LIB([m], [pow], [], AC_MSG_ERROR([libm is required]))
-
 if test "x$enable_pthreads" = "xno"; then
     AC_DEFINE([DISABLE_PTHREADS], [1], [Define to 1 to disable pthreads])
 else
     AC_CHECK_LIB([pthread], [pthread_mutex_lock], [], AC_MSG_ERROR([libpthread is required]))
 fi
 
+AC_ARG_ENABLE([debug],
+	AS_HELP_STRING([--disable-debug], [Do not include debugging statements]),
+	[enable_debug="$enableval"], [enable_debug="yes"])
 if test "x$enable_debug" = "xyes"; then
     AC_DEFINE([NL_DEBUG], [1], [Define to 1 to enable debugging])
 fi
 
 AC_CONFIG_SUBDIRS([doc])
 
+AC_CHECK_FUNCS([strerror_l])
+
 AC_CONFIG_FILES([
 Makefile
-libnl.sym
 libnl-3.0.pc
 libnl-route-3.0.pc
 libnl-genl-3.0.pc
 libnl-nf-3.0.pc
 libnl-cli-3.0.pc
-lib/Makefile
-include/Makefile
-src/Makefile
-src/lib/Makefile
-tests/Makefile
-man/Makefile
-python/Makefile
+libnl-xfrm-3.0.pc
+libnl-idiag-3.0.pc
 python/setup.py
-python/doc/Makefile
-python/examples/Makefile
-python/netlink/Makefile
-python/netlink/genl/Makefile
-python/netlink/route/Makefile
-python/tests/Makefile
 include/netlink/version.h
 ])
 
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index 50e8f0c..0c4fcc4 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -1562,7 +1562,7 @@
 # When a file name is specified after GENERATE_TAGFILE, doxygen will create
 # a tag file that is based on the input files it reads.
 
-GENERATE_TAGFILE       =
+GENERATE_TAGFILE       = api/libnl.tag
 
 # If the ALLEXTERNALS tag is set to YES all external classes will be listed
 # in the class index. If set to NO only the inherited external classes
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 338f077..67ae1fc 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -2,12 +2,15 @@
 
 .PHONY: gendoc api_ref asciidoc
 
-ASCIIDOCOPTS=-a pygments -a language=c -a icons \
-	     -a toc2 \
-	     -a numbered \
-	     -a imagesdir="./images/" \
-	     -a iconsdir="./images/icons" \
-	     -a stylesdir="${abs_srcdir}/stylesheets/"
+ASCIIDOCOPTS = \
+	-a pygments \
+	-a language=c \
+	-a icons \
+	-a toc2 \
+	-a numbered \
+	-a imagesdir="./images/" \
+	-a iconsdir="./images/icons" \
+	-a stylesdir="$(abs_srcdir)/stylesheets/"
 
 EXTRA_DIST = \
 	core.txt \
@@ -38,15 +41,19 @@
 else
 	@echo "Warning: Linking to API reference is disabled, check configure output"
 endif
-	
+
 
 %.html: %.txt link_doc
+if HAVE_ASCIIDOC
 	./resolve-asciidoc-refs.py $< > asciidoc.tmp
 	asciidoc $(ASCIIDOCOPTS) -o $@ asciidoc.tmp
 if LINK_DOC
 	./doxygen-link.py libnl.dict $@ > asciidoc.tmp
 	mv asciidoc.tmp $@
 endif
+else
+	@echo "Warning: Building of asciidoc files is disabled, check autoconf logs"
+endif
 
 asciidoc: core.html route.html index.html
 
@@ -70,4 +77,4 @@
 endif
 
 clean-local:
-	rm -f api/* libnl.dict *.html;
+	rm -f api/* libnl.dict *.html
diff --git a/doc/api/.gitignore b/doc/api/.gitignore
index e57ca88..cdef49d 100644
--- a/doc/api/.gitignore
+++ b/doc/api/.gitignore
@@ -4,5 +4,6 @@
 *.map
 *.md5
 *.js
+*.tag
 formula.repository
 jquery.js
diff --git a/doc/configure.ac b/doc/configure.ac
index d4cda85..187447c 100644
--- a/doc/configure.ac
+++ b/doc/configure.ac
@@ -9,14 +9,12 @@
 # Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
 #
 
-AC_INIT(libnl-doc, [3.2.25], [http://www.infradead.org/~tgr/libnl/])
+AC_INIT(libnl-doc, [3.5.0], [http://www.infradead.org/~tgr/libnl/])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR([build-aux])
 AM_INIT_AUTOMAKE([foreign])
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)], [])
 
-m4_include([m4/ax_python.m4])
-
 #
 # Generating the documentation
 #
@@ -24,12 +22,15 @@
 	      AS_HELP_STRING([--disable-doc], [Do not generate documentation]),
 	      [generate_doc="$enableval"], [generate_doc=auto])
 
-AX_PYTHON
-
 if test "x$generate_doc" != "xno"; then
 	AC_PROG_SED
 	AC_PROG_EGREP
 
+	AC_CHECK_PROG(HAVE_PYTHON, [python], yes, no)
+	if test "x$HAVE_PYTHON" = "xno"  -a "x$generate_doc" = "xyes"; then
+		AC_MSG_ERROR([*** python binary required to generate documentation])
+	fi
+
 	AC_CHECK_PROG(HAVE_DOXYGEN, [doxygen], yes, no)
 	if test "x$HAVE_DOXYGEN" = "xno" -a "x$generate_doc" = "xyes"; then
 		AC_MSG_ERROR([*** doxygen package required to generate documentation])
diff --git a/doc/m4/ax_python.m4 b/doc/m4/ax_python.m4
deleted file mode 100644
index f9a5135..0000000
--- a/doc/m4/ax_python.m4
+++ /dev/null
@@ -1,97 +0,0 @@
-# ===========================================================================
-#         http://www.gnu.org/software/autoconf-archive/ax_python.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_PYTHON
-#
-# DESCRIPTION
-#
-#   This macro does a complete Python development environment check.
-#
-#   It recurses through several python versions (from 2.1 to 2.6 in this
-#   version), looking for an executable. When it finds an executable, it
-#   looks to find the header files and library.
-#
-#   It sets PYTHON_BIN to the name of the python executable,
-#   PYTHON_INCLUDE_DIR to the directory holding the header files, and
-#   PYTHON_LIB to the name of the Python library.
-#
-#   This macro calls AC_SUBST on PYTHON_BIN (via AC_CHECK_PROG),
-#   PYTHON_INCLUDE_DIR and PYTHON_LIB.
-#
-# LICENSE
-#
-#   Copyright (c) 2008 Michael Tindal
-#
-#   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/>.
-#
-#   As a special exception, the respective Autoconf Macro's copyright owner
-#   gives unlimited permission to copy, distribute and modify the configure
-#   scripts that are the output of Autoconf when processing the Macro. You
-#   need not follow the terms of the GNU General Public License when using
-#   or distributing such scripts, even though portions of the text of the
-#   Macro appear in them. The GNU General Public License (GPL) does govern
-#   all other use of the material that constitutes the Autoconf Macro.
-#
-#   This special exception to the GPL applies to versions of the Autoconf
-#   Macro released by the Autoconf Archive. When you make and distribute a
-#   modified version of the Autoconf Macro, you may extend this special
-#   exception to the GPL to apply to your modified version as well.
-
-#serial 9
-
-AC_DEFUN([AX_PYTHON],
-[AC_MSG_CHECKING(for python build information)
-AC_MSG_RESULT([])
-for python in python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python; do
-AC_CHECK_PROGS(PYTHON_BIN, [$python])
-ax_python_bin=$PYTHON_BIN
-if test x$ax_python_bin != x; then
-   AC_CHECK_LIB($ax_python_bin, main, ax_python_lib=$ax_python_bin, ax_python_lib=no)
-   AC_CHECK_HEADER([$ax_python_bin/Python.h],
-   [[ax_python_header=`locate $ax_python_bin/Python.h | sed -e s,/Python.h,,`]],
-   ax_python_header=no)
-   if test x$ax_python_lib != xno; then
-     if test x$ax_python_header != xno; then
-       break;
-     fi
-   fi
-fi
-done
-if test x$ax_python_bin = x; then
-   ax_python_bin=no
-fi
-if test x$ax_python_header = x; then
-   ax_python_header=no
-fi
-if test x$ax_python_lib = x; then
-   ax_python_lib=no
-fi
-
-AC_MSG_RESULT([  results of the Python check:])
-AC_MSG_RESULT([    Binary:      $ax_python_bin])
-AC_MSG_RESULT([    Library:     $ax_python_lib])
-AC_MSG_RESULT([    Include Dir: $ax_python_header])
-
-if test x$ax_python_header != xno; then
-  PYTHON_INCLUDE_DIR=$ax_python_header
-  AC_SUBST(PYTHON_INCLUDE_DIR)
-fi
-if test x$ax_python_lib != xno; then
-  PYTHON_LIB=$ax_python_lib
-  AC_SUBST(PYTHON_LIB)
-fi
-])dnl
diff --git a/doc/route.txt b/doc/route.txt
index d9f88e1..9d4c23a 100644
--- a/doc/route.txt
+++ b/doc/route.txt
@@ -763,6 +763,63 @@
 rtnl_link_put(link);
 -----
 
+[[link_macvtap]]
+==== MACVTAP
+
+[source,c]
+-----
+extern struct rtnl_link *rtnl_link_macvtap_alloc(void);
+
+extern int		rtnl_link_is_macvtap(struct rtnl_link *);
+
+extern char *		rtnl_link_macvtap_mode2str(int, char *, size_t);
+extern int		rtnl_link_macvtap_str2mode(const char *);
+
+extern char *		rtnl_link_macvtap_flags2str(int, char *, size_t);
+extern int		rtnl_link_macvtap_str2flags(const char *);
+
+extern int		rtnl_link_macvtap_set_mode(struct rtnl_link *,
+			                           uint32_t);
+extern uint32_t		rtnl_link_macvtap_get_mode(struct rtnl_link *);
+
+extern int		rtnl_link_macvtap_set_flags(struct rtnl_link *,
+			                            uint16_t);
+extern int		rtnl_link_macvtap_unset_flags(struct rtnl_link *,
+			                              uint16_t);
+extern uint16_t		rtnl_link_macvtap_get_flags(struct rtnl_link *);
+-----
+
+.Example: Add a MACVTAP device
+[source,c]
+-----
+struct rtnl_link *link;
+int master_index;
+struct nl_addr* addr;
+
+/* lookup interface index of eth0 */
+if (!(master_index = rtnl_link_name2i(link_cache, "eth0")))
+	/* error */
+
+/* allocate new link object of type macvtap */
+link = rtnl_link_macvtap_alloc();
+
+/* set eth0 to be our master device */
+rtnl_link_set_link(link, master_index);
+
+/* set address of virtual interface */
+addr = nl_addr_build(AF_LLC, ether_aton("00:11:22:33:44:55"), ETH_ALEN);
+rtnl_link_set_addr(link, addr);
+nl_addr_put(addr);
+
+/* set mode of virtual interface */
+rtnl_link_macvtap_set_mode(link, rtnl_link_macvtap_str2mode("bridge"));
+
+if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0)
+	/* error */
+
+rtnl_link_put(link);
+-----
+
 [[link_vxlan]]
 ==== VXLAN
 
@@ -892,7 +949,7 @@
 struct rtnl_link *link
 struct in_addr addr
 
-/* allocate new link object of type vxlan */
+/* allocate new link object of type ipip */
 if(!(link = rtnl_link_ipip_alloc()))
         /* error */
 
@@ -970,7 +1027,7 @@
 struct rtnl_link *link
 struct in_addr addr
 
-/* allocate new link object of type vxlan */
+/* allocate new link object of type ipgre */
 if(!(link = rtnl_link_ipgre_alloc()))
 	/* error */
 
@@ -1048,7 +1105,7 @@
 struct rtnl_link *link
 struct in_addr addr
 
-/* allocate new link object of type vxlan */
+/* allocate new link object of type sit */
 if(!(link = rtnl_link_sit_alloc()))
 	/* error */
 
@@ -1093,10 +1150,10 @@
 extern uint32_t rtnl_link_ipvti_get_link(struct rtnl_link *link);
 
 extern int rtnl_link_ipvti_set_ikey(struct rtnl_link *link, uint32_t ikey);
-extern uint32_t rtnl_link_get_ikey(struct rtnl_link *link);
+extern uint32_t rtnl_link_ipvti_get_ikey(struct rtnl_link *link);
 
 extern int rtnl_link_ipvti_set_okey(struct rtnl_link *link, uint32_t okey);
-extern uint32_t rtnl_link_get_okey(struct rtnl_link *link)
+extern uint32_t rtnl_link_ipvti_get_okey(struct rtnl_link *link)
 
 extern int rtnl_link_ipvti_set_local(struct rtnl_link *link, uint32_t addr);
 extern uint32_t rtnl_link_ipvti_get_local(struct rtnl_link *link);
@@ -1112,7 +1169,7 @@
 struct rtnl_link *link
 struct in_addr addr
 
-/* allocate new link object of type vxlan */
+/* allocate new link object of type ipvti */
 if(!(link = rtnl_link_ipvti_alloc()))
 	/* error */
 
@@ -1200,6 +1257,49 @@
 -----
 
 
+[[link_xfrmi]]
+==== XFRMI
+
+[source,c]
+-----
+extern struct rtnl_link *rtnl_link_xfrmi_alloc(void);
+
+extern int rtnl_link_xfrmi_set_link(struct rtnl_link *link,  uint32_t index);
+extern uint32_t rtnl_link_xfrmi_get_link(struct rtnl_link *link);
+
+extern int rtnl_link_xfrmi_set_if_id(struct rtnl_link *link, uint32_t if_id);
+extern uint32_t rtnl_link_xfrmi_get_if_id(struct rtnl_link *link);
+
+-----
+
+.Example: Add a xfrmi device
+[source,c]
+-----
+struct rtnl_link *link
+struct in_addr addr
+
+/* allocate new link object of type xfrmi */
+if(!(link = rtnl_link_xfrmi_alloc()))
+	/* error */
+
+/* set xfrmi name */
+if ((err = rtnl_link_set_name(link, "ipsec0")) < 0)
+	/* error */
+
+/* set link index  */
+if ((err = rtnl_link_xfrmi_set_link(link, if_index)) < 0)
+	/* error */
+
+/* set if_id */
+if ((err = rtnl_link_xfrmi_set_if_id(link, 16)) < 0)
+	/* error */
+
+if((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0)
+	/* error */
+
+rtnl_link_put(link);
+-----
+
 == Neighbouring
 
 == Routing
diff --git a/doc/src/toc.c b/doc/src/toc.c
index a91c2a7..921bcf6 100644
--- a/doc/src/toc.c
+++ b/doc/src/toc.c
@@ -17,7 +17,7 @@
 
 The library is designed to ensure that all components are optional, i.e.
 even though the core library provides a caching system which allows to
-easly manage objects of any kind, no application is required to use this
+easily manage objects of any kind, no application is required to use this
 caching system if it has no need for it.
 
 The library was developed and tested on 2.6.x and 3.x kernel releases. It
@@ -39,7 +39,7 @@
 
 \section main_website Website
 
-- http://www.infradead.org/~tgr/libnl/
+- https://www.infradead.org/~tgr/libnl/
 
 \section main_mailinglist Mailinglist
 
@@ -49,6 +49,6 @@
 libnl@lists.infradead.org
 @endcode
 
-- Archives: http://canuck.infradead.org/pipermail/libnl/
+- Archives: https://lists.infradead.org/pipermail/libnl/
 
 */
diff --git a/etc/pktloc b/etc/pktloc
index 505c44e..8559161 100644
--- a/etc/pktloc
+++ b/etc/pktloc
@@ -58,7 +58,7 @@
 tcp.flag.ack	u8		tcp+13		0x10		4
 tcp.flag.psh	u8		tcp+13		0x08		3
 tcp.flag.rst	u8		tcp+13		0x04		2
-tpc.flag.syn	u8		tcp+13		0x02		1
+tcp.flag.syn	u8		tcp+13		0x02		1
 tcp.flag.fin	u8		tcp+13		0x01
 
 tcp.win		u16		tcp+14
diff --git a/include/Makefile.am b/include/Makefile.am
deleted file mode 100644
index 765cf5a..0000000
--- a/include/Makefile.am
+++ /dev/null
@@ -1,153 +0,0 @@
-# -*- Makefile -*-
-
-libnlincludedir = $(includedir)/libnl@MAJ_VERSION@
-
-nobase_libnlinclude_HEADERS = \
-	netlink/fib_lookup/lookup.h \
-	netlink/fib_lookup/request.h \
-	netlink/genl/ctrl.h \
-	netlink/genl/family.h \
-	netlink/genl/genl.h \
-	netlink/genl/mngt.h \
-	netlink/netfilter/ct.h \
-	netlink/netfilter/exp.h \
-	netlink/netfilter/log.h \
-	netlink/netfilter/log_msg.h \
-	netlink/netfilter/netfilter.h \
-	netlink/netfilter/nfnl.h \
-	netlink/netfilter/queue.h \
-	netlink/netfilter/queue_msg.h \
-	netlink/addr.h \
-	netlink/attr.h \
-	netlink/cache.h \
-	netlink/data.h \
-	netlink/errno.h \
-	netlink/handlers.h \
-	netlink/hash.h \
-	netlink/hashtable.h \
-	netlink/list.h \
-	netlink/msg.h \
-	netlink/netlink-compat.h \
-	netlink/netlink-kernel.h \
-	netlink/netlink.h \
-	netlink/object.h \
-	netlink/route/action.h \
-	netlink/route/act/mirred.h \
-	netlink/route/cls/ematch/cmp.h \
-	netlink/route/cls/ematch/meta.h \
-	netlink/route/cls/ematch/nbyte.h \
-	netlink/route/cls/ematch/text.h \
-	netlink/route/cls/basic.h \
-	netlink/route/cls/cgroup.h \
-	netlink/route/cls/ematch.h \
-	netlink/route/cls/fw.h \
-	netlink/route/cls/police.h \
-	netlink/route/cls/u32.h \
-	netlink/route/link/api.h \
-	netlink/route/link/bonding.h \
-	netlink/route/link/bridge.h \
-	netlink/route/link/can.h \
-	netlink/route/link/inet.h \
-	netlink/route/link/info-api.h \
-	netlink/route/link/macvlan.h \
-	netlink/route/link/vlan.h \
-	netlink/route/link/vxlan.h \
-	netlink/route/link/veth.h \
-	netlink/route/link/ip6tnl.h \
-	netlink/route/link/ipgre.h \
-	netlink/route/link/ipip.h \
-	netlink/route/link/ipvti.h \
-	netlink/route/link/sit.h \
-	netlink/route/qdisc/cbq.h \
-	netlink/route/qdisc/dsmark.h \
-	netlink/route/qdisc/fifo.h \
-	netlink/route/qdisc/htb.h \
-	netlink/route/qdisc/netem.h \
-	netlink/route/qdisc/prio.h \
-	netlink/route/qdisc/red.h \
-	netlink/route/qdisc/sfq.h \
-	netlink/route/qdisc/tbf.h \
-	netlink/route/qdisc/plug.h \
-	netlink/route/qdisc/fq_codel.h \
-	netlink/route/addr.h \
-	netlink/route/class.h \
-	netlink/route/classifier.h \
-	netlink/route/link.h \
-	netlink/route/neighbour.h \
-	netlink/route/neightbl.h \
-	netlink/route/nexthop.h \
-	netlink/route/pktloc.h \
-	netlink/route/qdisc.h \
-	netlink/route/route.h \
-	netlink/route/rtnl.h \
-	netlink/route/rule.h \
-	netlink/route/tc.h \
-	netlink/socket.h \
-	netlink/types.h \
-	netlink/utils.h \
-	netlink/version.h \
-	netlink/cache-api.h \
-	netlink/object-api.h \
-	netlink/route/tc-api.h \
-	netlink/idiag/idiagnl.h \
-	netlink/idiag/meminfo.h \
-	netlink/idiag/msg.h \
-	netlink/idiag/req.h \
-	netlink/idiag/vegasinfo.h
-
-if ENABLE_CLI
-nobase_libnlinclude_HEADERS += \
-	netlink/cli/addr.h \
-	netlink/cli/class.h \
-	netlink/cli/cls.h \
-	netlink/cli/ct.h \
-	netlink/cli/exp.h \
-	netlink/cli/link.h \
-	netlink/cli/neigh.h \
-	netlink/cli/qdisc.h \
-	netlink/cli/route.h \
-	netlink/cli/rule.h \
-	netlink/cli/tc.h \
-	netlink/cli/utils.h
-endif
-
-noinst_HEADERS = \
-	linux/fib_rules.h \
-	linux/genetlink.h \
-	linux/gen_stats.h \
-	linux/if_addr.h \
-	linux/if_arp.h \
-	linux/if_ether.h \
-	linux/if.h \
-	linux/if_bridge.h \
-	linux/if_link.h \
-	linux/if_tunnel.h \
-	linux/if_vlan.h \
-	linux/ip.h \
-	linux/ip_mp_alg.h \
-	linux/ipv6.h \
-	linux/can/netlink.h \
-	linux/neighbour.h \
-	linux/netfilter.h \
-	linux/netfilter/nf_conntrack_common.h \
-	linux/netfilter/nfnetlink_compat.h \
-	linux/netfilter/nfnetlink_conntrack.h \
-	linux/netfilter/nfnetlink.h \
-	linux/netfilter/nfnetlink_log.h \
-	linux/netfilter/nfnetlink_queue.h \
-	linux/netlink.h \
-	linux/pkt_cls.h \
-	linux/tc_act/tc_mirred.h \
-	linux/pkt_sched.h \
-	linux/rtnetlink.h \
-	linux/snmp.h \
-	linux/tc_ematch/tc_em_meta.h \
-	netlink-private/genl.h \
-	netlink-private/netlink.h \
-	netlink-private/socket.h \
-	netlink-private/tc.h \
-	netlink-private/types.h \
-	netlink-private/cache-api.h \
-	netlink-private/object-api.h \
-	netlink-private/route/link/api.h \
-	netlink-private/route/tc-api.h
diff --git a/include/linux/can/netlink.h b/include/linux-private/linux/can/netlink.h
similarity index 72%
rename from include/linux/can/netlink.h
rename to include/linux-private/linux/can/netlink.h
index 14966dd..f0c5e58 100644
--- a/include/linux/can/netlink.h
+++ b/include/linux-private/linux/can/netlink.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /*
  * linux/can/netlink.h
  *
@@ -5,10 +6,18 @@
  *
  * Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com>
  *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * 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.
  */
 
-#ifndef CAN_NETLINK_H
-#define CAN_NETLINK_H
+#ifndef _CAN_NETLINK_H
+#define _CAN_NETLINK_H
 
 #include <linux/types.h>
 
@@ -84,10 +93,13 @@
 };
 
 #define CAN_CTRLMODE_LOOPBACK		0x01	/* Loopback mode */
-#define CAN_CTRLMODE_LISTENONLY		0x02 	/* Listen-only mode */
+#define CAN_CTRLMODE_LISTENONLY		0x02	/* Listen-only mode */
 #define CAN_CTRLMODE_3_SAMPLES		0x04	/* Triple sampling mode */
 #define CAN_CTRLMODE_ONE_SHOT		0x08	/* One-Shot mode */
 #define CAN_CTRLMODE_BERR_REPORTING	0x10	/* Bus-error reporting */
+#define CAN_CTRLMODE_FD			0x20	/* CAN FD mode */
+#define CAN_CTRLMODE_PRESUME_ACK	0x40	/* Ignore missing CAN ACKs */
+#define CAN_CTRLMODE_FD_NON_ISO		0x80	/* CAN FD in non-ISO mode */
 
 /*
  * CAN device statistics
@@ -114,9 +126,19 @@
 	IFLA_CAN_RESTART_MS,
 	IFLA_CAN_RESTART,
 	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)
 
-#endif /* CAN_NETLINK_H */
+/* u16 termination range: 1..65535 Ohms */
+#define CAN_TERMINATION_DISABLED 0
+
+#endif /* !_UAPI_CAN_NETLINK_H */
diff --git a/include/linux/fib_rules.h b/include/linux-private/linux/fib_rules.h
similarity index 72%
rename from include/linux/fib_rules.h
rename to include/linux-private/linux/fib_rules.h
index ed4504a..232df14 100644
--- a/include/linux/fib_rules.h
+++ b/include/linux-private/linux/fib_rules.h
@@ -1,6 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_FIB_RULES_H
 #define __LINUX_FIB_RULES_H
 
+#include <linux/types.h>
+#include <linux/rtnetlink.h>
+
 /* rule is permanent, and cannot be deleted */
 #define FIB_RULE_PERMANENT	0x00000001
 #define FIB_RULE_INVERT		0x00000002
@@ -19,13 +23,23 @@
 	__u8		tos;
 
 	__u8		table;
-	__u8		res1;	/* reserved */
+	__u8		res1;   /* reserved */
 	__u8		res2;	/* reserved */
 	__u8		action;
 
 	__u32		flags;
 };
 
+struct fib_rule_uid_range {
+	__u32		start;
+	__u32		end;
+};
+
+struct fib_rule_port_range {
+	__u16		start;
+	__u16		end;
+};
+
 enum {
 	FRA_UNSPEC,
 	FRA_DST,	/* destination address */
@@ -40,12 +54,19 @@
 	FRA_UNUSED5,
 	FRA_FWMARK,	/* mark */
 	FRA_FLOW,	/* flow/class id */
-	FRA_UNUSED6,
-	FRA_UNUSED7,
-	FRA_UNUSED8,
+	FRA_TUN_ID,
+	FRA_SUPPRESS_IFGROUP,
+	FRA_SUPPRESS_PREFIXLEN,
 	FRA_TABLE,	/* Extended table id */
 	FRA_FWMASK,	/* mask for netfilter mark */
 	FRA_OIFNAME,
+	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/linux/gen_stats.h b/include/linux-private/linux/gen_stats.h
similarity index 82%
rename from include/linux/gen_stats.h
rename to include/linux-private/linux/gen_stats.h
index 552c8a0..24a861c 100644
--- a/include/linux/gen_stats.h
+++ b/include/linux-private/linux/gen_stats.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_GEN_STATS_H
 #define __LINUX_GEN_STATS_H
 
@@ -9,6 +10,8 @@
 	TCA_STATS_RATE_EST,
 	TCA_STATS_QUEUE,
 	TCA_STATS_APP,
+	TCA_STATS_RATE_EST64,
+	TCA_STATS_PAD,
 	__TCA_STATS_MAX,
 };
 #define TCA_STATS_MAX (__TCA_STATS_MAX - 1)
@@ -38,6 +41,16 @@
 };
 
 /**
+ * struct gnet_stats_rate_est64 - rate estimator
+ * @bps: current byte rate
+ * @pps: current packet rate
+ */
+struct gnet_stats_rate_est64 {
+	__u64	bps;
+	__u64	pps;
+};
+
+/**
  * struct gnet_stats_queue - queuing statistics
  * @qlen: queue length
  * @backlog: backlog size of queue
diff --git a/include/linux/genetlink.h b/include/linux-private/linux/genetlink.h
similarity index 83%
rename from include/linux/genetlink.h
rename to include/linux-private/linux/genetlink.h
index b834ef6..1317119 100644
--- a/include/linux/genetlink.h
+++ b/include/linux-private/linux/genetlink.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_GENERIC_NETLINK_H
 #define __LINUX_GENERIC_NETLINK_H
 
@@ -21,12 +22,16 @@
 #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
@@ -80,4 +85,5 @@
 
 #define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1)
 
-#endif	/* __LINUX_GENERIC_NETLINK_H */
+
+#endif /* __LINUX_GENERIC_NETLINK_H */
diff --git a/include/linux-private/linux/if.h b/include/linux-private/linux/if.h
new file mode 100644
index 0000000..495cdd2
--- /dev/null
+++ b/include/linux-private/linux/if.h
@@ -0,0 +1,293 @@
+/* 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.
+ *
+ *		Global definitions for the INET interface module.
+ *
+ * Version:	@(#)if.h	1.0.2	04/18/93
+ *
+ * Authors:	Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988
+ *		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_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
+ *
+ * These are the &struct net_device flags, they can be set by drivers, the
+ * kernel and some can be triggered by userspace. Userspace can query and
+ * set these flags using userspace utilities but there is also a sysfs
+ * entry available for all dev flags which can be queried and set. These flags
+ * 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
+ * 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.
+ *
+ * You should have a pretty good reason to be extending these flags.
+ *
+ * @IFF_UP: interface is up. Can be toggled through sysfs.
+ * @IFF_BROADCAST: broadcast address valid. Volatile.
+ * @IFF_DEBUG: turn on debugging. Can be toggled through sysfs.
+ * @IFF_LOOPBACK: is a loopback net. Volatile.
+ * @IFF_POINTOPOINT: interface is has p-p link. Volatile.
+ * @IFF_NOTRAILERS: avoid use of trailers. Can be toggled through sysfs.
+ *	Volatile.
+ * @IFF_RUNNING: interface RFC2863 OPER_UP. Volatile.
+ * @IFF_NOARP: no ARP protocol. Can be toggled through sysfs. Volatile.
+ * @IFF_PROMISC: receive all packets. Can be toggled through sysfs.
+ * @IFF_ALLMULTI: receive all multicast packets. Can be toggled through
+ *	sysfs.
+ * @IFF_MASTER: master of a load balancer. Volatile.
+ * @IFF_SLAVE: slave of a load balancer. Volatile.
+ * @IFF_MULTICAST: Supports multicast. Can be toggled through sysfs.
+ * @IFF_PORTSEL: can set media type. Can be toggled through sysfs.
+ * @IFF_AUTOMEDIA: auto media select active. Can be toggled through sysfs.
+ * @IFF_DYNAMIC: dialup device with changing addresses. Can be toggled
+ *	through sysfs.
+ * @IFF_LOWER_UP: driver signals L1 up. Volatile.
+ * @IFF_DORMANT: driver signals dormant. Volatile.
+ * @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 */
+	IFF_LOOPBACK			= 1<<3,  /* __volatile__ */
+	IFF_POINTOPOINT			= 1<<4,  /* __volatile__ */
+	IFF_NOTRAILERS			= 1<<5,  /* sysfs */
+	IFF_RUNNING			= 1<<6,  /* __volatile__ */
+	IFF_NOARP			= 1<<7,  /* sysfs */
+	IFF_PROMISC			= 1<<8,  /* sysfs */
+	IFF_ALLMULTI			= 1<<9,  /* sysfs */
+	IFF_MASTER			= 1<<10, /* __volatile__ */
+	IFF_SLAVE			= 1<<11, /* __volatile__ */
+	IFF_MULTICAST			= 1<<12, /* sysfs */
+	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
+#define IFF_LOOPBACK			IFF_LOOPBACK
+#define IFF_POINTOPOINT			IFF_POINTOPOINT
+#define IFF_NOTRAILERS			IFF_NOTRAILERS
+#define IFF_RUNNING			IFF_RUNNING
+#define IFF_NOARP			IFF_NOARP
+#define IFF_PROMISC			IFF_PROMISC
+#define IFF_ALLMULTI			IFF_ALLMULTI
+#define IFF_MASTER			IFF_MASTER
+#define IFF_SLAVE			IFF_SLAVE
+#define IFF_MULTICAST			IFF_MULTICAST
+#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)
+
+#define IF_GET_IFACE	0x0001		/* for querying only */
+#define IF_GET_PROTO	0x0002
+
+/* For definitions see hdlc.h */
+#define IF_IFACE_V35	0x1000		/* V.35 serial interface	*/
+#define IF_IFACE_V24	0x1001		/* V.24 serial interface	*/
+#define IF_IFACE_X21	0x1002		/* X.21 serial interface	*/
+#define IF_IFACE_T1	0x1003		/* T1 telco serial interface	*/
+#define IF_IFACE_E1	0x1004		/* E1 telco serial interface	*/
+#define IF_IFACE_SYNC_SERIAL 0x1005	/* can't be set by software	*/
+#define IF_IFACE_X21D   0x1006          /* X.21 Dual Clocking (FarSite) */
+
+/* For definitions see hdlc.h */
+#define IF_PROTO_HDLC	0x2000		/* raw HDLC protocol		*/
+#define IF_PROTO_PPP	0x2001		/* PPP protocol			*/
+#define IF_PROTO_CISCO	0x2002		/* Cisco HDLC protocol		*/
+#define IF_PROTO_FR	0x2003		/* Frame Relay protocol		*/
+#define IF_PROTO_FR_ADD_PVC 0x2004	/*    Create FR PVC		*/
+#define IF_PROTO_FR_DEL_PVC 0x2005	/*    Delete FR PVC		*/
+#define IF_PROTO_X25	0x2006		/* X.25				*/
+#define IF_PROTO_HDLC_ETH 0x2007	/* raw HDLC, Ethernet emulation	*/
+#define IF_PROTO_FR_ADD_ETH_PVC 0x2008	/*  Create FR Ethernet-bridged PVC */
+#define IF_PROTO_FR_DEL_ETH_PVC 0x2009	/*  Delete FR Ethernet-bridged PVC */
+#define IF_PROTO_FR_PVC	0x200A		/* for reading PVC status	*/
+#define IF_PROTO_FR_ETH_PVC 0x200B
+#define IF_PROTO_RAW    0x200C          /* RAW Socket                   */
+
+/* RFC 2863 operational status */
+enum {
+	IF_OPER_UNKNOWN,
+	IF_OPER_NOTPRESENT,
+	IF_OPER_DOWN,
+	IF_OPER_LOWERLAYERDOWN,
+	IF_OPER_TESTING,
+	IF_OPER_DORMANT,
+	IF_OPER_UP,
+};
+
+/* link modes */
+enum {
+	IF_LINK_MODE_DEFAULT,
+	IF_LINK_MODE_DORMANT,	/* limit upward transition to dormant */
+};
+
+/*
+ *	Device mapping structure. I'd just gone off and designed a 
+ *	beautiful scheme using only loadable modules with arguments
+ *	for driver options and along come the PCMCIA people 8)
+ *
+ *	Ah well. The get() side of this is good for WDSETUP, and it'll
+ *	be handy for debugging things. The set side is fine for now and
+ *	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;
+	unsigned short base_addr; 
+	unsigned char irq;
+	unsigned char dma;
+	unsigned char port;
+	/* 3 bytes spare */
+};
+#endif /* __UAPI_DEF_IF_IFMAP */
+
+struct if_settings {
+	unsigned int type;	/* Type of physical device or protocol */
+	unsigned int size;	/* Size of the data allocated by the caller */
+	union {
+		/* {atm/eth/dsl}_settings anyone ? */
+		raw_hdlc_proto		*raw_hdlc;
+		cisco_proto		*cisco;
+		fr_proto		*fr;
+		fr_proto_pvc		*fr_pvc;
+		fr_proto_pvc_info	*fr_pvc_info;
+
+		/* interface settings */
+		sync_serial_settings	*sync;
+		te1_settings		*te1;
+	} ifs_ifsu;
+};
+
+/*
+ * Interface request structure used for socket
+ * ioctl's.  All interface ioctl's must have parameter
+ * definitions which begin with ifr_name.  The
+ * remainder may be interface specific.
+ */
+
+/* for compatibility with glibc net/if.h */
+#if __UAPI_DEF_IF_IFREQ
+struct ifreq {
+#define IFHWADDRLEN	6
+	union
+	{
+		char	ifrn_name[IFNAMSIZ];		/* if name, e.g. "en0" */
+	} ifr_ifrn;
+	
+	union {
+		struct	sockaddr ifru_addr;
+		struct	sockaddr ifru_dstaddr;
+		struct	sockaddr ifru_broadaddr;
+		struct	sockaddr ifru_netmask;
+		struct  sockaddr ifru_hwaddr;
+		short	ifru_flags;
+		int	ifru_ivalue;
+		int	ifru_mtu;
+		struct  ifmap ifru_map;
+		char	ifru_slave[IFNAMSIZ];	/* Just fits the size */
+		char	ifru_newname[IFNAMSIZ];
+		void *	ifru_data;
+		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 		*/
+#define	ifr_addr	ifr_ifru.ifru_addr	/* address		*/
+#define	ifr_dstaddr	ifr_ifru.ifru_dstaddr	/* other end of p-p lnk	*/
+#define	ifr_broadaddr	ifr_ifru.ifru_broadaddr	/* broadcast address	*/
+#define	ifr_netmask	ifr_ifru.ifru_netmask	/* interface net mask	*/
+#define	ifr_flags	ifr_ifru.ifru_flags	/* flags		*/
+#define	ifr_metric	ifr_ifru.ifru_ivalue	/* metric		*/
+#define	ifr_mtu		ifr_ifru.ifru_mtu	/* mtu			*/
+#define ifr_map		ifr_ifru.ifru_map	/* device map		*/
+#define ifr_slave	ifr_ifru.ifru_slave	/* slave device		*/
+#define	ifr_data	ifr_ifru.ifru_data	/* for use by interface	*/
+#define ifr_ifindex	ifr_ifru.ifru_ivalue	/* interface index	*/
+#define ifr_bandwidth	ifr_ifru.ifru_ivalue    /* link bandwidth	*/
+#define ifr_qlen	ifr_ifru.ifru_ivalue	/* Queue length 	*/
+#define ifr_newname	ifr_ifru.ifru_newname	/* New name		*/
+#define ifr_settings	ifr_ifru.ifru_settings	/* Device/proto settings*/
+
+/*
+ * Structure used in SIOCGIFCONF request.
+ * Used to retrieve interface configuration
+ * for machine (useful for programs which
+ * 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 {
+		char *ifcu_buf;
+		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	*/
+
+#endif /* _LINUX_IF_H */
diff --git a/include/linux/if_addr.h b/include/linux-private/linux/if_addr.h
similarity index 78%
rename from include/linux/if_addr.h
rename to include/linux-private/linux/if_addr.h
index 7d4de85..a924606 100644
--- a/include/linux/if_addr.h
+++ b/include/linux-private/linux/if_addr.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_IF_ADDR_H
 #define __LINUX_IF_ADDR_H
 
@@ -32,6 +33,7 @@
 	IFA_CACHEINFO,
 	IFA_MULTICAST,
 	IFA_FLAGS,
+	IFA_RT_PRIORITY,  /* u32, priority/metric for prefix route */
 	__IFA_MAX,
 };
 
@@ -50,6 +52,8 @@
 #define IFA_F_PERMANENT		0x80
 #define IFA_F_MANAGETEMPADDR	0x100
 #define IFA_F_NOPREFIXROUTE	0x200
+#define IFA_F_MCAUTOJOIN	0x400
+#define IFA_F_STABLE_PRIVACY	0x800
 
 struct ifa_cacheinfo {
 	__u32	ifa_prefered;
@@ -58,4 +62,8 @@
 	__u32	tstamp; /* updated timestamp, hundredths of seconds */
 };
 
+/* backwards compatibility for userspace */
+#define IFA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
+
 #endif
diff --git a/include/linux/if_arp.h b/include/linux-private/linux/if_arp.h
similarity index 92%
rename from include/linux/if_arp.h
rename to include/linux-private/linux/if_arp.h
index e04cd2c..cd136a6 100644
--- a/include/linux/if_arp.h
+++ b/include/linux-private/linux/if_arp.h
@@ -1,3 +1,4 @@
+/* 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
@@ -59,6 +60,7 @@
 #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       		*/
@@ -87,10 +89,15 @@
 #define ARPHRD_IEEE80211_PRISM 802	/* IEEE 802.11 + Prism2 header  */
 #define ARPHRD_IEEE80211_RADIOTAP 803	/* IEEE 802.11 + radiotap header */
 #define ARPHRD_IEEE802154	  804
+#define ARPHRD_IEEE802154_MONITOR 805	/* IEEE 802.15.4 network monitor */
 
 #define ARPHRD_PHONET	820		/* PhoNet media type		*/
 #define ARPHRD_PHONET_PIPE 821		/* PhoNet pipe header		*/
 #define ARPHRD_CAIF	822		/* CAIF media type		*/
+#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 */
@@ -153,4 +160,5 @@
 
 };
 
-#endif	/* _LINUX_IF_ARP_H */
+
+#endif /* _LINUX_IF_ARP_H */
diff --git a/include/linux-private/linux/if_bridge.h b/include/linux-private/linux/if_bridge.h
new file mode 100644
index 0000000..bdfecf9
--- /dev/null
+++ b/include/linux-private/linux/if_bridge.h
@@ -0,0 +1,295 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.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_IF_BRIDGE_H
+#define _LINUX_IF_BRIDGE_H
+
+#include <linux/types.h>
+#include <linux/if_ether.h>
+#include <linux/in6.h>
+
+#define SYSFS_BRIDGE_ATTR	"bridge"
+#define SYSFS_BRIDGE_FDB	"brforward"
+#define SYSFS_BRIDGE_PORT_SUBDIR "brif"
+#define SYSFS_BRIDGE_PORT_ATTR	"brport"
+#define SYSFS_BRIDGE_PORT_LINK	"bridge"
+
+#define BRCTL_VERSION 1
+
+#define BRCTL_GET_VERSION 0
+#define BRCTL_GET_BRIDGES 1
+#define BRCTL_ADD_BRIDGE 2
+#define BRCTL_DEL_BRIDGE 3
+#define BRCTL_ADD_IF 4
+#define BRCTL_DEL_IF 5
+#define BRCTL_GET_BRIDGE_INFO 6
+#define BRCTL_GET_PORT_LIST 7
+#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8
+#define BRCTL_SET_BRIDGE_HELLO_TIME 9
+#define BRCTL_SET_BRIDGE_MAX_AGE 10
+#define BRCTL_SET_AGEING_TIME 11
+#define BRCTL_SET_GC_INTERVAL 12
+#define BRCTL_GET_PORT_INFO 13
+#define BRCTL_SET_BRIDGE_STP_STATE 14
+#define BRCTL_SET_BRIDGE_PRIORITY 15
+#define BRCTL_SET_PORT_PRIORITY 16
+#define BRCTL_SET_PATH_COST 17
+#define BRCTL_GET_FDB_ENTRIES 18
+
+#define BR_STATE_DISABLED 0
+#define BR_STATE_LISTENING 1
+#define BR_STATE_LEARNING 2
+#define BR_STATE_FORWARDING 3
+#define BR_STATE_BLOCKING 4
+
+struct __bridge_info {
+	__u64 designated_root;
+	__u64 bridge_id;
+	__u32 root_path_cost;
+	__u32 max_age;
+	__u32 hello_time;
+	__u32 forward_delay;
+	__u32 bridge_max_age;
+	__u32 bridge_hello_time;
+	__u32 bridge_forward_delay;
+	__u8 topology_change;
+	__u8 topology_change_detected;
+	__u8 root_port;
+	__u8 stp_enabled;
+	__u32 ageing_time;
+	__u32 gc_interval;
+	__u32 hello_timer_value;
+	__u32 tcn_timer_value;
+	__u32 topology_change_timer_value;
+	__u32 gc_timer_value;
+};
+
+struct __port_info {
+	__u64 designated_root;
+	__u64 designated_bridge;
+	__u16 port_id;
+	__u16 designated_port;
+	__u32 path_cost;
+	__u32 designated_cost;
+	__u8 state;
+	__u8 top_change_ack;
+	__u8 config_pending;
+	__u8 unused0;
+	__u32 message_age_timer_value;
+	__u32 forward_delay_timer_value;
+	__u32 hold_timer_value;
+};
+
+struct __fdb_entry {
+	__u8 mac_addr[ETH_ALEN];
+	__u8 port_no;
+	__u8 is_local;
+	__u32 ageing_timer_value;
+	__u8 port_hi;
+	__u8 pad0;
+	__u16 unused;
+};
+
+/* Bridge Flags */
+#define BRIDGE_FLAGS_MASTER	1	/* Bridge command to/from master */
+#define BRIDGE_FLAGS_SELF	2	/* Bridge command to/from lowerdev */
+
+#define BRIDGE_MODE_VEB		0	/* Default loopback mode */
+#define BRIDGE_MODE_VEPA	1	/* 802.1Qbg defined VEPA mode */
+#define BRIDGE_MODE_UNDEF	0xFFFF  /* mode undefined */
+
+/* Bridge management nested attributes
+ * [IFLA_AF_SPEC] = {
+ *     [IFLA_BRIDGE_FLAGS]
+ *     [IFLA_BRIDGE_MODE]
+ *     [IFLA_BRIDGE_VLAN_INFO]
+ * }
+ */
+enum {
+	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)
+
+#define BRIDGE_VLAN_INFO_MASTER	(1<<0)	/* Operate on Bridge device as well */
+#define BRIDGE_VLAN_INFO_PVID	(1<<1)	/* VLAN is PVID, ingress untagged */
+#define BRIDGE_VLAN_INFO_UNTAGGED	(1<<2)	/* VLAN egresses untagged */
+#define BRIDGE_VLAN_INFO_RANGE_BEGIN	(1<<3) /* VLAN is start of vlan range */
+#define BRIDGE_VLAN_INFO_RANGE_END	(1<<4) /* VLAN is end of vlan range */
+#define BRIDGE_VLAN_INFO_BRENTRY	(1<<5) /* Global bridge VLAN entry */
+
+struct bridge_vlan_info {
+	__u16 flags;
+	__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_ROUTER] = {
+ *    [MDBA_ROUTER_PORT] = {
+ *        u32 ifindex
+ *        [MDBA_ROUTER_PATTR attributes]
+ *    }
+ * }
+ */
+enum {
+	MDBA_UNSPEC,
+	MDBA_MDB,
+	MDBA_ROUTER,
+	__MDBA_MAX,
+};
+#define MDBA_MAX (__MDBA_MAX - 1)
+
+enum {
+	MDBA_MDB_UNSPEC,
+	MDBA_MDB_ENTRY,
+	__MDBA_MDB_MAX,
+};
+#define MDBA_MDB_MAX (__MDBA_MDB_MAX - 1)
+
+enum {
+	MDBA_MDB_ENTRY_UNSPEC,
+	MDBA_MDB_ENTRY_INFO,
+	__MDBA_MDB_ENTRY_MAX,
+};
+#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,
+	__MDBA_ROUTER_MAX,
+};
+#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;
+};
+
+struct br_mdb_entry {
+	__u32 ifindex;
+#define MDB_TEMPORARY 0
+#define MDB_PERMANENT 1
+	__u8 state;
+#define MDB_FLAGS_OFFLOAD	(1 << 0)
+	__u8 flags;
+	__u16 vid;
+	struct {
+		union {
+			__be32	ip4;
+			struct in6_addr ip6;
+		} u;
+		__be16		proto;
+	} addr;
+};
+
+enum {
+	MDBA_SET_ENTRY_UNSPEC,
+	MDBA_SET_ENTRY,
+	__MDBA_SET_ENTRY_MAX,
+};
+#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];
+};
+#endif /* _LINUX_IF_BRIDGE_H */
diff --git a/include/linux/if_ether.h b/include/linux-private/linux/if_ether.h
similarity index 70%
rename from include/linux/if_ether.h
rename to include/linux-private/linux/if_ether.h
index a6af32d..8c36f63 100644
--- a/include/linux/if_ether.h
+++ b/include/linux-private/linux/if_ether.h
@@ -1,3 +1,4 @@
+/* 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
@@ -29,12 +30,16 @@
  */
 
 #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.
  */
@@ -42,12 +47,15 @@
 #define ETH_P_LOOP	0x0060		/* Ethernet Loopback packet	*/
 #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	*/
 #define	ETH_P_BPQ	0x08FF		/* G8BPQ AX.25 Ethernet Packet	[ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_IEEEPUP	0x0a00		/* Xerox IEEE802.3 PUP packet */
 #define ETH_P_IEEEPUPAT	0x0a01		/* Xerox IEEE802.3 PUP Addr Trans packet */
+#define ETH_P_BATMAN	0x4305		/* B.A.T.M.A.N.-Advanced packet [ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_DEC       0x6000          /* DEC Assigned proto           */
 #define ETH_P_DNA_DL    0x6001          /* DEC DNA Dump/Load            */
 #define ETH_P_DNA_RC    0x6002          /* DEC DNA Remote Console       */
@@ -61,28 +69,51 @@
 #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 */
 #define ETH_P_SLOW	0x8809		/* Slow Protocol. See 802.3ad 43B */
 #define ETH_P_WCCP	0x883E		/* Web-cache coordination protocol
 					 * defined in draft-wilson-wrec-wccp-v2-00.txt */
-#define ETH_P_PPP_DISC	0x8863		/* PPPoE discovery messages     */
-#define ETH_P_PPP_SES	0x8864		/* PPPoE session messages	*/
 #define ETH_P_MPLS_UC	0x8847		/* MPLS Unicast traffic		*/
 #define ETH_P_MPLS_MC	0x8848		/* MPLS Multicast traffic	*/
 #define ETH_P_ATMMPOA	0x884c		/* MultiProtocol Over ATM	*/
+#define ETH_P_PPP_DISC	0x8863		/* PPPoE discovery messages     */
+#define ETH_P_PPP_SES	0x8864		/* PPPoE session messages	*/
 #define ETH_P_LINK_CTL	0x886c		/* HPNA, wlan link local tunnel */
 #define ETH_P_ATMFATE	0x8884		/* Frame-based ATM Transport
 					 * over Ethernet
 					 */
 #define ETH_P_PAE	0x888E		/* Port Access Entity (IEEE 802.1X) */
 #define ETH_P_AOE	0x88A2		/* ATA over Ethernet		*/
+#define ETH_P_8021AD	0x88A8          /* 802.1ad Service VLAN		*/
+#define ETH_P_802_EX1	0x88B5		/* 802.1 Local Experimental 1.  */
+#define ETH_P_PREAUTH	0x88C7		/* 802.11 Preauthentication */
 #define ETH_P_TIPC	0x88CA		/* TIPC 			*/
+#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_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
+					 * then the frame is Ethernet II. Else it is 802.3 */
 
 /*
  *	Non DIX types. Won't clash for 1500 types.
@@ -97,7 +128,8 @@
 #define ETH_P_WAN_PPP   0x0007          /* Dummy type for WAN PPP frames*/
 #define ETH_P_PPP_MP    0x0008          /* Dummy type for PPP MP frames */
 #define ETH_P_LOCALTALK 0x0009		/* Localtalk pseudo type 	*/
-#define ETH_P_CAN	0x000C		/* Controller Area Network      */
+#define ETH_P_CAN	0x000C		/* CAN: Controller Area Network */
+#define ETH_P_CANFD	0x000D		/* CANFD: CAN flexible data rate*/
 #define ETH_P_PPPTALK	0x0010		/* Dummy type for Atalk over PPP*/
 #define ETH_P_TR_802_2	0x0011		/* 802.2 frames 		*/
 #define ETH_P_MOBITEX	0x0015		/* Mobitex (kaz@cafe.net)	*/
@@ -111,15 +143,27 @@
 #define ETH_P_PHONET	0x00F5		/* Nokia Phonet frames          */
 #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 */
+
+#endif /* _LINUX_IF_ETHER_H */
diff --git a/include/linux-private/linux/if_link.h b/include/linux-private/linux/if_link.h
new file mode 100644
index 0000000..f4a9715
--- /dev/null
+++ b/include/linux-private/linux/if_link.h
@@ -0,0 +1,1000 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LINUX_IF_LINK_H
+#define _LINUX_IF_LINK_H
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+/* This struct should be in sync with struct rtnl_link_stats64 */
+struct rtnl_link_stats {
+	__u32	rx_packets;		/* total packets received	*/
+	__u32	tx_packets;		/* total packets transmitted	*/
+	__u32	rx_bytes;		/* total bytes received 	*/
+	__u32	tx_bytes;		/* total bytes transmitted	*/
+	__u32	rx_errors;		/* bad packets received		*/
+	__u32	tx_errors;		/* packet transmit problems	*/
+	__u32	rx_dropped;		/* no space in linux buffers	*/
+	__u32	tx_dropped;		/* no space available in linux	*/
+	__u32	multicast;		/* multicast packets received	*/
+	__u32	collisions;
+
+	/* detailed rx_errors: */
+	__u32	rx_length_errors;
+	__u32	rx_over_errors;		/* receiver ring buff overflow	*/
+	__u32	rx_crc_errors;		/* recved pkt with crc error	*/
+	__u32	rx_frame_errors;	/* recv'd frame alignment error */
+	__u32	rx_fifo_errors;		/* recv'r fifo overrun		*/
+	__u32	rx_missed_errors;	/* receiver missed packet	*/
+
+	/* detailed tx_errors */
+	__u32	tx_aborted_errors;
+	__u32	tx_carrier_errors;
+	__u32	tx_fifo_errors;
+	__u32	tx_heartbeat_errors;
+	__u32	tx_window_errors;
+
+	/* for cslip etc */
+	__u32	rx_compressed;
+	__u32	tx_compressed;
+
+	__u32	rx_nohandler;		/* dropped, no handler found	*/
+};
+
+/* The main device statistics structure */
+struct rtnl_link_stats64 {
+	__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;		/* no space in linux buffers	*/
+	__u64	tx_dropped;		/* no space available in linux	*/
+	__u64	multicast;		/* multicast packets received	*/
+	__u64	collisions;
+
+	/* detailed rx_errors: */
+	__u64	rx_length_errors;
+	__u64	rx_over_errors;		/* receiver ring buff overflow	*/
+	__u64	rx_crc_errors;		/* recved pkt with crc error	*/
+	__u64	rx_frame_errors;	/* recv'd frame alignment error */
+	__u64	rx_fifo_errors;		/* recv'r fifo overrun		*/
+	__u64	rx_missed_errors;	/* receiver missed packet	*/
+
+	/* detailed tx_errors */
+	__u64	tx_aborted_errors;
+	__u64	tx_carrier_errors;
+	__u64	tx_fifo_errors;
+	__u64	tx_heartbeat_errors;
+	__u64	tx_window_errors;
+
+	/* 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 */
+struct rtnl_link_ifmap {
+	__u64	mem_start;
+	__u64	mem_end;
+	__u64	base_addr;
+	__u16	irq;
+	__u8	dma;
+	__u8	port;
+};
+
+/*
+ * IFLA_AF_SPEC
+ *   Contains nested attributes for address family specific attributes.
+ *   Each address family may create a attribute with the address family
+ *   number as type and create its own attribute structure in it.
+ *
+ *   Example:
+ *   [IFLA_AF_SPEC] = {
+ *       [AF_INET] = {
+ *           [IFLA_INET_CONF] = ...,
+ *       },
+ *       [AF_INET6] = {
+ *           [IFLA_INET6_FLAGS] = ...,
+ *           [IFLA_INET6_CONF] = ...,
+ *       }
+ *   }
+ */
+
+enum {
+	IFLA_UNSPEC,
+	IFLA_ADDRESS,
+	IFLA_BROADCAST,
+	IFLA_IFNAME,
+	IFLA_MTU,
+	IFLA_LINK,
+	IFLA_QDISC,
+	IFLA_STATS,
+	IFLA_COST,
+#define IFLA_COST IFLA_COST
+	IFLA_PRIORITY,
+#define IFLA_PRIORITY IFLA_PRIORITY
+	IFLA_MASTER,
+#define IFLA_MASTER IFLA_MASTER
+	IFLA_WIRELESS,		/* Wireless Extension event - see wireless.h */
+#define IFLA_WIRELESS IFLA_WIRELESS
+	IFLA_PROTINFO,		/* Protocol specific information for a link */
+#define IFLA_PROTINFO IFLA_PROTINFO
+	IFLA_TXQLEN,
+#define IFLA_TXQLEN IFLA_TXQLEN
+	IFLA_MAP,
+#define IFLA_MAP IFLA_MAP
+	IFLA_WEIGHT,
+#define IFLA_WEIGHT IFLA_WEIGHT
+	IFLA_OPERSTATE,
+	IFLA_LINKMODE,
+	IFLA_LINKINFO,
+#define IFLA_LINKINFO IFLA_LINKINFO
+	IFLA_NET_NS_PID,
+	IFLA_IFALIAS,
+	IFLA_NUM_VF,		/* Number of VFs if device is SR-IOV PF */
+	IFLA_VFINFO_LIST,
+	IFLA_STATS64,
+	IFLA_VF_PORTS,
+	IFLA_PORT_SELF,
+	IFLA_AF_SPEC,
+	IFLA_GROUP,		/* Group the device belongs to */
+	IFLA_NET_NS_FD,
+	IFLA_EXT_MASK,		/* Extended info mask, VFs, etc */
+	IFLA_PROMISCUITY,	/* Promiscuity count: > 0 means acts PROMISC */
+#define IFLA_PROMISCUITY IFLA_PROMISCUITY
+	IFLA_NUM_TX_QUEUES,
+	IFLA_NUM_RX_QUEUES,
+	IFLA_CARRIER,
+	IFLA_PHYS_PORT_ID,
+	IFLA_CARRIER_CHANGES,
+	IFLA_PHYS_SWITCH_ID,
+	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_CARRIER_UP_COUNT,
+	IFLA_CARRIER_DOWN_COUNT,
+	IFLA_NEW_IFINDEX,
+	IFLA_MIN_MTU,
+	IFLA_MAX_MTU,
+	__IFLA_MAX
+};
+
+
+#define IFLA_MAX (__IFLA_MAX - 1)
+
+/* backwards compatibility for userspace */
+#define IFLA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
+
+enum {
+	IFLA_INET_UNSPEC,
+	IFLA_INET_CONF,
+	__IFLA_INET_MAX,
+};
+
+#define IFLA_INET_MAX (__IFLA_INET_MAX - 1)
+
+/* ifi_flags.
+
+   IFF_* flags.
+
+   The only change is:
+   IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are
+   more not changeable by user. They describe link media
+   characteristics and set by device driver.
+
+   Comments:
+   - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid
+   - If neither of these three flags are set;
+     the interface is NBMA.
+
+   - IFF_MULTICAST does not mean anything special:
+   multicasts can be used on all not-NBMA links.
+   IFF_MULTICAST means that this media uses special encapsulation
+   for multicast frames. Apparently, all IFF_POINTOPOINT and
+   IFF_BROADCAST devices are able to use multicasts too.
+ */
+
+/* IFLA_LINK.
+   For usual devices it is equal ifi_index.
+   If it is a "virtual interface" (f.e. tunnel), ifi_link
+   can point to real physical interface (f.e. for bandwidth calculations),
+   or maybe 0, what means, that real media is unknown (usual
+   for IPIP tunnels, when route to endpoint is allowed to change)
+ */
+
+/* Subtype attributes for IFLA_PROTINFO */
+enum {
+	IFLA_INET6_UNSPEC,
+	IFLA_INET6_FLAGS,	/* link flags			*/
+	IFLA_INET6_CONF,	/* sysctl parameters		*/
+	IFLA_INET6_STATS,	/* statistics			*/
+	IFLA_INET6_MCAST,	/* MC things. What of them?	*/
+	IFLA_INET6_CACHEINFO,	/* time values and max reasm size */
+	IFLA_INET6_ICMP6STATS,	/* statistics (icmpv6)		*/
+	IFLA_INET6_TOKEN,	/* device token			*/
+	IFLA_INET6_ADDR_GEN_MODE, /* implicit address generator mode */
+	__IFLA_INET6_MAX
+};
+
+#define IFLA_INET6_MAX	(__IFLA_INET6_MAX - 1)
+
+enum in6_addr_gen_mode {
+	IN6_ADDR_GEN_MODE_EUI64,
+	IN6_ADDR_GEN_MODE_NONE,
+	IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
+	IN6_ADDR_GEN_MODE_RANDOM,
+};
+
+/* Bridge section */
+
+enum {
+	IFLA_BR_UNSPEC,
+	IFLA_BR_FORWARD_DELAY,
+	IFLA_BR_HELLO_TIME,
+	IFLA_BR_MAX_AGE,
+	IFLA_BR_AGEING_TIME,
+	IFLA_BR_STP_STATE,
+	IFLA_BR_PRIORITY,
+	IFLA_BR_VLAN_FILTERING,
+	IFLA_BR_VLAN_PROTOCOL,
+	IFLA_BR_GROUP_FWD_MASK,
+	IFLA_BR_ROOT_ID,
+	IFLA_BR_BRIDGE_ID,
+	IFLA_BR_ROOT_PORT,
+	IFLA_BR_ROOT_PATH_COST,
+	IFLA_BR_TOPOLOGY_CHANGE,
+	IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
+	IFLA_BR_HELLO_TIMER,
+	IFLA_BR_TCN_TIMER,
+	IFLA_BR_TOPOLOGY_CHANGE_TIMER,
+	IFLA_BR_GC_TIMER,
+	IFLA_BR_GROUP_ADDR,
+	IFLA_BR_FDB_FLUSH,
+	IFLA_BR_MCAST_ROUTER,
+	IFLA_BR_MCAST_SNOOPING,
+	IFLA_BR_MCAST_QUERY_USE_IFADDR,
+	IFLA_BR_MCAST_QUERIER,
+	IFLA_BR_MCAST_HASH_ELASTICITY,
+	IFLA_BR_MCAST_HASH_MAX,
+	IFLA_BR_MCAST_LAST_MEMBER_CNT,
+	IFLA_BR_MCAST_STARTUP_QUERY_CNT,
+	IFLA_BR_MCAST_LAST_MEMBER_INTVL,
+	IFLA_BR_MCAST_MEMBERSHIP_INTVL,
+	IFLA_BR_MCAST_QUERIER_INTVL,
+	IFLA_BR_MCAST_QUERY_INTVL,
+	IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
+	IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
+	IFLA_BR_NF_CALL_IPTABLES,
+	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_MAX,
+};
+
+#define IFLA_BR_MAX	(__IFLA_BR_MAX - 1)
+
+struct ifla_bridge_id {
+	__u8	prio[2];
+	__u8	addr[6]; /* ETH_ALEN */
+};
+
+enum {
+	BRIDGE_MODE_UNSPEC,
+	BRIDGE_MODE_HAIRPIN,
+};
+
+enum {
+	IFLA_BRPORT_UNSPEC,
+	IFLA_BRPORT_STATE,	/* Spanning tree state     */
+	IFLA_BRPORT_PRIORITY,	/* "             priority  */
+	IFLA_BRPORT_COST,	/* "             cost      */
+	IFLA_BRPORT_MODE,	/* mode (hairpin)          */
+	IFLA_BRPORT_GUARD,	/* bpdu guard              */
+	IFLA_BRPORT_PROTECT,	/* root port protection    */
+	IFLA_BRPORT_FAST_LEAVE,	/* multicast fast leave    */
+	IFLA_BRPORT_LEARNING,	/* mac learning */
+	IFLA_BRPORT_UNICAST_FLOOD, /* flood unicast traffic */
+	IFLA_BRPORT_PROXYARP,	/* proxy ARP */
+	IFLA_BRPORT_LEARNING_SYNC, /* mac learning sync from device */
+	IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */
+	IFLA_BRPORT_ROOT_ID,	/* designated root */
+	IFLA_BRPORT_BRIDGE_ID,	/* designated bridge */
+	IFLA_BRPORT_DESIGNATED_PORT,
+	IFLA_BRPORT_DESIGNATED_COST,
+	IFLA_BRPORT_ID,
+	IFLA_BRPORT_NO,
+	IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
+	IFLA_BRPORT_CONFIG_PENDING,
+	IFLA_BRPORT_MESSAGE_AGE_TIMER,
+	IFLA_BRPORT_FORWARD_DELAY_TIMER,
+	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)
+
+struct ifla_cacheinfo {
+	__u32	max_reasm_len;
+	__u32	tstamp;		/* ipv6InterfaceTable updated timestamp */
+	__u32	reachable_time;
+	__u32	retrans_time;
+};
+
+enum {
+	IFLA_INFO_UNSPEC,
+	IFLA_INFO_KIND,
+	IFLA_INFO_DATA,
+	IFLA_INFO_XSTATS,
+	IFLA_INFO_SLAVE_KIND,
+	IFLA_INFO_SLAVE_DATA,
+	__IFLA_INFO_MAX,
+};
+
+#define IFLA_INFO_MAX	(__IFLA_INFO_MAX - 1)
+
+/* VLAN section */
+
+enum {
+	IFLA_VLAN_UNSPEC,
+	IFLA_VLAN_ID,
+	IFLA_VLAN_FLAGS,
+	IFLA_VLAN_EGRESS_QOS,
+	IFLA_VLAN_INGRESS_QOS,
+	IFLA_VLAN_PROTOCOL,
+	__IFLA_VLAN_MAX,
+};
+
+#define IFLA_VLAN_MAX	(__IFLA_VLAN_MAX - 1)
+
+struct ifla_vlan_flags {
+	__u32	flags;
+	__u32	mask;
+};
+
+enum {
+	IFLA_VLAN_QOS_UNSPEC,
+	IFLA_VLAN_QOS_MAPPING,
+	__IFLA_VLAN_QOS_MAX
+};
+
+#define IFLA_VLAN_QOS_MAX	(__IFLA_VLAN_QOS_MAX - 1)
+
+struct ifla_vlan_qos_mapping {
+	__u32 from;
+	__u32 to;
+};
+
+/* MACVLAN section */
+enum {
+	IFLA_MACVLAN_UNSPEC,
+	IFLA_MACVLAN_MODE,
+	IFLA_MACVLAN_FLAGS,
+	IFLA_MACVLAN_MACADDR_MODE,
+	IFLA_MACVLAN_MACADDR,
+	IFLA_MACVLAN_MACADDR_DATA,
+	IFLA_MACVLAN_MACADDR_COUNT,
+	__IFLA_MACVLAN_MAX,
+};
+
+#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1)
+
+enum macvlan_mode {
+	MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */
+	MACVLAN_MODE_VEPA    = 2, /* talk to other ports through ext bridge */
+	MACVLAN_MODE_BRIDGE  = 4, /* talk to bridge ports directly */
+	MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */
+	MACVLAN_MODE_SOURCE  = 16,/* use source MAC address list to assign */
+};
+
+enum macvlan_macaddr_mode {
+	MACVLAN_MACADDR_ADD,
+	MACVLAN_MACADDR_DEL,
+	MACVLAN_MACADDR_FLUSH,
+	MACVLAN_MACADDR_SET,
+};
+
+#define MACVLAN_FLAG_NOPROMISC	1
+
+/* VRF section */
+enum {
+	IFLA_VRF_UNSPEC,
+	IFLA_VRF_TABLE,
+	__IFLA_VRF_MAX
+};
+
+#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
+};
+
+#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
+
+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,
+	IFLA_VXLAN_ID,
+	IFLA_VXLAN_GROUP,	/* group or remote address */
+	IFLA_VXLAN_LINK,
+	IFLA_VXLAN_LOCAL,
+	IFLA_VXLAN_TTL,
+	IFLA_VXLAN_TOS,
+	IFLA_VXLAN_LEARNING,
+	IFLA_VXLAN_AGEING,
+	IFLA_VXLAN_LIMIT,
+	IFLA_VXLAN_PORT_RANGE,	/* source port */
+	IFLA_VXLAN_PROXY,
+	IFLA_VXLAN_RSC,
+	IFLA_VXLAN_L2MISS,
+	IFLA_VXLAN_L3MISS,
+	IFLA_VXLAN_PORT,	/* destination port */
+	IFLA_VXLAN_GROUP6,
+	IFLA_VXLAN_LOCAL6,
+	IFLA_VXLAN_UDP_CSUM,
+	IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
+	IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
+	IFLA_VXLAN_REMCSUM_TX,
+	IFLA_VXLAN_REMCSUM_RX,
+	IFLA_VXLAN_GBP,
+	IFLA_VXLAN_REMCSUM_NOPARTIAL,
+	IFLA_VXLAN_COLLECT_METADATA,
+	IFLA_VXLAN_LABEL,
+	IFLA_VXLAN_GPE,
+	IFLA_VXLAN_TTL_INHERIT,
+	__IFLA_VXLAN_MAX
+};
+#define IFLA_VXLAN_MAX	(__IFLA_VXLAN_MAX - 1)
+
+struct ifla_vxlan_port_range {
+	__be16	low;
+	__be16	high;
+};
+
+/* GENEVE section */
+enum {
+	IFLA_GENEVE_UNSPEC,
+	IFLA_GENEVE_ID,
+	IFLA_GENEVE_REMOTE,
+	IFLA_GENEVE_TTL,
+	IFLA_GENEVE_TOS,
+	IFLA_GENEVE_PORT,	/* destination port */
+	IFLA_GENEVE_COLLECT_METADATA,
+	IFLA_GENEVE_REMOTE6,
+	IFLA_GENEVE_UDP_CSUM,
+	IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
+	IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
+	IFLA_GENEVE_LABEL,
+	__IFLA_GENEVE_MAX
+};
+#define IFLA_GENEVE_MAX	(__IFLA_GENEVE_MAX - 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 {
+	IFLA_BOND_UNSPEC,
+	IFLA_BOND_MODE,
+	IFLA_BOND_ACTIVE_SLAVE,
+	IFLA_BOND_MIIMON,
+	IFLA_BOND_UPDELAY,
+	IFLA_BOND_DOWNDELAY,
+	IFLA_BOND_USE_CARRIER,
+	IFLA_BOND_ARP_INTERVAL,
+	IFLA_BOND_ARP_IP_TARGET,
+	IFLA_BOND_ARP_VALIDATE,
+	IFLA_BOND_ARP_ALL_TARGETS,
+	IFLA_BOND_PRIMARY,
+	IFLA_BOND_PRIMARY_RESELECT,
+	IFLA_BOND_FAIL_OVER_MAC,
+	IFLA_BOND_XMIT_HASH_POLICY,
+	IFLA_BOND_RESEND_IGMP,
+	IFLA_BOND_NUM_PEER_NOTIF,
+	IFLA_BOND_ALL_SLAVES_ACTIVE,
+	IFLA_BOND_MIN_LINKS,
+	IFLA_BOND_LP_INTERVAL,
+	IFLA_BOND_PACKETS_PER_SLAVE,
+	IFLA_BOND_AD_LACP_RATE,
+	IFLA_BOND_AD_SELECT,
+	IFLA_BOND_AD_INFO,
+	IFLA_BOND_AD_ACTOR_SYS_PRIO,
+	IFLA_BOND_AD_USER_PORT_KEY,
+	IFLA_BOND_AD_ACTOR_SYSTEM,
+	IFLA_BOND_TLB_DYNAMIC_LB,
+	__IFLA_BOND_MAX,
+};
+
+#define IFLA_BOND_MAX	(__IFLA_BOND_MAX - 1)
+
+enum {
+	IFLA_BOND_AD_INFO_UNSPEC,
+	IFLA_BOND_AD_INFO_AGGREGATOR,
+	IFLA_BOND_AD_INFO_NUM_PORTS,
+	IFLA_BOND_AD_INFO_ACTOR_KEY,
+	IFLA_BOND_AD_INFO_PARTNER_KEY,
+	IFLA_BOND_AD_INFO_PARTNER_MAC,
+	__IFLA_BOND_AD_INFO_MAX,
+};
+
+#define IFLA_BOND_AD_INFO_MAX	(__IFLA_BOND_AD_INFO_MAX - 1)
+
+enum {
+	IFLA_BOND_SLAVE_UNSPEC,
+	IFLA_BOND_SLAVE_STATE,
+	IFLA_BOND_SLAVE_MII_STATUS,
+	IFLA_BOND_SLAVE_LINK_FAILURE_COUNT,
+	IFLA_BOND_SLAVE_PERM_HWADDR,
+	IFLA_BOND_SLAVE_QUEUE_ID,
+	IFLA_BOND_SLAVE_AD_AGGREGATOR_ID,
+	IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE,
+	IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE,
+	__IFLA_BOND_SLAVE_MAX,
+};
+
+#define IFLA_BOND_SLAVE_MAX	(__IFLA_BOND_SLAVE_MAX - 1)
+
+/* SR-IOV virtual function management section */
+
+enum {
+	IFLA_VF_INFO_UNSPEC,
+	IFLA_VF_INFO,
+	__IFLA_VF_INFO_MAX,
+};
+
+#define IFLA_VF_INFO_MAX (__IFLA_VF_INFO_MAX - 1)
+
+enum {
+	IFLA_VF_UNSPEC,
+	IFLA_VF_MAC,		/* Hardware queue specific attributes */
+	IFLA_VF_VLAN,		/* VLAN ID and QoS */
+	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 */
+	IFLA_VF_RATE,		/* Min and Max TX Bandwidth Allocation */
+	IFLA_VF_RSS_QUERY_EN,	/* RSS Redirection Table and Hash Key query
+				 * on/off switch
+				 */
+	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_MAX,
+};
+
+#define IFLA_VF_MAX (__IFLA_VF_MAX - 1)
+
+struct ifla_vf_mac {
+	__u32 vf;
+	__u8 mac[32]; /* MAX_ADDR_LEN */
+};
+
+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 */
+};
+
+struct ifla_vf_rate {
+	__u32 vf;
+	__u32 min_tx_rate; /* Min Bandwidth in Mbps */
+	__u32 max_tx_rate; /* Max Bandwidth in Mbps */
+};
+
+struct ifla_vf_spoofchk {
+	__u32 vf;
+	__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 */
+	IFLA_VF_LINK_STATE_DISABLE,	/* link always down */
+	__IFLA_VF_LINK_STATE_MAX,
+};
+
+struct ifla_vf_link_state {
+	__u32 vf;
+	__u32 link_state;
+};
+
+struct ifla_vf_rss_query_en {
+	__u32 vf;
+	__u32 setting;
+};
+
+enum {
+	IFLA_VF_STATS_RX_PACKETS,
+	IFLA_VF_STATS_TX_PACKETS,
+	IFLA_VF_STATS_RX_BYTES,
+	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,
+};
+
+#define IFLA_VF_STATS_MAX (__IFLA_VF_STATS_MAX - 1)
+
+struct ifla_vf_trust {
+	__u32 vf;
+	__u32 setting;
+};
+
+/* VF ports management section
+ *
+ *	Nested layout of set/get msg is:
+ *
+ *		[IFLA_NUM_VF]
+ *		[IFLA_VF_PORTS]
+ *			[IFLA_VF_PORT]
+ *				[IFLA_PORT_*], ...
+ *			[IFLA_VF_PORT]
+ *				[IFLA_PORT_*], ...
+ *			...
+ *		[IFLA_PORT_SELF]
+ *			[IFLA_PORT_*], ...
+ */
+
+enum {
+	IFLA_VF_PORT_UNSPEC,
+	IFLA_VF_PORT,			/* nest */
+	__IFLA_VF_PORT_MAX,
+};
+
+#define IFLA_VF_PORT_MAX (__IFLA_VF_PORT_MAX - 1)
+
+enum {
+	IFLA_PORT_UNSPEC,
+	IFLA_PORT_VF,			/* __u32 */
+	IFLA_PORT_PROFILE,		/* string */
+	IFLA_PORT_VSI_TYPE,		/* 802.1Qbg (pre-)standard VDP */
+	IFLA_PORT_INSTANCE_UUID,	/* binary UUID */
+	IFLA_PORT_HOST_UUID,		/* binary UUID */
+	IFLA_PORT_REQUEST,		/* __u8 */
+	IFLA_PORT_RESPONSE,		/* __u16, output only */
+	__IFLA_PORT_MAX,
+};
+
+#define IFLA_PORT_MAX (__IFLA_PORT_MAX - 1)
+
+#define PORT_PROFILE_MAX	40
+#define PORT_UUID_MAX		16
+#define PORT_SELF_VF		-1
+
+enum {
+	PORT_REQUEST_PREASSOCIATE = 0,
+	PORT_REQUEST_PREASSOCIATE_RR,
+	PORT_REQUEST_ASSOCIATE,
+	PORT_REQUEST_DISASSOCIATE,
+};
+
+enum {
+	PORT_VDP_RESPONSE_SUCCESS = 0,
+	PORT_VDP_RESPONSE_INVALID_FORMAT,
+	PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES,
+	PORT_VDP_RESPONSE_UNUSED_VTID,
+	PORT_VDP_RESPONSE_VTID_VIOLATION,
+	PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION,
+	PORT_VDP_RESPONSE_OUT_OF_SYNC,
+	/* 0x08-0xFF reserved for future VDP use */
+	PORT_PROFILE_RESPONSE_SUCCESS = 0x100,
+	PORT_PROFILE_RESPONSE_INPROGRESS,
+	PORT_PROFILE_RESPONSE_INVALID,
+	PORT_PROFILE_RESPONSE_BADSTATE,
+	PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES,
+	PORT_PROFILE_RESPONSE_ERROR,
+};
+
+struct ifla_port_vsi {
+	__u8 vsi_mgr_id;
+	__u8 vsi_type_id[3];
+	__u8 vsi_type_version;
+	__u8 pad[3];
+};
+
+
+/* IPoIB section */
+
+enum {
+	IFLA_IPOIB_UNSPEC,
+	IFLA_IPOIB_PKEY,
+	IFLA_IPOIB_MODE,
+	IFLA_IPOIB_UMCAST,
+	__IFLA_IPOIB_MAX
+};
+
+enum {
+	IPOIB_MODE_DATAGRAM  = 0, /* using unreliable datagram QPs */
+	IPOIB_MODE_CONNECTED = 1, /* using connected QPs */
+};
+
+#define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1)
+
+
+/* HSR section */
+
+enum {
+	IFLA_HSR_UNSPEC,
+	IFLA_HSR_SLAVE1,
+	IFLA_HSR_SLAVE2,
+	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_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/linux-private/linux/if_macsec.h b/include/linux-private/linux/if_macsec.h
new file mode 100644
index 0000000..7743993
--- /dev/null
+++ b/include/linux-private/linux/if_macsec.h
@@ -0,0 +1,177 @@
+/* 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/linux/if_tunnel.h b/include/linux-private/linux/if_tunnel.h
similarity index 61%
rename from include/linux/if_tunnel.h
rename to include/linux-private/linux/if_tunnel.h
index aee73d0..ecdc766 100644
--- a/include/linux/if_tunnel.h
+++ b/include/linux-private/linux/if_tunnel.h
@@ -1,7 +1,11 @@
-#ifndef _UAPI_IF_TUNNEL_H_
-#define _UAPI_IF_TUNNEL_H_
+/* 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>
 
 
@@ -24,9 +28,23 @@
 #define GRE_SEQ		__cpu_to_be16(0x1000)
 #define GRE_STRICT	__cpu_to_be16(0x0800)
 #define GRE_REC		__cpu_to_be16(0x0700)
-#define GRE_FLAGS	__cpu_to_be16(0x00F8)
+#define GRE_ACK		__cpu_to_be16(0x0080)
+#define GRE_FLAGS	__cpu_to_be16(0x0078)
 #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;
@@ -53,10 +71,27 @@
 	IFLA_IPTUN_6RD_RELAY_PREFIX,
 	IFLA_IPTUN_6RD_PREFIXLEN,
 	IFLA_IPTUN_6RD_RELAY_PREFIXLEN,
+	IFLA_IPTUN_ENCAP_TYPE,
+	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)
 
+enum tunnel_encap_types {
+	TUNNEL_ENCAP_NONE,
+	TUNNEL_ENCAP_FOU,
+	TUNNEL_ENCAP_GUE,
+	TUNNEL_ENCAP_MPLS,
+};
+
+#define TUNNEL_ENCAP_FLAG_CSUM		(1<<0)
+#define TUNNEL_ENCAP_FLAG_CSUM6		(1<<1)
+#define TUNNEL_ENCAP_FLAG_REMCSUM	(1<<2)
+
 /* SIT-mode i_flags */
 #define	SIT_ISATAP	0x0001
 
@@ -94,13 +129,24 @@
 	IFLA_GRE_ENCAP_LIMIT,
 	IFLA_GRE_FLOWINFO,
 	IFLA_GRE_FLAGS,
+	IFLA_GRE_ENCAP_TYPE,
+	IFLA_GRE_ENCAP_FLAGS,
+	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,
 };
 
 #define IFLA_GRE_MAX	(__IFLA_GRE_MAX - 1)
 
 /* VTI-mode i_flags */
-#define VTI_ISVTI 0x0001
+#define VTI_ISVTI ((__be16)0x0001)
 
 enum {
 	IFLA_VTI_UNSPEC,
@@ -109,8 +155,9 @@
 	IFLA_VTI_OKEY,
 	IFLA_VTI_LOCAL,
 	IFLA_VTI_REMOTE,
+	IFLA_VTI_FWMARK,
 	__IFLA_VTI_MAX,
 };
 
 #define IFLA_VTI_MAX	(__IFLA_VTI_MAX - 1)
-#endif /* _UAPI_IF_TUNNEL_H_ */
+#endif /* _IF_TUNNEL_H_ */
diff --git a/include/linux/if_vlan.h b/include/linux-private/linux/if_vlan.h
similarity index 90%
rename from include/linux/if_vlan.h
rename to include/linux-private/linux/if_vlan.h
index 67affd1..18a15da 100644
--- a/include/linux/if_vlan.h
+++ b/include/linux-private/linux/if_vlan.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
 /*
  * VLAN		An implementation of 802.1Q VLAN tagging.
  *
@@ -13,6 +14,7 @@
 #ifndef _LINUX_IF_VLAN_H_
 #define _LINUX_IF_VLAN_H_
 
+
 /* VLAN IOCTLs are found in sockios.h */
 
 /* Passed in vlan_ioctl_args structure to determine behaviour. */
@@ -33,6 +35,7 @@
 	VLAN_FLAG_REORDER_HDR	= 0x1,
 	VLAN_FLAG_GVRP		= 0x2,
 	VLAN_FLAG_LOOSE_BINDING	= 0x4,
+	VLAN_FLAG_MVRP		= 0x8,
 };
 
 enum vlan_name_types {
@@ -53,10 +56,10 @@
 		unsigned int skb_priority;
 		unsigned int name_type;
 		unsigned int bind_type;
-		unsigned int flag; /* Matches vlan_dev_info flags */
+		unsigned int flag; /* Matches vlan_dev_priv flags */
         } u;
 
 	short vlan_qos;   
 };
 
-#endif /* !(_LINUX_IF_VLAN_H_) */
+#endif /* _LINUX_IF_VLAN_H_ */
diff --git a/include/linux-private/linux/in.h b/include/linux-private/linux/in.h
new file mode 100644
index 0000000..a4f143b
--- /dev/null
+++ b/include/linux-private/linux/in.h
@@ -0,0 +1,301 @@
+/* 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 Internet Protocol.
+ *
+ * Version:	@(#)in.h	1.0.1	04/21/93
+ *
+ * Authors:	Original taken from the GNU Project <netinet/in.h> file.
+ *		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_IN_H
+#define _LINUX_IN_H
+
+#include <linux/types.h>
+#include <linux/libc-compat.h>
+#include <linux/socket.h>
+
+#if __UAPI_DEF_IN_IPPROTO
+/* Standard well-defined IP protocols.  */
+enum {
+  IPPROTO_IP = 0,		/* Dummy protocol for TCP		*/
+#define IPPROTO_IP		IPPROTO_IP
+  IPPROTO_ICMP = 1,		/* Internet Control Message Protocol	*/
+#define IPPROTO_ICMP		IPPROTO_ICMP
+  IPPROTO_IGMP = 2,		/* Internet Group Management Protocol	*/
+#define IPPROTO_IGMP		IPPROTO_IGMP
+  IPPROTO_IPIP = 4,		/* IPIP tunnels (older KA9Q tunnels use 94) */
+#define IPPROTO_IPIP		IPPROTO_IPIP
+  IPPROTO_TCP = 6,		/* Transmission Control Protocol	*/
+#define IPPROTO_TCP		IPPROTO_TCP
+  IPPROTO_EGP = 8,		/* Exterior Gateway Protocol		*/
+#define IPPROTO_EGP		IPPROTO_EGP
+  IPPROTO_PUP = 12,		/* PUP protocol				*/
+#define IPPROTO_PUP		IPPROTO_PUP
+  IPPROTO_UDP = 17,		/* User Datagram Protocol		*/
+#define IPPROTO_UDP		IPPROTO_UDP
+  IPPROTO_IDP = 22,		/* XNS IDP protocol			*/
+#define IPPROTO_IDP		IPPROTO_IDP
+  IPPROTO_TP = 29,		/* SO Transport Protocol Class 4	*/
+#define IPPROTO_TP		IPPROTO_TP
+  IPPROTO_DCCP = 33,		/* Datagram Congestion Control Protocol */
+#define IPPROTO_DCCP		IPPROTO_DCCP
+  IPPROTO_IPV6 = 41,		/* IPv6-in-IPv4 tunnelling		*/
+#define IPPROTO_IPV6		IPPROTO_IPV6
+  IPPROTO_RSVP = 46,		/* RSVP Protocol			*/
+#define IPPROTO_RSVP		IPPROTO_RSVP
+  IPPROTO_GRE = 47,		/* Cisco GRE tunnels (rfc 1701,1702)	*/
+#define IPPROTO_GRE		IPPROTO_GRE
+  IPPROTO_ESP = 50,		/* Encapsulation Security Payload protocol */
+#define IPPROTO_ESP		IPPROTO_ESP
+  IPPROTO_AH = 51,		/* Authentication Header protocol	*/
+#define IPPROTO_AH		IPPROTO_AH
+  IPPROTO_MTP = 92,		/* Multicast Transport Protocol		*/
+#define IPPROTO_MTP		IPPROTO_MTP
+  IPPROTO_BEETPH = 94,		/* IP option pseudo header for BEET	*/
+#define IPPROTO_BEETPH		IPPROTO_BEETPH
+  IPPROTO_ENCAP = 98,		/* Encapsulation Header			*/
+#define IPPROTO_ENCAP		IPPROTO_ENCAP
+  IPPROTO_PIM = 103,		/* Protocol Independent Multicast	*/
+#define IPPROTO_PIM		IPPROTO_PIM
+  IPPROTO_COMP = 108,		/* Compression Header Protocol		*/
+#define IPPROTO_COMP		IPPROTO_COMP
+  IPPROTO_SCTP = 132,		/* Stream Control Transport Protocol	*/
+#define IPPROTO_SCTP		IPPROTO_SCTP
+  IPPROTO_UDPLITE = 136,	/* UDP-Lite (RFC 3828)			*/
+#define IPPROTO_UDPLITE		IPPROTO_UDPLITE
+  IPPROTO_MPLS = 137,		/* MPLS in IP (RFC 4023)		*/
+#define IPPROTO_MPLS		IPPROTO_MPLS
+  IPPROTO_RAW = 255,		/* Raw IP packets			*/
+#define IPPROTO_RAW		IPPROTO_RAW
+  IPPROTO_MAX
+};
+#endif
+
+#if __UAPI_DEF_IN_ADDR
+/* Internet address. */
+struct in_addr {
+	__be32	s_addr;
+};
+#endif
+
+#define IP_TOS		1
+#define IP_TTL		2
+#define IP_HDRINCL	3
+#define IP_OPTIONS	4
+#define IP_ROUTER_ALERT	5
+#define IP_RECVOPTS	6
+#define IP_RETOPTS	7
+#define IP_PKTINFO	8
+#define IP_PKTOPTIONS	9
+#define IP_MTU_DISCOVER	10
+#define IP_RECVERR	11
+#define IP_RECVTTL	12
+#define	IP_RECVTOS	13
+#define IP_MTU		14
+#define IP_FREEBIND	15
+#define IP_IPSEC_POLICY	16
+#define IP_XFRM_POLICY	17
+#define IP_PASSSEC	18
+#define IP_TRANSPARENT	19
+
+/* BSD compatibility */
+#define IP_RECVRETOPTS	IP_RETOPTS
+
+/* TProxy original addresses */
+#define IP_ORIGDSTADDR       20
+#define IP_RECVORIGDSTADDR   IP_ORIGDSTADDR
+
+#define IP_MINTTL       21
+#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 */
+#define IP_PMTUDISC_WANT		1	/* Use per route hints	*/
+#define IP_PMTUDISC_DO			2	/* Always DF		*/
+#define IP_PMTUDISC_PROBE		3       /* Ignore dst pmtu      */
+/* Always use interface mtu (ignores dst pmtu) but don't set DF flag.
+ * Also incoming ICMP frag_needed notifications will be ignored on
+ * this socket to prevent accepting spoofed ones.
+ */
+#define IP_PMTUDISC_INTERFACE		4
+/* weaker version of IP_PMTUDISC_INTERFACE, which allos packets to get
+ * fragmented if they exeed the interface mtu
+ */
+#define IP_PMTUDISC_OMIT		5
+
+#define IP_MULTICAST_IF			32
+#define IP_MULTICAST_TTL 		33
+#define IP_MULTICAST_LOOP 		34
+#define IP_ADD_MEMBERSHIP		35
+#define IP_DROP_MEMBERSHIP		36
+#define IP_UNBLOCK_SOURCE		37
+#define IP_BLOCK_SOURCE			38
+#define IP_ADD_SOURCE_MEMBERSHIP	39
+#define IP_DROP_SOURCE_MEMBERSHIP	40
+#define IP_MSFILTER			41
+#define MCAST_JOIN_GROUP		42
+#define MCAST_BLOCK_SOURCE		43
+#define MCAST_UNBLOCK_SOURCE		44
+#define MCAST_LEAVE_GROUP		45
+#define MCAST_JOIN_SOURCE_GROUP		46
+#define MCAST_LEAVE_SOURCE_GROUP	47
+#define MCAST_MSFILTER			48
+#define IP_MULTICAST_ALL		49
+#define IP_UNICAST_IF			50
+
+#define MCAST_EXCLUDE	0
+#define MCAST_INCLUDE	1
+
+/* These need to appear somewhere around here */
+#define IP_DEFAULT_MULTICAST_TTL        1
+#define IP_DEFAULT_MULTICAST_LOOP       1
+
+/* Request struct for multicast socket ops */
+
+#if __UAPI_DEF_IP_MREQ
+struct ip_mreq  {
+	struct in_addr imr_multiaddr;	/* IP multicast address of group */
+	struct in_addr imr_interface;	/* local IP address of interface */
+};
+
+struct ip_mreqn {
+	struct in_addr	imr_multiaddr;		/* IP multicast address of group */
+	struct in_addr	imr_address;		/* local IP address of interface */
+	int		imr_ifindex;		/* Interface index */
+};
+
+struct ip_mreq_source {
+	__be32		imr_multiaddr;
+	__be32		imr_interface;
+	__be32		imr_sourceaddr;
+};
+
+struct ip_msfilter {
+	__be32		imsf_multiaddr;
+	__be32		imsf_interface;
+	__u32		imsf_fmode;
+	__u32		imsf_numsrc;
+	__be32		imsf_slist[1];
+};
+
+#define IP_MSFILTER_SIZE(numsrc) \
+	(sizeof(struct ip_msfilter) - sizeof(__u32) \
+	+ (numsrc) * sizeof(__u32))
+
+struct group_req {
+	__u32				 gr_interface;	/* interface index */
+	struct __kernel_sockaddr_storage gr_group;	/* group address */
+};
+
+struct group_source_req {
+	__u32				 gsr_interface;	/* interface index */
+	struct __kernel_sockaddr_storage gsr_group;	/* group address */
+	struct __kernel_sockaddr_storage gsr_source;	/* source address */
+};
+
+struct group_filter {
+	__u32				 gf_interface;	/* interface index */
+	struct __kernel_sockaddr_storage gf_group;	/* multicast address */
+	__u32				 gf_fmode;	/* filter mode */
+	__u32				 gf_numsrc;	/* number of sources */
+	struct __kernel_sockaddr_storage gf_slist[1];	/* interface index */
+};
+
+#define GROUP_FILTER_SIZE(numsrc) \
+	(sizeof(struct group_filter) - sizeof(struct __kernel_sockaddr_storage) \
+	+ (numsrc) * sizeof(struct __kernel_sockaddr_storage))
+#endif
+
+#if __UAPI_DEF_IN_PKTINFO
+struct in_pktinfo {
+	int		ipi_ifindex;
+	struct in_addr	ipi_spec_dst;
+	struct in_addr	ipi_addr;
+};
+#endif
+
+/* Structure describing an Internet (IP) socket address. */
+#if  __UAPI_DEF_SOCKADDR_IN
+#define __SOCK_SIZE__	16		/* sizeof(struct sockaddr)	*/
+struct sockaddr_in {
+  __kernel_sa_family_t	sin_family;	/* Address family		*/
+  __be16		sin_port;	/* Port number			*/
+  struct in_addr	sin_addr;	/* Internet address		*/
+
+  /* Pad to size of `struct sockaddr'. */
+  unsigned char		__pad[__SOCK_SIZE__ - sizeof(short int) -
+			sizeof(unsigned short int) - sizeof(struct in_addr)];
+};
+#define sin_zero	__pad		/* for BSD UNIX comp. -FvK	*/
+#endif
+
+#if __UAPI_DEF_IN_CLASS
+/*
+ * Definitions of the bits in an Internet address integer.
+ * On subnets, host and network parts are found according
+ * to the subnet mask, not these masks.
+ */
+#define	IN_CLASSA(a)		((((long int) (a)) & 0x80000000) == 0)
+#define	IN_CLASSA_NET		0xff000000
+#define	IN_CLASSA_NSHIFT	24
+#define	IN_CLASSA_HOST		(0xffffffff & ~IN_CLASSA_NET)
+#define	IN_CLASSA_MAX		128
+
+#define	IN_CLASSB(a)		((((long int) (a)) & 0xc0000000) == 0x80000000)
+#define	IN_CLASSB_NET		0xffff0000
+#define	IN_CLASSB_NSHIFT	16
+#define	IN_CLASSB_HOST		(0xffffffff & ~IN_CLASSB_NET)
+#define	IN_CLASSB_MAX		65536
+
+#define	IN_CLASSC(a)		((((long int) (a)) & 0xe0000000) == 0xc0000000)
+#define	IN_CLASSC_NET		0xffffff00
+#define	IN_CLASSC_NSHIFT	8
+#define	IN_CLASSC_HOST		(0xffffffff & ~IN_CLASSC_NET)
+
+#define	IN_CLASSD(a)		((((long int) (a)) & 0xf0000000) == 0xe0000000)
+#define	IN_MULTICAST(a)		IN_CLASSD(a)
+#define IN_MULTICAST_NET	0xF0000000
+
+#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)
+
+/* Address to send to all hosts. */
+#define	INADDR_BROADCAST	((unsigned long int) 0xffffffff)
+
+/* Address indicating an error return. */
+#define	INADDR_NONE		((unsigned long int) 0xffffffff)
+
+/* Network number for local host loopback. */
+#define	IN_LOOPBACKNET		127
+
+/* Address to loopback in software to local host.  */
+#define	INADDR_LOOPBACK		0x7f000001	/* 127.0.0.1   */
+#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_MAX_LOCAL_GROUP  0xe00000ffU	/* 224.0.0.255 */
+#endif
+
+/* <asm/byteorder.h> contains the htonl type stuff.. */
+#include <asm/byteorder.h> 
+
+
+#endif /* _LINUX_IN_H */
diff --git a/include/linux-private/linux/in6.h b/include/linux-private/linux/in6.h
new file mode 100644
index 0000000..409bb3f
--- /dev/null
+++ b/include/linux-private/linux/in6.h
@@ -0,0 +1,298 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ *	Types and definitions for AF_INET6 
+ *	Linux INET6 implementation 
+ *
+ *	Authors:
+ *	Pedro Roque		<roque@di.fc.ul.pt>	
+ *
+ *	Sources:
+ *	IPv6 Program Interfaces for BSD Systems
+ *      <draft-ietf-ipngwg-bsd-api-05.txt>
+ *
+ *	Advanced Sockets API for IPv6
+ *	<draft-stevens-advanced-api-00.txt>
+ *
+ *	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_IN6_H
+#define _LINUX_IN6_H
+
+#include <linux/types.h>
+#include <linux/libc-compat.h>
+
+/*
+ *	IPv6 address structure
+ */
+
+#if __UAPI_DEF_IN6_ADDR
+struct in6_addr {
+	union {
+		__u8		u6_addr8[16];
+#if __UAPI_DEF_IN6_ADDR_ALT
+		__be16		u6_addr16[8];
+		__be32		u6_addr32[4];
+#endif
+	} in6_u;
+#define s6_addr			in6_u.u6_addr8
+#if __UAPI_DEF_IN6_ADDR_ALT
+#define s6_addr16		in6_u.u6_addr16
+#define s6_addr32		in6_u.u6_addr32
+#endif
+};
+#endif /* __UAPI_DEF_IN6_ADDR */
+
+#if __UAPI_DEF_SOCKADDR_IN6
+struct sockaddr_in6 {
+	unsigned short int	sin6_family;    /* AF_INET6 */
+	__be16			sin6_port;      /* Transport layer port # */
+	__be32			sin6_flowinfo;  /* IPv6 flow information */
+	struct in6_addr		sin6_addr;      /* IPv6 address */
+	__u32			sin6_scope_id;  /* scope id (new in RFC2553) */
+};
+#endif /* __UAPI_DEF_SOCKADDR_IN6 */
+
+#if __UAPI_DEF_IPV6_MREQ
+struct ipv6_mreq {
+	/* IPv6 multicast address of group */
+	struct in6_addr ipv6mr_multiaddr;
+
+	/* local IPv6 address of interface */
+	int		ipv6mr_ifindex;
+};
+#endif /* __UAPI_DEF_IVP6_MREQ */
+
+#define ipv6mr_acaddr	ipv6mr_multiaddr
+
+struct in6_flowlabel_req {
+	struct in6_addr	flr_dst;
+	__be32	flr_label;
+	__u8	flr_action;
+	__u8	flr_share;
+	__u16	flr_flags;
+	__u16 	flr_expires;
+	__u16	flr_linger;
+	__u32	__flr_pad;
+	/* Options in format of IPV6_PKTOPTIONS */
+};
+
+#define IPV6_FL_A_GET	0
+#define IPV6_FL_A_PUT	1
+#define IPV6_FL_A_RENEW	2
+
+#define IPV6_FL_F_CREATE	1
+#define IPV6_FL_F_EXCL		2
+#define IPV6_FL_F_REFLECT	4
+#define IPV6_FL_F_REMOTE	8
+
+#define IPV6_FL_S_NONE		0
+#define IPV6_FL_S_EXCL		1
+#define IPV6_FL_S_PROCESS	2
+#define IPV6_FL_S_USER		3
+#define IPV6_FL_S_ANY		255
+
+
+/*
+ *	Bitmask constant declarations to help applications select out the 
+ *	flow label and priority fields.
+ *
+ *	Note that this are in host byte order while the flowinfo field of
+ *	sockaddr_in6 is in network byte order.
+ */
+
+#define IPV6_FLOWINFO_FLOWLABEL		0x000fffff
+#define IPV6_FLOWINFO_PRIORITY		0x0ff00000
+
+/* These definitions are obsolete */
+#define IPV6_PRIORITY_UNCHARACTERIZED	0x0000
+#define IPV6_PRIORITY_FILLER		0x0100
+#define IPV6_PRIORITY_UNATTENDED	0x0200
+#define IPV6_PRIORITY_RESERVED1		0x0300
+#define IPV6_PRIORITY_BULK		0x0400
+#define IPV6_PRIORITY_RESERVED2		0x0500
+#define IPV6_PRIORITY_INTERACTIVE	0x0600
+#define IPV6_PRIORITY_CONTROL		0x0700
+#define IPV6_PRIORITY_8			0x0800
+#define IPV6_PRIORITY_9			0x0900
+#define IPV6_PRIORITY_10		0x0a00
+#define IPV6_PRIORITY_11		0x0b00
+#define IPV6_PRIORITY_12		0x0c00
+#define IPV6_PRIORITY_13		0x0d00
+#define IPV6_PRIORITY_14		0x0e00
+#define IPV6_PRIORITY_15		0x0f00
+
+/*
+ *	IPV6 extension headers
+ */
+#if __UAPI_DEF_IPPROTO_V6
+#define IPPROTO_HOPOPTS		0	/* IPv6 hop-by-hop options	*/
+#define IPPROTO_ROUTING		43	/* IPv6 routing header		*/
+#define IPPROTO_FRAGMENT	44	/* IPv6 fragmentation header	*/
+#define IPPROTO_ICMPV6		58	/* ICMPv6			*/
+#define IPPROTO_NONE		59	/* IPv6 no next header		*/
+#define IPPROTO_DSTOPTS		60	/* IPv6 destination options	*/
+#define IPPROTO_MH		135	/* IPv6 mobility header		*/
+#endif /* __UAPI_DEF_IPPROTO_V6 */
+
+/*
+ *	IPv6 TLV options.
+ */
+#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 */
+
+/*
+ *	IPV6 socket options
+ */
+#if __UAPI_DEF_IPV6_OPTIONS
+#define IPV6_ADDRFORM		1
+#define IPV6_2292PKTINFO	2
+#define IPV6_2292HOPOPTS	3
+#define IPV6_2292DSTOPTS	4
+#define IPV6_2292RTHDR		5
+#define IPV6_2292PKTOPTIONS	6
+#define IPV6_CHECKSUM		7
+#define IPV6_2292HOPLIMIT	8
+#define IPV6_NEXTHOP		9
+#define IPV6_AUTHHDR		10	/* obsolete */
+#define IPV6_FLOWINFO		11
+
+#define IPV6_UNICAST_HOPS	16
+#define IPV6_MULTICAST_IF	17
+#define IPV6_MULTICAST_HOPS	18
+#define IPV6_MULTICAST_LOOP	19
+#define IPV6_ADD_MEMBERSHIP	20
+#define IPV6_DROP_MEMBERSHIP	21
+#define IPV6_ROUTER_ALERT	22
+#define IPV6_MTU_DISCOVER	23
+#define IPV6_MTU		24
+#define IPV6_RECVERR		25
+#define IPV6_V6ONLY		26
+#define IPV6_JOIN_ANYCAST	27
+#define IPV6_LEAVE_ANYCAST	28
+
+/* IPV6_MTU_DISCOVER values */
+#define IPV6_PMTUDISC_DONT		0
+#define IPV6_PMTUDISC_WANT		1
+#define IPV6_PMTUDISC_DO		2
+#define IPV6_PMTUDISC_PROBE		3
+/* same as IPV6_PMTUDISC_PROBE, provided for symetry with IPv4
+ * also see comments on IP_PMTUDISC_INTERFACE
+ */
+#define IPV6_PMTUDISC_INTERFACE		4
+/* weaker version of IPV6_PMTUDISC_INTERFACE, which allows packets to
+ * get fragmented if they exceed the interface mtu
+ */
+#define IPV6_PMTUDISC_OMIT		5
+
+/* Flowlabel */
+#define IPV6_FLOWLABEL_MGR	32
+#define IPV6_FLOWINFO_SEND	33
+
+#define IPV6_IPSEC_POLICY	34
+#define IPV6_XFRM_POLICY	35
+#define IPV6_HDRINCL		36
+#endif
+
+/*
+ * Multicast:
+ * Following socket options are shared between IPv4 and IPv6.
+ *
+ * MCAST_JOIN_GROUP		42
+ * MCAST_BLOCK_SOURCE		43
+ * MCAST_UNBLOCK_SOURCE		44
+ * MCAST_LEAVE_GROUP		45
+ * MCAST_JOIN_SOURCE_GROUP	46
+ * MCAST_LEAVE_SOURCE_GROUP	47
+ * MCAST_MSFILTER		48
+ */
+
+/*
+ * Advanced API (RFC3542) (1)
+ *
+ * Note: IPV6_RECVRTHDRDSTOPTS does not exist. see net/ipv6/datagram.c.
+ */
+
+#define IPV6_RECVPKTINFO	49
+#define IPV6_PKTINFO		50
+#define IPV6_RECVHOPLIMIT	51
+#define IPV6_HOPLIMIT		52
+#define IPV6_RECVHOPOPTS	53
+#define IPV6_HOPOPTS		54
+#define IPV6_RTHDRDSTOPTS	55
+#define IPV6_RECVRTHDR		56
+#define IPV6_RTHDR		57
+#define IPV6_RECVDSTOPTS	58
+#define IPV6_DSTOPTS		59
+#define IPV6_RECVPATHMTU	60
+#define IPV6_PATHMTU		61
+#define IPV6_DONTFRAG		62
+#if 0	/* not yet */
+#define IPV6_USE_MIN_MTU	63
+#endif
+
+/*
+ * Netfilter (1)
+ *
+ * Following socket options are used in ip6_tables;
+ * see include/linux/netfilter_ipv6/ip6_tables.h.
+ *
+ * IP6T_SO_SET_REPLACE / IP6T_SO_GET_INFO		64
+ * IP6T_SO_SET_ADD_COUNTERS / IP6T_SO_GET_ENTRIES	65
+ */
+
+/*
+ * Advanced API (RFC3542) (2)
+ */
+#define IPV6_RECVTCLASS		66
+#define IPV6_TCLASS		67
+
+/*
+ * Netfilter (2)
+ *
+ * Following socket options are used in ip6_tables;
+ * see include/linux/netfilter_ipv6/ip6_tables.h.
+ *
+ * IP6T_SO_GET_REVISION_MATCH	68
+ * IP6T_SO_GET_REVISION_TARGET	69
+ * IP6T_SO_ORIGINAL_DST		80
+ */
+
+#define IPV6_AUTOFLOWLABEL	70
+/* RFC5014: Source address selection */
+#define IPV6_ADDR_PREFERENCES	72
+
+#define IPV6_PREFER_SRC_TMP		0x0001
+#define IPV6_PREFER_SRC_PUBLIC		0x0002
+#define IPV6_PREFER_SRC_PUBTMP_DEFAULT	0x0100
+#define IPV6_PREFER_SRC_COA		0x0004
+#define IPV6_PREFER_SRC_HOME		0x0400
+#define IPV6_PREFER_SRC_CGA		0x0008
+#define IPV6_PREFER_SRC_NONCGA		0x0800
+
+/* RFC5082: Generalized Ttl Security Mechanism */
+#define IPV6_MINHOPCOUNT		73
+
+#define IPV6_ORIGDSTADDR        74
+#define IPV6_RECVORIGDSTADDR    IPV6_ORIGDSTADDR
+#define IPV6_TRANSPARENT        75
+#define IPV6_UNICAST_IF         76
+#define IPV6_RECVFRAGSIZE	77
+#define IPV6_FREEBIND		78
+
+/*
+ * Multicast Routing:
+ * see include/uapi/linux/mroute6.h.
+ *
+ * MRT6_BASE			200
+ * ...
+ * MRT6_MAX
+ */
+#endif /* _LINUX_IN6_H */
diff --git a/include/linux-private/linux/inet_diag.h b/include/linux-private/linux/inet_diag.h
new file mode 100644
index 0000000..f3bcd7e
--- /dev/null
+++ b/include/linux-private/linux/inet_diag.h
@@ -0,0 +1,205 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _INET_DIAG_H_
+#define _INET_DIAG_H_
+
+#include <linux/types.h>
+
+/* Just some random number */
+#define TCPDIAG_GETSOCK 18
+#define DCCPDIAG_GETSOCK 19
+
+#define INET_DIAG_GETSOCK_MAX 24
+
+/* Socket identity */
+struct inet_diag_sockid {
+	__be16	idiag_sport;
+	__be16	idiag_dport;
+	__be32	idiag_src[4];
+	__be32	idiag_dst[4];
+	__u32	idiag_if;
+	__u32	idiag_cookie[2];
+#define INET_DIAG_NOCOOKIE (~0U)
+};
+
+/* Request structure */
+
+struct inet_diag_req {
+	__u8	idiag_family;		/* Family of addresses. */
+	__u8	idiag_src_len;
+	__u8	idiag_dst_len;
+	__u8	idiag_ext;		/* Query extended information */
+
+	struct inet_diag_sockid id;
+
+	__u32	idiag_states;		/* States to dump */
+	__u32	idiag_dbs;		/* Tables to dump (NI) */
+};
+
+struct inet_diag_req_v2 {
+	__u8	sdiag_family;
+	__u8	sdiag_protocol;
+	__u8	idiag_ext;
+	__u8	pad;
+	__u32	idiag_states;
+	struct inet_diag_sockid id;
+};
+
+/*
+ * 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,
+};
+
+#define INET_DIAG_REQ_MAX INET_DIAG_REQ_BYTECODE
+
+/* Bytecode is sequence of 4 byte commands followed by variable arguments.
+ * All the commands identified by "code" are conditional jumps forward:
+ * to offset cc+"yes" or to offset cc+"no". "yes" is supposed to be
+ * length of the command and its arguments.
+ */
+ 
+struct inet_diag_bc_op {
+	unsigned char	code;
+	unsigned char	yes;
+	unsigned short	no;
+};
+
+enum {
+	INET_DIAG_BC_NOP,
+	INET_DIAG_BC_JMP,
+	INET_DIAG_BC_S_GE,
+	INET_DIAG_BC_S_LE,
+	INET_DIAG_BC_D_GE,
+	INET_DIAG_BC_D_LE,
+	INET_DIAG_BC_AUTO,
+	INET_DIAG_BC_S_COND,
+	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 {
+	__u8	family;
+	__u8	prefix_len;
+	int	port;
+	__be32	addr[0];
+};
+
+struct inet_diag_markcond {
+	__u32 mark;
+	__u32 mask;
+};
+
+/* Base info structure. It contains socket identity (addrs/ports/cookie)
+ * and, alas, the information shown by netstat. */
+struct inet_diag_msg {
+	__u8	idiag_family;
+	__u8	idiag_state;
+	__u8	idiag_timer;
+	__u8	idiag_retrans;
+
+	struct inet_diag_sockid id;
+
+	__u32	idiag_expires;
+	__u32	idiag_rqueue;
+	__u32	idiag_wqueue;
+	__u32	idiag_uid;
+	__u32	idiag_inode;
+};
+
+/* Extensions */
+
+enum {
+	INET_DIAG_NONE,
+	INET_DIAG_MEMINFO,
+	INET_DIAG_INFO,
+	INET_DIAG_VEGASINFO,
+	INET_DIAG_CONG,
+	INET_DIAG_TOS,
+	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_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_MAX,
+};
+
+#define INET_DIAG_MAX (__INET_DIAG_MAX - 1)
+
+/* INET_DIAG_MEM */
+
+struct inet_diag_meminfo {
+	__u32	idiag_rmem;
+	__u32	idiag_wmem;
+	__u32	idiag_fmem;
+	__u32	idiag_tmem;
+};
+
+/* INET_DIAG_VEGASINFO */
+
+struct tcpvegas_info {
+	__u32	tcpv_enabled;
+	__u32	tcpv_rttcnt;
+	__u32	tcpv_rtt;
+	__u32	tcpv_minrtt;
+};
+
+/* INET_DIAG_DCTCPINFO */
+
+struct tcp_dctcp_info {
+	__u16	dctcp_enabled;
+	__u16	dctcp_ce_state;
+	__u32	dctcp_alpha;
+	__u32	dctcp_ab_ecn;
+	__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/linux/ip.h b/include/linux-private/linux/ip.h
similarity index 93%
rename from include/linux/ip.h
rename to include/linux-private/linux/ip.h
index 4119594..f4ecd2f 100644
--- a/include/linux/ip.h
+++ b/include/linux-private/linux/ip.h
@@ -1,3 +1,4 @@
+/* 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
@@ -14,8 +15,8 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  */
-#ifndef _UAPI_LINUX_IP_H
-#define _UAPI_LINUX_IP_H
+#ifndef _LINUX_IP_H
+#define _LINUX_IP_H
 #include <linux/types.h>
 #include <asm/byteorder.h>
 
@@ -164,9 +165,13 @@
 	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 /* _UAPI_LINUX_IP_H */
+#endif /* _LINUX_IP_H */
diff --git a/include/linux/ipv6.h b/include/linux-private/linux/ipv6.h
similarity index 67%
rename from include/linux/ipv6.h
rename to include/linux-private/linux/ipv6.h
index f16349d..769b4a3 100644
--- a/include/linux/ipv6.h
+++ b/include/linux-private/linux/ipv6.h
@@ -1,6 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _IPV6_H
 #define _IPV6_H
 
+#include <linux/libc-compat.h>
+#include <linux/types.h>
+#include <linux/in6.h>
 #include <asm/byteorder.h>
 
 /* The latest drafts declared increase in minimal mtu up to 1280. */
@@ -13,10 +17,30 @@
  *	*under construction*
  */
 
+#if __UAPI_DEF_IN6_PKTINFO
+struct in6_pktinfo {
+	struct in6_addr	ipi6_addr;
+	int		ipi6_ifindex;
+};
+#endif
+
+#if __UAPI_DEF_IP6_MTUINFO
+struct ip6_mtuinfo {
+	struct sockaddr_in6	ip6m_addr;
+	__u32			ip6m_mtu;
+};
+#endif
+
+struct in6_ifreq {
+	struct in6_addr	ifr6_addr;
+	__u32		ifr6_prefixlen;
+	int		ifr6_ifindex; 
+};
 
 #define IPV6_SRCRT_STRICT	0x01	/* Deprecated; will be removed */
 #define IPV6_SRCRT_TYPE_0	0	/* Deprecated; will be removed */
 #define IPV6_SRCRT_TYPE_2	2	/* IPv6 type 2 Routing Header	*/
+#define IPV6_SRCRT_TYPE_4	4	/* Segment Routing with IPv6 */
 
 /*
  *	routing header
@@ -45,6 +69,8 @@
 #define ipv6_destopt_hdr ipv6_opt_hdr
 #define ipv6_hopopt_hdr  ipv6_opt_hdr
 
+/* Router Alert option values (RFC2711) */
+#define IPV6_OPT_ROUTERALERT_MLD	0x0000	/* MLD(RFC2710) */
 
 /*
  *	routing header type 0 (used in cmsghdr struct)
@@ -139,6 +165,28 @@
 	DEVCONF_DISABLE_IPV6,
 	DEVCONF_ACCEPT_DAD,
 	DEVCONF_FORCE_TLLAO,
+	DEVCONF_NDISC_NOTIFY,
+	DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL,
+	DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL,
+	DEVCONF_SUPPRESS_FRAG_NDISC,
+	DEVCONF_ACCEPT_RA_FROM_LOCAL,
+	DEVCONF_USE_OPTIMISTIC,
+	DEVCONF_ACCEPT_RA_MTU,
+	DEVCONF_STABLE_SECRET,
+	DEVCONF_USE_OIF_ADDRS_ONLY,
+	DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT,
+	DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN,
+	DEVCONF_DROP_UNICAST_IN_L2_MULTICAST,
+	DEVCONF_DROP_UNSOLICITED_NA,
+	DEVCONF_KEEP_ADDR_ON_DOWN,
+	DEVCONF_RTR_SOLICIT_MAX_INTERVAL,
+	DEVCONF_SEG6_ENABLED,
+	DEVCONF_SEG6_REQUIRE_HMAC,
+	DEVCONF_ENHANCED_DAD,
+	DEVCONF_ADDR_GEN_MODE,
+	DEVCONF_DISABLE_POLICY,
+	DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN,
+	DEVCONF_NDISC_TCLASS,
 	DEVCONF_MAX
 };
 
diff --git a/include/linux-private/linux/libc-compat.h b/include/linux-private/linux/libc-compat.h
new file mode 100644
index 0000000..a159991
--- /dev/null
+++ b/include/linux-private/linux/libc-compat.h
@@ -0,0 +1,267 @@
+/* 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/linux-private/linux/lwtunnel.h b/include/linux-private/linux/lwtunnel.h
new file mode 100644
index 0000000..3f3fe6f
--- /dev/null
+++ b/include/linux-private/linux/lwtunnel.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LWTUNNEL_H_
+#define _LWTUNNEL_H_
+
+#include <linux/types.h>
+
+enum lwtunnel_encap_types {
+	LWTUNNEL_ENCAP_NONE,
+	LWTUNNEL_ENCAP_MPLS,
+	LWTUNNEL_ENCAP_IP,
+	LWTUNNEL_ENCAP_ILA,
+	LWTUNNEL_ENCAP_IP6,
+	LWTUNNEL_ENCAP_SEG6,
+	LWTUNNEL_ENCAP_BPF,
+	LWTUNNEL_ENCAP_SEG6_LOCAL,
+	__LWTUNNEL_ENCAP_MAX,
+};
+
+#define LWTUNNEL_ENCAP_MAX (__LWTUNNEL_ENCAP_MAX - 1)
+
+enum lwtunnel_ip_t {
+	LWTUNNEL_IP_UNSPEC,
+	LWTUNNEL_IP_ID,
+	LWTUNNEL_IP_DST,
+	LWTUNNEL_IP_SRC,
+	LWTUNNEL_IP_TTL,
+	LWTUNNEL_IP_TOS,
+	LWTUNNEL_IP_FLAGS,
+	LWTUNNEL_IP_PAD,
+	__LWTUNNEL_IP_MAX,
+};
+
+#define LWTUNNEL_IP_MAX (__LWTUNNEL_IP_MAX - 1)
+
+enum lwtunnel_ip6_t {
+	LWTUNNEL_IP6_UNSPEC,
+	LWTUNNEL_IP6_ID,
+	LWTUNNEL_IP6_DST,
+	LWTUNNEL_IP6_SRC,
+	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/linux-private/linux/mpls.h b/include/linux-private/linux/mpls.h
new file mode 100644
index 0000000..9effbf9
--- /dev/null
+++ b/include/linux-private/linux/mpls.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _MPLS_H
+#define _MPLS_H
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+/* Reference: RFC 5462, RFC 3032
+ *
+ *  0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                Label                  | TC  |S|       TTL     |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *	Label:  Label Value, 20 bits
+ *	TC:     Traffic Class field, 3 bits
+ *	S:      Bottom of Stack, 1 bit
+ *	TTL:    Time to Live, 8 bits
+ */
+
+struct mpls_label {
+	__be32 entry;
+};
+
+#define MPLS_LS_LABEL_MASK      0xFFFFF000
+#define MPLS_LS_LABEL_SHIFT     12
+#define MPLS_LS_TC_MASK         0x00000E00
+#define MPLS_LS_TC_SHIFT        9
+#define MPLS_LS_S_MASK          0x00000100
+#define MPLS_LS_S_SHIFT         8
+#define MPLS_LS_TTL_MASK        0x000000FF
+#define MPLS_LS_TTL_SHIFT       0
+
+/* Reserved labels */
+#define MPLS_LABEL_IPV4NULL		0 /* RFC3032 */
+#define MPLS_LABEL_RTALERT		1 /* RFC3032 */
+#define MPLS_LABEL_IPV6NULL		2 /* RFC3032 */
+#define MPLS_LABEL_IMPLNULL		3 /* RFC3032 */
+#define MPLS_LABEL_ENTROPY		7 /* RFC6790 */
+#define MPLS_LABEL_GAL			13 /* RFC5586 */
+#define MPLS_LABEL_OAMALERT		14 /* RFC3429 */
+#define MPLS_LABEL_EXTENSION		15 /* RFC7274 */
+
+#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/linux-private/linux/mpls_iptunnel.h b/include/linux-private/linux/mpls_iptunnel.h
new file mode 100644
index 0000000..2c69b7d
--- /dev/null
+++ b/include/linux-private/linux/mpls_iptunnel.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ *	mpls tunnel api
+ *
+ *	Authors:
+ *		Roopa Prabhu <roopa@cumulusnetworks.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_MPLS_IPTUNNEL_H
+#define _LINUX_MPLS_IPTUNNEL_H
+
+/* 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)
+
+#endif /* _LINUX_MPLS_IPTUNNEL_H */
diff --git a/include/linux/neighbour.h b/include/linux-private/linux/neighbour.h
similarity index 89%
rename from include/linux/neighbour.h
rename to include/linux-private/linux/neighbour.h
index a7003b7..904db61 100644
--- a/include/linux/neighbour.h
+++ b/include/linux-private/linux/neighbour.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_NEIGHBOUR_H
 #define __LINUX_NEIGHBOUR_H
 
@@ -20,6 +21,13 @@
 	NDA_LLADDR,
 	NDA_CACHEINFO,
 	NDA_PROBES,
+	NDA_VLAN,
+	NDA_PORT,
+	NDA_VNI,
+	NDA_IFINDEX,
+	NDA_MASTER,
+	NDA_LINK_NETNSID,
+	NDA_SRC_VNI,
 	__NDA_MAX
 };
 
@@ -30,7 +38,11 @@
  */
 
 #define NTF_USE		0x01
+#define NTF_SELF	0x02
+#define NTF_MASTER	0x04
 #define NTF_PROXY	0x08	/* == ATF_PUBL */
+#define NTF_EXT_LEARNED	0x10
+#define NTF_OFFLOADED   0x20
 #define NTF_ROUTER	0x80
 
 /*
@@ -51,7 +63,7 @@
 
 /* NUD_NOARP & NUD_PERMANENT are pseudostates, they never change
    and make no address resolution or NUD.
-   NUD_PERMANENT is also cannot be deleted by garbage collectors.
+   NUD_PERMANENT also cannot be deleted by garbage collectors.
  */
 
 struct nda_cacheinfo {
@@ -97,6 +109,7 @@
 	__u64		ndts_rcv_probes_ucast;
 	__u64		ndts_periodic_gc_runs;
 	__u64		ndts_forced_gc_runs;
+	__u64		ndts_table_fulls;
 };
 
 enum {
@@ -116,6 +129,9 @@
 	NDTPA_PROXY_DELAY,		/* u64, msecs */
 	NDTPA_PROXY_QLEN,		/* u32 */
 	NDTPA_LOCKTIME,			/* u64, msecs */
+	NDTPA_QUEUE_LENBYTES,		/* u32 */
+	NDTPA_MCAST_REPROBES,		/* u32 */
+	NDTPA_PAD,
 	__NDTPA_MAX
 };
 #define NDTPA_MAX (__NDTPA_MAX - 1)
@@ -148,6 +164,7 @@
 	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/linux-private/linux/netconf.h b/include/linux-private/linux/netconf.h
new file mode 100644
index 0000000..229e885
--- /dev/null
+++ b/include/linux-private/linux/netconf.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LINUX_NETCONF_H_
+#define _LINUX_NETCONF_H_
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+struct netconfmsg {
+	__u8	ncm_family;
+};
+
+enum {
+	NETCONFA_UNSPEC,
+	NETCONFA_IFINDEX,
+	NETCONFA_FORWARDING,
+	NETCONFA_RP_FILTER,
+	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
+
+#endif /* _LINUX_NETCONF_H_ */
diff --git a/include/linux-private/linux/netfilter.h b/include/linux-private/linux/netfilter.h
new file mode 100644
index 0000000..36378a0
--- /dev/null
+++ b/include/linux-private/linux/netfilter.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_NETFILTER_H
+#define __LINUX_NETFILTER_H
+
+#include <linux/types.h>
+
+#include <linux/in.h>
+#include <linux/in6.h>
+
+/* Responses from hook functions. */
+#define NF_DROP 0
+#define NF_ACCEPT 1
+#define NF_STOLEN 2
+#define NF_QUEUE 3
+#define NF_REPEAT 4
+#define NF_STOP 5	/* Deprecated, for userspace nf_queue compatibility. */
+#define NF_MAX_VERDICT NF_STOP
+
+/* we overload the higher bits for encoding auxiliary data such as the queue
+ * number or errno values. Not nice, but better than additional function
+ * arguments. */
+#define NF_VERDICT_MASK 0x000000ff
+
+/* extra verdict flags have mask 0x0000ff00 */
+#define NF_VERDICT_FLAG_QUEUE_BYPASS	0x00008000
+
+/* queue number (NF_QUEUE) or errno (NF_DROP) */
+#define NF_VERDICT_QMASK 0xffff0000
+#define NF_VERDICT_QBITS 16
+
+#define NF_QUEUE_NR(x) ((((x) << 16) & NF_VERDICT_QMASK) | NF_QUEUE)
+
+#define NF_DROP_ERR(x) (((-x) << 16) | NF_DROP)
+
+/* only for userspace compatibility */
+/* Generic cache responses from hook functions.
+   <= 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
+
+enum nf_inet_hooks {
+	NF_INET_PRE_ROUTING,
+	NF_INET_LOCAL_IN,
+	NF_INET_FORWARD,
+	NF_INET_LOCAL_OUT,
+	NF_INET_POST_ROUTING,
+	NF_INET_NUMHOOKS
+};
+
+enum nf_dev_hooks {
+	NF_NETDEV_INGRESS,
+	NF_NETDEV_NUMHOOKS
+};
+
+enum {
+	NFPROTO_UNSPEC =  0,
+	NFPROTO_INET   =  1,
+	NFPROTO_IPV4   =  2,
+	NFPROTO_ARP    =  3,
+	NFPROTO_NETDEV =  5,
+	NFPROTO_BRIDGE =  7,
+	NFPROTO_IPV6   = 10,
+	NFPROTO_DECNET = 12,
+	NFPROTO_NUMPROTO,
+};
+
+union nf_inet_addr {
+	__u32		all[4];
+	__be32		ip;
+	__be32		ip6[4];
+	struct in_addr	in;
+	struct in6_addr	in6;
+};
+
+#endif /* __LINUX_NETFILTER_H */
diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux-private/linux/netfilter/nf_conntrack_common.h
similarity index 73%
rename from include/linux/netfilter/nf_conntrack_common.h
rename to include/linux-private/linux/netfilter/nf_conntrack_common.h
index 1644cdd..dc374c9 100644
--- a/include/linux/netfilter/nf_conntrack_common.h
+++ b/include/linux-private/linux/netfilter/nf_conntrack_common.h
@@ -1,5 +1,6 @@
-#ifndef _UAPI_NF_CONNTRACK_COMMON_H
-#define _UAPI_NF_CONNTRACK_COMMON_H
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _NF_CONNTRACK_COMMON_H
+#define _NF_CONNTRACK_COMMON_H
 /* Connection state tracking for netfilter.  This is separated from,
    but required by, the NAT layer; it can also be used by an iptables
    extension. */
@@ -20,11 +21,19 @@
 
 	IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY,
 	IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY,
-	IP_CT_NEW_REPLY = IP_CT_NEW + IP_CT_IS_REPLY,	
-	/* Number of distinct IP_CT types (no NEW in reply dirn). */
-	IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1
+	/* No NEW in reply direction. */
+
+	/* Number of distinct IP_CT types. */
+	IP_CT_NUMBER,
+
+	/* only for userspace compatibility */
+	IP_CT_NEW_REPLY = IP_CT_NUMBER,
 };
 
+#define NF_CT_STATE_INVALID_BIT			(1 << 0)
+#define NF_CT_STATE_BIT(ctinfo)			(1 << ((ctinfo) % IP_CT_IS_REPLY + 1))
+#define NF_CT_STATE_UNTRACKED_BIT		(1 << 6)
+
 /* Bitset representing status of connection. */
 enum ip_conntrack_status {
 	/* It's an expected connection: bit 0 set.  This bit never changed */
@@ -80,13 +89,26 @@
 	IPS_TEMPLATE_BIT = 11,
 	IPS_TEMPLATE = (1 << IPS_TEMPLATE_BIT),
 
-	/* Conntrack is a fake untracked entry */
+	/* Conntrack is a fake untracked entry.  Obsolete and not used anymore */
 	IPS_UNTRACKED_BIT = 12,
 	IPS_UNTRACKED = (1 << IPS_UNTRACKED_BIT),
 
 	/* Conntrack got a helper explicitly attached via CT target. */
 	IPS_HELPER_BIT = 13,
 	IPS_HELPER = (1 << IPS_HELPER_BIT),
+
+	/* Conntrack has been offloaded to flow table. */
+	IPS_OFFLOAD_BIT = 14,
+	IPS_OFFLOAD = (1 << IPS_OFFLOAD_BIT),
+
+	/* Be careful here, modifying these bits can make things messy,
+	 * so don't let users modify them directly.
+	 */
+	IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK |
+				 IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING |
+				 IPS_SEQ_ADJUST | IPS_TEMPLATE | IPS_OFFLOAD),
+
+	__IPS_MAX_BIT = 15,
 };
 
 /* Connection tracking event types */
@@ -99,8 +121,11 @@
 	IPCT_PROTOINFO,		/* protocol information has changed */
 	IPCT_HELPER,		/* new helper has been set */
 	IPCT_MARK,		/* new mark has been set */
-	IPCT_NATSEQADJ,		/* NAT is doing sequence adjustment */
+	IPCT_SEQADJ,		/* sequence adjustment has changed */
+	IPCT_NATSEQADJ = IPCT_SEQADJ,
 	IPCT_SECMARK,		/* new security mark has been set */
+	IPCT_LABEL,		/* new connlabel has been set */
+	IPCT_SYNPROXY,		/* synproxy has been set */
 };
 
 enum ip_conntrack_expect_events {
@@ -114,4 +139,4 @@
 #define NF_CT_EXPECT_USERSPACE		0x4
 
 
-#endif /* _UAPI_NF_CONNTRACK_COMMON_H */
+#endif /* _NF_CONNTRACK_COMMON_H */
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux-private/linux/netfilter/nfnetlink.h
similarity index 64%
rename from include/linux/netfilter/nfnetlink.h
rename to include/linux-private/linux/netfilter/nfnetlink.h
index 4a4efaf..a89f3a5 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux-private/linux/netfilter/nfnetlink.h
@@ -1,5 +1,6 @@
-#ifndef _UAPI_NFNETLINK_H
-#define _UAPI_NFNETLINK_H
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _NFNETLINK_H
+#define _NFNETLINK_H
 #include <linux/types.h>
 #include <linux/netfilter/nfnetlink_compat.h>
 
@@ -18,6 +19,12 @@
 #define NFNLGRP_CONNTRACK_EXP_UPDATE	NFNLGRP_CONNTRACK_EXP_UPDATE
 	NFNLGRP_CONNTRACK_EXP_DESTROY,
 #define NFNLGRP_CONNTRACK_EXP_DESTROY	NFNLGRP_CONNTRACK_EXP_DESTROY
+	NFNLGRP_NFTABLES,
+#define NFNLGRP_NFTABLES                NFNLGRP_NFTABLES
+	NFNLGRP_ACCT_QUOTA,
+#define NFNLGRP_ACCT_QUOTA		NFNLGRP_ACCT_QUOTA
+	NFNLGRP_NFTRACE,
+#define NFNLGRP_NFTRACE			NFNLGRP_NFTRACE
 	__NFNLGRP_MAX,
 };
 #define NFNLGRP_MAX	(__NFNLGRP_MAX - 1)
@@ -51,6 +58,24 @@
 #define NFNL_SUBSYS_ACCT		7
 #define NFNL_SUBSYS_CTNETLINK_TIMEOUT	8
 #define NFNL_SUBSYS_CTHELPER		9
-#define NFNL_SUBSYS_COUNT		10
+#define NFNL_SUBSYS_NFTABLES		10
+#define NFNL_SUBSYS_NFT_COMPAT		11
+#define NFNL_SUBSYS_COUNT		12
 
-#endif /* _UAPI_NFNETLINK_H */
+/* Reserved control nfnetlink messages */
+#define NFNL_MSG_BATCH_BEGIN		NLMSG_MIN_TYPE
+#define NFNL_MSG_BATCH_END		NLMSG_MIN_TYPE+1
+
+/**
+ * enum nfnl_batch_attributes - nfnetlink batch netlink attributes
+ *
+ * @NFNL_BATCH_GENID: generation ID for this changeset (NLA_U32)
+ */
+enum nfnl_batch_attributes {
+        NFNL_BATCH_UNSPEC,
+        NFNL_BATCH_GENID,
+        __NFNL_BATCH_MAX
+};
+#define NFNL_BATCH_MAX			(__NFNL_BATCH_MAX - 1)
+
+#endif /* _NFNETLINK_H */
diff --git a/include/linux/netfilter/nfnetlink_compat.h b/include/linux-private/linux/netfilter/nfnetlink_compat.h
similarity index 97%
rename from include/linux/netfilter/nfnetlink_compat.h
rename to include/linux-private/linux/netfilter/nfnetlink_compat.h
index ffb9503..ead7161 100644
--- a/include/linux/netfilter/nfnetlink_compat.h
+++ b/include/linux-private/linux/netfilter/nfnetlink_compat.h
@@ -1,9 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _NFNETLINK_COMPAT_H
 #define _NFNETLINK_COMPAT_H
 
 #include <linux/types.h>
 
-#ifndef __KERNEL__
 /* Old nfnetlink macros for userspace */
 
 /* nfnetlink groups: Up to 32 maximum */
@@ -59,5 +59,4 @@
         + NLMSG_ALIGN(sizeof(struct nfgenmsg))))
 #define NFM_PAYLOAD(n)  NLMSG_PAYLOAD(n, sizeof(struct nfgenmsg))
 
-#endif /* ! __KERNEL__ */
 #endif /* _NFNETLINK_COMPAT_H */
diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux-private/linux/netfilter/nfnetlink_conntrack.h
similarity index 84%
rename from include/linux/netfilter/nfnetlink_conntrack.h
rename to include/linux-private/linux/netfilter/nfnetlink_conntrack.h
index 43bfe3e..1d41810 100644
--- a/include/linux/netfilter/nfnetlink_conntrack.h
+++ b/include/linux-private/linux/netfilter/nfnetlink_conntrack.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _IPCONNTRACK_NETLINK_H
 #define _IPCONNTRACK_NETLINK_H
 #include <linux/netfilter/nfnetlink.h>
@@ -9,6 +10,8 @@
 	IPCTNL_MSG_CT_GET_CTRZERO,
 	IPCTNL_MSG_CT_GET_STATS_CPU,
 	IPCTNL_MSG_CT_GET_STATS,
+	IPCTNL_MSG_CT_GET_DYING,
+	IPCTNL_MSG_CT_GET_UNCONFIRMED,
 
 	IPCTNL_MSG_MAX
 };
@@ -40,13 +43,18 @@
 	CTA_ID,
 	CTA_NAT_DST,
 	CTA_TUPLE_MASTER,
-	CTA_NAT_SEQ_ADJ_ORIG,
-	CTA_NAT_SEQ_ADJ_REPLY,
+	CTA_SEQ_ADJ_ORIG,
+	CTA_NAT_SEQ_ADJ_ORIG	= CTA_SEQ_ADJ_ORIG,
+	CTA_SEQ_ADJ_REPLY,
+	CTA_NAT_SEQ_ADJ_REPLY	= CTA_SEQ_ADJ_REPLY,
 	CTA_SECMARK,		/* obsolete */
 	CTA_ZONE,
 	CTA_SECCTX,
 	CTA_TIMESTAMP,
 	CTA_MARK_MASK,
+	CTA_LABELS,
+	CTA_LABELS_MASK,
+	CTA_SYNPROXY,
 	__CTA_MAX
 };
 #define CTA_MAX (__CTA_MAX - 1)
@@ -55,6 +63,7 @@
 	CTA_TUPLE_UNSPEC,
 	CTA_TUPLE_IP,
 	CTA_TUPLE_PROTO,
+	CTA_TUPLE_ZONE,
 	__CTA_TUPLE_MAX
 };
 #define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1)
@@ -109,6 +118,7 @@
 	CTA_PROTOINFO_DCCP_STATE,
 	CTA_PROTOINFO_DCCP_ROLE,
 	CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ,
+	CTA_PROTOINFO_DCCP_PAD,
 	__CTA_PROTOINFO_DCCP_MAX,
 };
 #define CTA_PROTOINFO_DCCP_MAX (__CTA_PROTOINFO_DCCP_MAX - 1)
@@ -128,6 +138,7 @@
 	CTA_COUNTERS_BYTES,		/* 64bit counters */
 	CTA_COUNTERS32_PACKETS,		/* old 32bit counters, unused */
 	CTA_COUNTERS32_BYTES,		/* old 32bit counters, unused */
+	CTA_COUNTERS_PAD,
 	__CTA_COUNTERS_MAX
 };
 #define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1)
@@ -136,6 +147,7 @@
 	CTA_TIMESTAMP_UNSPEC,
 	CTA_TIMESTAMP_START,
 	CTA_TIMESTAMP_STOP,
+	CTA_TIMESTAMP_PAD,
 	__CTA_TIMESTAMP_MAX
 };
 #define CTA_TIMESTAMP_MAX (__CTA_TIMESTAMP_MAX - 1)
@@ -161,6 +173,15 @@
 };
 #define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1)
 
+enum ctattr_seqadj {
+	CTA_SEQADJ_UNSPEC,
+	CTA_SEQADJ_CORRECTION_POS,
+	CTA_SEQADJ_OFFSET_BEFORE,
+	CTA_SEQADJ_OFFSET_AFTER,
+	__CTA_SEQADJ_MAX
+};
+#define CTA_SEQADJ_MAX (__CTA_SEQADJ_MAX - 1)
+
 enum ctattr_natseq {
 	CTA_NAT_SEQ_UNSPEC,
 	CTA_NAT_SEQ_CORRECTION_POS,
@@ -170,6 +191,15 @@
 };
 #define CTA_NAT_SEQ_MAX (__CTA_NAT_SEQ_MAX - 1)
 
+enum ctattr_synproxy {
+	CTA_SYNPROXY_UNSPEC,
+	CTA_SYNPROXY_ISN,
+	CTA_SYNPROXY_ITS,
+	CTA_SYNPROXY_TSOFF,
+	__CTA_SYNPROXY_MAX,
+};
+#define CTA_SYNPROXY_MAX (__CTA_SYNPROXY_MAX - 1)
+
 enum ctattr_expect {
 	CTA_EXPECT_UNSPEC,
 	CTA_EXPECT_MASTER,
@@ -212,13 +242,13 @@
 
 enum ctattr_stats_cpu {
 	CTA_STATS_UNSPEC,
-	CTA_STATS_SEARCHED,
+	CTA_STATS_SEARCHED,	/* no longer used */
 	CTA_STATS_FOUND,
-	CTA_STATS_NEW,
+	CTA_STATS_NEW,		/* no longer used */
 	CTA_STATS_INVALID,
 	CTA_STATS_IGNORE,
-	CTA_STATS_DELETE,
-	CTA_STATS_DELETE_LIST,
+	CTA_STATS_DELETE,	/* no longer used */
+	CTA_STATS_DELETE_LIST,	/* no longer used */
 	CTA_STATS_INSERT,
 	CTA_STATS_INSERT_FAILED,
 	CTA_STATS_DROP,
@@ -232,6 +262,7 @@
 enum ctattr_stats_global {
 	CTA_STATS_GLOBAL_UNSPEC,
 	CTA_STATS_GLOBAL_ENTRIES,
+	CTA_STATS_GLOBAL_MAX_ENTRIES,
 	__CTA_STATS_GLOBAL_MAX,
 };
 #define CTA_STATS_GLOBAL_MAX (__CTA_STATS_GLOBAL_MAX - 1)
diff --git a/include/linux/netfilter/nfnetlink_log.h b/include/linux-private/linux/netfilter/nfnetlink_log.h
similarity index 91%
rename from include/linux/netfilter/nfnetlink_log.h
rename to include/linux-private/linux/netfilter/nfnetlink_log.h
index 2cfbf13..20983cb 100644
--- a/include/linux/netfilter/nfnetlink_log.h
+++ b/include/linux-private/linux/netfilter/nfnetlink_log.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _NFNETLINK_LOG_H
 #define _NFNETLINK_LOG_H
 
@@ -5,10 +6,6 @@
  * and not any kind of function definitions.  It is shared between kernel and
  * userspace.  Don't put kernel specific stuff in here */
 
-#ifndef __aligned_be64
-#define __aligned_be64 u_int64_t __attribute__((aligned(8)))
-#endif
-
 #include <linux/types.h>
 #include <linux/netfilter/nfnetlink.h>
 
@@ -55,6 +52,8 @@
 	NFULA_HWTYPE,			/* hardware type */
 	NFULA_HWHEADER,			/* hardware header */
 	NFULA_HWLEN,			/* hardware header length */
+	NFULA_CT,                       /* nf_conntrack_netlink.h */
+	NFULA_CT_INFO,                  /* enum ip_conntrack_info */
 
 	__NFULA_MAX
 };
@@ -97,5 +96,6 @@
 
 #define NFULNL_CFG_F_SEQ	0x0001
 #define NFULNL_CFG_F_SEQ_GLOBAL	0x0002
+#define NFULNL_CFG_F_CONNTRACK	0x0004
 
 #endif /* _NFNETLINK_LOG_H */
diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux-private/linux/netfilter/nfnetlink_queue.h
similarity index 70%
rename from include/linux/netfilter/nfnetlink_queue.h
rename to include/linux-private/linux/netfilter/nfnetlink_queue.h
index 95af967..bcb2cb5 100644
--- a/include/linux/netfilter/nfnetlink_queue.h
+++ b/include/linux-private/linux/netfilter/nfnetlink_queue.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _NFNETLINK_QUEUE_H
 #define _NFNETLINK_QUEUE_H
 
 #include <linux/types.h>
 #include <linux/netfilter/nfnetlink.h>
 
-#ifndef __aligned_be64
-#define __aligned_be64 u_int64_t __attribute__((aligned(8)))
-#endif
-
 enum nfqnl_msg_types {
 	NFQNL_MSG_PACKET,		/* packet from kernel to userspace */
 	NFQNL_MSG_VERDICT,		/* verdict from userspace to kernel */
@@ -34,6 +31,14 @@
 	__aligned_be64	usec;
 };
 
+enum nfqnl_vlan_attr {
+	NFQA_VLAN_UNSPEC,
+	NFQA_VLAN_PROTO,		/* __be16 skb vlan_proto */
+	NFQA_VLAN_TCI,			/* __be16 skb htons(vlan_tci) */
+	__NFQA_VLAN_MAX,
+};
+#define NFQA_VLAN_MAX (__NFQA_VLAN_MAX - 1)
+
 enum nfqnl_attr_type {
 	NFQA_UNSPEC,
 	NFQA_PACKET_HDR,
@@ -49,6 +54,13 @@
 	NFQA_CT,			/* nf_conntrack_netlink.h */
 	NFQA_CT_INFO,			/* enum ip_conntrack_info */
 	NFQA_CAP_LEN,			/* __u32 length of captured packet */
+	NFQA_SKB_INFO,			/* __u32 skb meta information */
+	NFQA_EXP,			/* nf_conntrack_netlink.h */
+	NFQA_UID,			/* __u32 sk uid */
+	NFQA_GID,			/* __u32 sk gid */
+	NFQA_SECCTX,			/* security context string */
+	NFQA_VLAN,			/* nested attribute: packet vlan info */
+	NFQA_L2HDR,			/* full L2 header */
 
 	__NFQA_MAX
 };
@@ -100,6 +112,17 @@
 /* Flags for NFQA_CFG_FLAGS */
 #define NFQA_CFG_F_FAIL_OPEN			(1 << 0)
 #define NFQA_CFG_F_CONNTRACK			(1 << 1)
-#define NFQA_CFG_F_MAX				(1 << 2)
+#define NFQA_CFG_F_GSO				(1 << 2)
+#define NFQA_CFG_F_UID_GID			(1 << 3)
+#define NFQA_CFG_F_SECCTX			(1 << 4)
+#define NFQA_CFG_F_MAX				(1 << 5)
+
+/* flags for NFQA_SKB_INFO */
+/* packet appears to have wrong checksums, but they are ok */
+#define NFQA_SKB_CSUMNOTREADY (1 << 0)
+/* packet is GSO (i.e., exceeds device mtu) */
+#define NFQA_SKB_GSO (1 << 1)
+/* csum not validated (incoming device doesn't support hw checksum, etc.) */
+#define NFQA_SKB_CSUM_NOTVERIFIED (1 << 2)
 
 #endif /* _NFNETLINK_QUEUE_H */
diff --git a/include/linux-private/linux/netlink.h b/include/linux-private/linux/netlink.h
new file mode 100644
index 0000000..0b2c29b
--- /dev/null
+++ b/include/linux-private/linux/netlink.h
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_NETLINK_H
+#define __LINUX_NETLINK_H
+
+#include <linux/kernel.h>
+#include <linux/socket.h> /* for __kernel_sa_family_t */
+#include <linux/types.h>
+
+#define NETLINK_ROUTE		0	/* Routing/device hook				*/
+#define NETLINK_UNUSED		1	/* Unused number				*/
+#define NETLINK_USERSOCK	2	/* Reserved for user mode socket protocols 	*/
+#define NETLINK_FIREWALL	3	/* Unused number, formerly ip_queue		*/
+#define NETLINK_SOCK_DIAG	4	/* socket monitoring				*/
+#define NETLINK_NFLOG		5	/* netfilter/iptables ULOG */
+#define NETLINK_XFRM		6	/* ipsec */
+#define NETLINK_SELINUX		7	/* SELinux event notifications */
+#define NETLINK_ISCSI		8	/* Open-iSCSI */
+#define NETLINK_AUDIT		9	/* auditing */
+#define NETLINK_FIB_LOOKUP	10	
+#define NETLINK_CONNECTOR	11
+#define NETLINK_NETFILTER	12	/* netfilter subsystem */
+#define NETLINK_IP6_FW		13
+#define NETLINK_DNRTMSG		14	/* DECnet routing messages */
+#define NETLINK_KOBJECT_UEVENT	15	/* Kernel messages to userspace */
+#define NETLINK_GENERIC		16
+/* leave room for NETLINK_DM (DM Events) */
+#define NETLINK_SCSITRANSPORT	18	/* SCSI Transports */
+#define NETLINK_ECRYPTFS	19
+#define NETLINK_RDMA		20
+#define NETLINK_CRYPTO		21	/* Crypto layer */
+#define NETLINK_SMC		22	/* SMC monitoring */
+
+#define NETLINK_INET_DIAG	NETLINK_SOCK_DIAG
+
+#define MAX_LINKS 32		
+
+struct sockaddr_nl {
+	__kernel_sa_family_t	nl_family;	/* AF_NETLINK	*/
+	unsigned short	nl_pad;		/* zero		*/
+	__u32		nl_pid;		/* port ID	*/
+       	__u32		nl_groups;	/* multicast groups mask */
+};
+
+struct nlmsghdr {
+	__u32		nlmsg_len;	/* Length of message including header */
+	__u16		nlmsg_type;	/* Message content */
+	__u16		nlmsg_flags;	/* Additional flags */
+	__u32		nlmsg_seq;	/* Sequence number */
+	__u32		nlmsg_pid;	/* Sending process port ID */
+};
+
+/* 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 */
+
+/* Modifiers to GET request */
+#define NLM_F_ROOT	0x100	/* specify tree	root	*/
+#define NLM_F_MATCH	0x200	/* return all matching	*/
+#define NLM_F_ATOMIC	0x400	/* atomic GET		*/
+#define NLM_F_DUMP	(NLM_F_ROOT|NLM_F_MATCH)
+
+/* Modifiers to NEW request */
+#define NLM_F_REPLACE	0x100	/* Override existing		*/
+#define NLM_F_EXCL	0x200	/* Do not touch, if it exists	*/
+#define NLM_F_CREATE	0x400	/* Create, if it does not exist	*/
+#define NLM_F_APPEND	0x800	/* Add to end of list		*/
+
+/* 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
+
+   True CHANGE		NLM_F_CREATE|NLM_F_REPLACE
+   Append		NLM_F_CREATE
+   Check		NLM_F_EXCL
+ */
+
+#define NLMSG_ALIGNTO	4U
+#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
+#define NLMSG_HDRLEN	 ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
+#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
+#define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
+#define NLMSG_NEXT(nlh,len)	 ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
+				  (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
+#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
+			   (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
+			   (nlh)->nlmsg_len <= (len))
+#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
+
+#define NLMSG_NOOP		0x1	/* Nothing.		*/
+#define NLMSG_ERROR		0x2	/* Error		*/
+#define NLMSG_DONE		0x3	/* End of a dump	*/
+#define NLMSG_OVERRUN		0x4	/* Data lost		*/
+
+#define NLMSG_MIN_TYPE		0x10	/* < 0x10: reserved control messages */
+
+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
+#define NETLINK_DROP_MEMBERSHIP		2
+#define NETLINK_PKTINFO			3
+#define NETLINK_BROADCAST_ERROR		4
+#define NETLINK_NO_ENOBUFS		5
+#define NETLINK_RX_RING			6
+#define NETLINK_TX_RING			7
+#define NETLINK_LISTEN_ALL_NSID		8
+#define NETLINK_LIST_MEMBERSHIPS	9
+#define NETLINK_CAP_ACK			10
+#define NETLINK_EXT_ACK			11
+
+struct nl_pktinfo {
+	__u32	group;
+};
+
+struct nl_mmap_req {
+	unsigned int	nm_block_size;
+	unsigned int	nm_block_nr;
+	unsigned int	nm_frame_size;
+	unsigned int	nm_frame_nr;
+};
+
+struct nl_mmap_hdr {
+	unsigned int	nm_status;
+	unsigned int	nm_len;
+	__u32		nm_group;
+	/* credentials */
+	__u32		nm_pid;
+	__u32		nm_uid;
+	__u32		nm_gid;
+};
+
+enum nl_mmap_status {
+	NL_MMAP_STATUS_UNUSED,
+	NL_MMAP_STATUS_RESERVED,
+	NL_MMAP_STATUS_VALID,
+	NL_MMAP_STATUS_COPY,
+	NL_MMAP_STATUS_SKIP,
+};
+
+#define NL_MMAP_MSG_ALIGNMENT		NLMSG_ALIGNTO
+#define NL_MMAP_MSG_ALIGN(sz)		__ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT)
+#define NL_MMAP_HDRLEN			NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr))
+
+#define NET_MAJOR 36		/* Major 36 is reserved for networking 						*/
+
+enum {
+	NETLINK_UNCONNECTED = 0,
+	NETLINK_CONNECTED,
+};
+
+/*
+ *  <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
+ * +---------------------+- - -+- - - - - - - - - -+- - -+
+ * |        Header       | Pad |     Payload       | Pad |
+ * |   (struct nlattr)   | ing |                   | ing |
+ * +---------------------+- - -+- - - - - - - - - -+- - -+
+ *  <-------------- nlattr->nla_len -------------->
+ */
+
+struct nlattr {
+	__u16           nla_len;
+	__u16           nla_type;
+};
+
+/*
+ * nla_type (16 bits)
+ * +---+---+-------------------------------+
+ * | N | O | Attribute Type                |
+ * +---+---+-------------------------------+
+ * N := Carries nested attributes
+ * O := Payload stored in network byte order
+ *
+ * Note: The N and O flag are mutually exclusive.
+ */
+#define NLA_F_NESTED		(1 << 15)
+#define NLA_F_NET_BYTEORDER	(1 << 14)
+#define NLA_TYPE_MASK		~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
+
+#define NLA_ALIGNTO		4
+#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/linux-private/linux/pkt_cls.h b/include/linux-private/linux/pkt_cls.h
new file mode 100644
index 0000000..be382fb
--- /dev/null
+++ b/include/linux-private/linux/pkt_cls.h
@@ -0,0 +1,610 @@
+/* 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
+
+/* 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;
+	__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_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, /*  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_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_MAX,
+};
+
+#define TCA_FLOWER_MAX (__TCA_FLOWER_MAX - 1)
+
+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),
+};
+
+/* Match-all classifier */
+
+enum {
+	TCA_MATCHALL_UNSPEC,
+	TCA_MATCHALL_CLASSID,
+	TCA_MATCHALL_ACT,
+	TCA_MATCHALL_FLAGS,
+	__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/linux-private/linux/pkt_sched.h b/include/linux-private/linux/pkt_sched.h
new file mode 100644
index 0000000..8975fd1
--- /dev/null
+++ b/include/linux-private/linux/pkt_sched.h
@@ -0,0 +1,1087 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#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_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)
+
+/* 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_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_PAD,
+	__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_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_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;
+};
+
+/* 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*/
+};
+
+/* 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	BIT(0)
+#define TC_ETF_OFFLOAD_ON	BIT(1)
+};
+
+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_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
+};
+
+#endif
diff --git a/include/linux/rtnetlink.h b/include/linux-private/linux/rtnetlink.h
similarity index 77%
rename from include/linux/rtnetlink.h
rename to include/linux-private/linux/rtnetlink.h
index 2363c18..8c1d600 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux-private/linux/rtnetlink.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_RTNETLINK_H
 #define __LINUX_RTNETLINK_H
 
@@ -120,6 +121,42 @@
 	RTM_SETDCB,
 #define RTM_SETDCB RTM_SETDCB
 
+	RTM_NEWNETCONF = 80,
+#define RTM_NEWNETCONF RTM_NEWNETCONF
+	RTM_DELNETCONF,
+#define RTM_DELNETCONF RTM_DELNETCONF
+	RTM_GETNETCONF = 82,
+#define RTM_GETNETCONF RTM_GETNETCONF
+
+	RTM_NEWMDB = 84,
+#define RTM_NEWMDB RTM_NEWMDB
+	RTM_DELMDB = 85,
+#define RTM_DELMDB RTM_DELMDB
+	RTM_GETMDB = 86,
+#define RTM_GETMDB RTM_GETMDB
+
+	RTM_NEWNSID = 88,
+#define RTM_NEWNSID RTM_NEWNSID
+	RTM_DELNSID = 89,
+#define RTM_DELNSID RTM_DELNSID
+	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_MAX,
 #define RTM_MAX		(((__RTM_MAX + 3) & ~3) - 1)
 };
@@ -141,7 +178,7 @@
 
 /* Macros to handle rtattributes */
 
-#define RTA_ALIGNTO	4
+#define RTA_ALIGNTO	4U
 #define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) )
 #define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \
 			 (rta)->rta_len >= sizeof(struct rtattr) && \
@@ -222,6 +259,13 @@
 #define RTPROT_XORP	14	/* XORP */
 #define RTPROT_NTK	15	/* Netsukuku */
 #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
 
@@ -249,6 +293,8 @@
 #define RTM_F_CLONED		0x200	/* This route is cloned		*/
 #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 */
 
@@ -283,6 +329,19 @@
 	RTA_MP_ALGO, /* no longer used */
 	RTA_TABLE,
 	RTA_MARK,
+	RTA_MFC_STATS,
+	RTA_VIA,
+	RTA_NEWDST,
+	RTA_PREF,
+	RTA_ENCAP_TYPE,
+	RTA_ENCAP,
+	RTA_EXPIRES,
+	RTA_PAD,
+	RTA_UID,
+	RTA_TTL_PROPAGATE,
+	RTA_IP_PROTO,
+	RTA_SPORT,
+	RTA_DPORT,
 	__RTA_MAX
 };
 
@@ -312,6 +371,11 @@
 #define RTNH_F_DEAD		1	/* Nexthop is dead (used by multipath)	*/
 #define RTNH_F_PERVASIVE	2	/* Do recursive gateway lookup	*/
 #define RTNH_F_ONLINK		4	/* Gateway is forced on link	*/
+#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)
 
 /* Macros to handle hexthops */
 
@@ -324,6 +388,12 @@
 #define RTNH_SPACE(len)	RTNH_ALIGN(RTNH_LENGTH(len))
 #define RTNH_DATA(rtnh)   ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0)))
 
+/* RTA_VIA */
+struct rtvia {
+	__kernel_sa_family_t	rtvia_family;
+	__u8			rtvia_addr[0];
+};
+
 /* RTM_CACHEINFO */
 
 struct rta_cacheinfo {
@@ -372,15 +442,24 @@
 #define RTAX_RTO_MIN RTAX_RTO_MIN
 	RTAX_INITRWND,
 #define RTAX_INITRWND RTAX_INITRWND
+	RTAX_QUICKACK,
+#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
 };
 
 #define RTAX_MAX (__RTAX_MAX - 1)
 
-#define RTAX_FEATURE_ECN	0x00000001
-#define RTAX_FEATURE_SACK	0x00000002
-#define RTAX_FEATURE_TIMESTAMP	0x00000004
-#define RTAX_FEATURE_ALLFRAG	0x00000008
+#define RTAX_FEATURE_ECN	(1 << 0)
+#define RTAX_FEATURE_SACK	(1 << 1)
+#define RTAX_FEATURE_TIMESTAMP	(1 << 2)
+#define RTAX_FEATURE_ALLFRAG	(1 << 3)
+
+#define RTAX_FEATURE_MASK	(RTAX_FEATURE_ECN | RTAX_FEATURE_SACK | \
+				 RTAX_FEATURE_TIMESTAMP | RTAX_FEATURE_ALLFRAG)
 
 struct rta_session {
 	__u8	proto;
@@ -403,6 +482,12 @@
 	} u;
 };
 
+struct rta_mfc_stats {
+	__u64	mfcs_packets;
+	__u64	mfcs_bytes;
+	__u64	mfcs_wrong_if;
+};
+
 /****
  *		General form of address family dependent message.
  ****/
@@ -471,9 +556,19 @@
 	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,
@@ -484,6 +579,12 @@
 	TCA_FCNT,
 	TCA_STATS2,
 	TCA_STAB,
+	TCA_PAD,
+	TCA_DUMP_INVISIBLE,
+	TCA_CHAIN,
+	TCA_HW_OFFLOAD,
+	TCA_INGRESS_BLOCK,
+	TCA_EGRESS_BLOCK,
 	__TCA_MAX
 };
 
@@ -516,7 +617,6 @@
 
 #define NDUSEROPT_MAX	(__NDUSEROPT_MAX - 1)
 
-#ifndef __KERNEL__
 /* RTnetlink multicast groups - backwards compatibility for userspace */
 #define RTMGRP_LINK		1
 #define RTMGRP_NOTIFY		2
@@ -537,7 +637,6 @@
 #define RTMGRP_DECnet_ROUTE     0x4000
 
 #define RTMGRP_IPV6_PREFIX	0x20000
-#endif
 
 /* RTnetlink multicast groups */
 enum rtnetlink_groups {
@@ -585,6 +684,24 @@
 #define RTNLGRP_PHONET_IFADDR	RTNLGRP_PHONET_IFADDR
 	RTNLGRP_PHONET_ROUTE,
 #define RTNLGRP_PHONET_ROUTE	RTNLGRP_PHONET_ROUTE
+	RTNLGRP_DCB,
+#define RTNLGRP_DCB		RTNLGRP_DCB
+	RTNLGRP_IPV4_NETCONF,
+#define RTNLGRP_IPV4_NETCONF	RTNLGRP_IPV4_NETCONF
+	RTNLGRP_IPV6_NETCONF,
+#define RTNLGRP_IPV6_NETCONF	RTNLGRP_IPV6_NETCONF
+	RTNLGRP_MDB,
+#define RTNLGRP_MDB		RTNLGRP_MDB
+	RTNLGRP_MPLS_ROUTE,
+#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_MAX
 };
 #define RTNLGRP_MAX	(__RTNLGRP_MAX - 1)
@@ -595,11 +712,38 @@
 	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))
-#define TCA_ACT_TAB 1 /* attr type must be >=1 */	
-#define TCAA_MAX 1
+/* 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)
+
+/* New extended info filters for IFLA_EXT_MASK */
+#define RTEXT_FILTER_VF		(1 << 0)
+#define RTEXT_FILTER_BRVLAN	(1 << 1)
+#define RTEXT_FILTER_BRVLAN_COMPRESSED	(1 << 2)
+#define	RTEXT_FILTER_SKIP_STATS	(1 << 3)
 
 /* End of information exported to user level */
 
-#endif	/* __LINUX_RTNETLINK_H */
+
+
+#endif /* __LINUX_RTNETLINK_H */
diff --git a/include/linux/snmp.h b/include/linux-private/linux/snmp.h
similarity index 85%
rename from include/linux/snmp.h
rename to include/linux-private/linux/snmp.h
index 1bdb4a3..abae27c 100644
--- a/include/linux/snmp.h
+++ b/include/linux-private/linux/snmp.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 /*
  * Definitions for MIBs
  *
@@ -55,6 +56,7 @@
 	IPSTATS_MIB_ECT1PKTS,			/* InECT1Pkts */
 	IPSTATS_MIB_ECT0PKTS,			/* InECT0Pkts */
 	IPSTATS_MIB_CEPKTS,			/* InCEPkts */
+	IPSTATS_MIB_REASM_OVERLAPS,		/* ReasmOverlaps */
 	__IPSTATS_MIB_MAX
 };
 
@@ -156,6 +158,7 @@
 	UDP_MIB_RCVBUFERRORS,			/* RcvbufErrors */
 	UDP_MIB_SNDBUFERRORS,			/* SndbufErrors */
 	UDP_MIB_CSUMERRORS,			/* InCsumErrors */
+	UDP_MIB_IGNOREDMULTI,			/* IgnoredMulti */
 	__UDP_MIB_MAX
 };
 
@@ -176,7 +179,6 @@
 	LINUX_MIB_TIMEWAITED,			/* TimeWaited */
 	LINUX_MIB_TIMEWAITRECYCLED,		/* TimeWaitRecycled */
 	LINUX_MIB_TIMEWAITKILLED,		/* TimeWaitKilled */
-	LINUX_MIB_PAWSPASSIVEREJECTED,		/* PAWSPassiveRejected */
 	LINUX_MIB_PAWSACTIVEREJECTED,		/* PAWSActiveRejected */
 	LINUX_MIB_PAWSESTABREJECTED,		/* PAWSEstabRejected */
 	LINUX_MIB_DELAYEDACKS,			/* DelayedACKs */
@@ -184,18 +186,12 @@
 	LINUX_MIB_DELAYEDACKLOST,		/* DelayedACKLost */
 	LINUX_MIB_LISTENOVERFLOWS,		/* ListenOverflows */
 	LINUX_MIB_LISTENDROPS,			/* ListenDrops */
-	LINUX_MIB_TCPPREQUEUED,			/* TCPPrequeued */
-	LINUX_MIB_TCPDIRECTCOPYFROMBACKLOG,	/* TCPDirectCopyFromBacklog */
-	LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE,	/* TCPDirectCopyFromPrequeue */
-	LINUX_MIB_TCPPREQUEUEDROPPED,		/* TCPPrequeueDropped */
 	LINUX_MIB_TCPHPHITS,			/* TCPHPHits */
-	LINUX_MIB_TCPHPHITSTOUSER,		/* TCPHPHitsToUser */
 	LINUX_MIB_TCPPUREACKS,			/* TCPPureAcks */
 	LINUX_MIB_TCPHPACKS,			/* TCPHPAcks */
 	LINUX_MIB_TCPRENORECOVERY,		/* TCPRenoRecovery */
 	LINUX_MIB_TCPSACKRECOVERY,		/* TCPSackRecovery */
 	LINUX_MIB_TCPSACKRENEGING,		/* TCPSACKReneging */
-	LINUX_MIB_TCPFACKREORDER,		/* TCPFACKReorder */
 	LINUX_MIB_TCPSACKREORDER,		/* TCPSACKReorder */
 	LINUX_MIB_TCPRENOREORDER,		/* TCPRenoReorder */
 	LINUX_MIB_TCPTSREORDER,			/* TCPTSReorder */
@@ -208,14 +204,12 @@
 	LINUX_MIB_TCPSACKFAILURES,		/* TCPSackFailures */
 	LINUX_MIB_TCPLOSSFAILURES,		/* TCPLossFailures */
 	LINUX_MIB_TCPFASTRETRANS,		/* TCPFastRetrans */
-	LINUX_MIB_TCPFORWARDRETRANS,		/* TCPForwardRetrans */
 	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_TCPSCHEDULERFAILED,		/* TCPSchedulerFailed */
 	LINUX_MIB_TCPRCVCOLLAPSED,		/* TCPRcvCollapsed */
 	LINUX_MIB_TCPDSACKOLDSENT,		/* TCPDSACKOldSent */
 	LINUX_MIB_TCPDSACKOFOSENT,		/* TCPDSACKOfoSent */
@@ -228,16 +222,19 @@
 	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) */
@@ -252,12 +249,40 @@
 	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_MAX
 };
 
diff --git a/include/linux-private/linux/sock_diag.h b/include/linux-private/linux/sock_diag.h
new file mode 100644
index 0000000..a69cf20
--- /dev/null
+++ b/include/linux-private/linux/sock_diag.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __SOCK_DIAG_H__
+#define __SOCK_DIAG_H__
+
+#include <linux/types.h>
+
+#define SOCK_DIAG_BY_FAMILY 20
+#define SOCK_DESTROY 21
+
+struct sock_diag_req {
+	__u8	sdiag_family;
+	__u8	sdiag_protocol;
+};
+
+enum {
+	SK_MEMINFO_RMEM_ALLOC,
+	SK_MEMINFO_RCVBUF,
+	SK_MEMINFO_WMEM_ALLOC,
+	SK_MEMINFO_SNDBUF,
+	SK_MEMINFO_FWD_ALLOC,
+	SK_MEMINFO_WMEM_QUEUED,
+	SK_MEMINFO_OPTMEM,
+	SK_MEMINFO_BACKLOG,
+	SK_MEMINFO_DROPS,
+
+	SK_MEMINFO_VARS,
+};
+
+enum sknetlink_groups {
+	SKNLGRP_NONE,
+	SKNLGRP_INET_TCP_DESTROY,
+	SKNLGRP_INET_UDP_DESTROY,
+	SKNLGRP_INET6_TCP_DESTROY,
+	SKNLGRP_INET6_UDP_DESTROY,
+	__SKNLGRP_MAX,
+};
+#define SKNLGRP_MAX	(__SKNLGRP_MAX - 1)
+
+#endif /* __SOCK_DIAG_H__ */
diff --git a/include/linux-private/linux/socket.h b/include/linux-private/linux/socket.h
new file mode 100644
index 0000000..268b948
--- /dev/null
+++ b/include/linux-private/linux/socket.h
@@ -0,0 +1,22 @@
+/* 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 */
+#define _K_SS_ALIGNSIZE	(__alignof__ (struct sockaddr *))
+				/* Implementation specific desired alignment */
+
+typedef unsigned short __kernel_sa_family_t;
+
+struct __kernel_sockaddr_storage {
+	__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 */
+} __attribute__ ((aligned(_K_SS_ALIGNSIZE)));	/* force desired alignment */
+
+#endif /* _LINUX_SOCKET_H */
diff --git a/include/linux-private/linux/tc_act/tc_gact.h b/include/linux-private/linux/tc_act/tc_gact.h
new file mode 100644
index 0000000..94273c3
--- /dev/null
+++ b/include/linux-private/linux/tc_act/tc_gact.h
@@ -0,0 +1,34 @@
+/* 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;
+
+};
+
+struct tc_gact_p {
+#define PGACT_NONE              0
+#define PGACT_NETRAND           1
+#define PGACT_DETERM            2
+#define MAX_RAND                (PGACT_DETERM + 1 )
+	__u16                 ptype;
+	__u16                 pval;
+	int                   paction;
+};
+ 
+enum {
+	TCA_GACT_UNSPEC,
+	TCA_GACT_TM,
+	TCA_GACT_PARMS,
+	TCA_GACT_PROB,
+	TCA_GACT_PAD,
+	__TCA_GACT_MAX
+};
+#define TCA_GACT_MAX (__TCA_GACT_MAX - 1)
+ 
+#endif
diff --git a/include/linux/tc_act/tc_mirred.h b/include/linux-private/linux/tc_act/tc_mirred.h
similarity index 73%
rename from include/linux/tc_act/tc_mirred.h
rename to include/linux-private/linux/tc_act/tc_mirred.h
index 7561750..5dd671c 100644
--- a/include/linux/tc_act/tc_mirred.h
+++ b/include/linux-private/linux/tc_act/tc_mirred.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_TC_MIR_H
 #define __LINUX_TC_MIR_H
 
@@ -9,19 +10,20 @@
 #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/linux-private/linux/tc_act/tc_skbedit.h b/include/linux-private/linux/tc_act/tc_skbedit.h
new file mode 100644
index 0000000..6de6071
--- /dev/null
+++ b/include/linux-private/linux/tc_act/tc_skbedit.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2008, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Author: Alexander Duyck <alexander.h.duyck@intel.com>
+ */
+
+#ifndef __LINUX_TC_SKBEDIT_H
+#define __LINUX_TC_SKBEDIT_H
+
+#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;
+};
+
+enum {
+	TCA_SKBEDIT_UNSPEC,
+	TCA_SKBEDIT_TM,
+	TCA_SKBEDIT_PARMS,
+	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)
+
+#endif
diff --git a/include/linux-private/linux/tc_act/tc_vlan.h b/include/linux-private/linux/tc_act/tc_vlan.h
new file mode 100644
index 0000000..0d7b5fd
--- /dev/null
+++ b/include/linux-private/linux/tc_act/tc_vlan.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
+ *
+ * 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_VLAN_H
+#define __LINUX_TC_VLAN_H
+
+#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;
+	int v_action;
+};
+
+enum {
+	TCA_VLAN_UNSPEC,
+	TCA_VLAN_TM,
+	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)
+
+#endif
diff --git a/include/linux/tc_ematch/tc_em_meta.h b/include/linux-private/linux/tc_ematch/tc_em_meta.h
similarity index 91%
rename from include/linux/tc_ematch/tc_em_meta.h
rename to include/linux-private/linux/tc_ematch/tc_em_meta.h
index fe815e2..cf30b5b 100644
--- a/include/linux/tc_ematch/tc_em_meta.h
+++ b/include/linux-private/linux/tc_ematch/tc_em_meta.h
@@ -1,6 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef __LINUX_TC_EM_META_H
 #define __LINUX_TC_EM_META_H
 
+#include <linux/types.h>
+#include <linux/pkt_cls.h>
+
 enum {
 	TCA_EM_META_UNSPEC,
 	TCA_EM_META_HDR,
@@ -64,7 +68,7 @@
 	TCF_META_ID_SK_FORWARD_ALLOCS,
 	TCF_META_ID_SK_SNDBUF,
  	TCF_META_ID_SK_ALLOCS,
- 	TCF_META_ID_SK_ROUTE_CAPS,
+	__TCF_META_ID_SK_ROUTE_CAPS,	/* unimplemented but in ABI already */
  	TCF_META_ID_SK_HASH,
  	TCF_META_ID_SK_LINGERTIME,
  	TCF_META_ID_SK_ACK_BACKLOG,
diff --git a/include/linux-private/linux/veth.h b/include/linux-private/linux/veth.h
new file mode 100644
index 0000000..52b58e5
--- /dev/null
+++ b/include/linux-private/linux/veth.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __NET_VETH_H_
+#define __NET_VETH_H_
+
+enum {
+	VETH_INFO_UNSPEC,
+	VETH_INFO_PEER,
+
+	__VETH_INFO_MAX
+#define VETH_INFO_MAX	(__VETH_INFO_MAX - 1)
+};
+
+#endif
diff --git a/include/linux-private/linux/xfrm.h b/include/linux-private/linux/xfrm.h
new file mode 100644
index 0000000..5cdda9d
--- /dev/null
+++ b/include/linux-private/linux/xfrm.h
@@ -0,0 +1,540 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LINUX_XFRM_H
+#define _LINUX_XFRM_H
+
+#include <linux/in6.h>
+#include <linux/types.h>
+
+/* All of the structures in this file may not change size as they are
+ * passed into the kernel from userspace via netlink sockets.
+ */
+
+/* Structure to encapsulate addresses. I do not want to use
+ * "standard" structure. My apologies.
+ */
+typedef union {
+	__be32		a4;
+	__be32		a6[4];
+	struct in6_addr	in6;
+} xfrm_address_t;
+
+/* Ident of a specific xfrm_state. It is used on input to lookup
+ * the state by (spi,daddr,ah/esp) or to store information about
+ * spi, protocol and tunnel address on output.
+ */
+struct xfrm_id {
+	xfrm_address_t	daddr;
+	__be32		spi;
+	__u8		proto;
+};
+
+struct xfrm_sec_ctx {
+	__u8	ctx_doi;
+	__u8	ctx_alg;
+	__u16	ctx_len;
+	__u32	ctx_sid;
+	char	ctx_str[0];
+};
+
+/* Security Context Domains of Interpretation */
+#define XFRM_SC_DOI_RESERVED 0
+#define XFRM_SC_DOI_LSM 1
+
+/* Security Context Algorithms */
+#define XFRM_SC_ALG_RESERVED 0
+#define XFRM_SC_ALG_SELINUX 1
+
+/* Selector, used as selector both on policy rules (SPD) and SAs. */
+
+struct xfrm_selector {
+	xfrm_address_t	daddr;
+	xfrm_address_t	saddr;
+	__be16	dport;
+	__be16	dport_mask;
+	__be16	sport;
+	__be16	sport_mask;
+	__u16	family;
+	__u8	prefixlen_d;
+	__u8	prefixlen_s;
+	__u8	proto;
+	int	ifindex;
+	__kernel_uid32_t	user;
+};
+
+#define XFRM_INF (~(__u64)0)
+
+struct xfrm_lifetime_cfg {
+	__u64	soft_byte_limit;
+	__u64	hard_byte_limit;
+	__u64	soft_packet_limit;
+	__u64	hard_packet_limit;
+	__u64	soft_add_expires_seconds;
+	__u64	hard_add_expires_seconds;
+	__u64	soft_use_expires_seconds;
+	__u64	hard_use_expires_seconds;
+};
+
+struct xfrm_lifetime_cur {
+	__u64	bytes;
+	__u64	packets;
+	__u64	add_time;
+	__u64	use_time;
+};
+
+struct xfrm_replay_state {
+	__u32	oseq;
+	__u32	seq;
+	__u32	bitmap;
+};
+
+#define XFRMA_REPLAY_ESN_MAX	4096
+
+struct xfrm_replay_state_esn {
+	unsigned int	bmp_len;
+	__u32		oseq;
+	__u32		seq;
+	__u32		oseq_hi;
+	__u32		seq_hi;
+	__u32		replay_window;
+	__u32		bmp[0];
+};
+
+struct xfrm_algo {
+	char		alg_name[64];
+	unsigned int	alg_key_len;    /* in bits */
+	char		alg_key[0];
+};
+
+struct xfrm_algo_auth {
+	char		alg_name[64];
+	unsigned int	alg_key_len;    /* in bits */
+	unsigned int	alg_trunc_len;  /* in bits */
+	char		alg_key[0];
+};
+
+struct xfrm_algo_aead {
+	char		alg_name[64];
+	unsigned int	alg_key_len;	/* in bits */
+	unsigned int	alg_icv_len;	/* in bits */
+	char		alg_key[0];
+};
+
+struct xfrm_stats {
+	__u32	replay_window;
+	__u32	replay;
+	__u32	integrity_failed;
+};
+
+enum {
+	XFRM_POLICY_TYPE_MAIN	= 0,
+	XFRM_POLICY_TYPE_SUB	= 1,
+	XFRM_POLICY_TYPE_MAX	= 2,
+	XFRM_POLICY_TYPE_ANY	= 255
+};
+
+enum {
+	XFRM_POLICY_IN	= 0,
+	XFRM_POLICY_OUT	= 1,
+	XFRM_POLICY_FWD	= 2,
+	XFRM_POLICY_MASK = 3,
+	XFRM_POLICY_MAX	= 3
+};
+
+enum {
+	XFRM_SHARE_ANY,		/* No limitations */
+	XFRM_SHARE_SESSION,	/* For this session only */
+	XFRM_SHARE_USER,	/* For this user only */
+	XFRM_SHARE_UNIQUE	/* Use once */
+};
+
+#define XFRM_MODE_TRANSPORT 0
+#define XFRM_MODE_TUNNEL 1
+#define XFRM_MODE_ROUTEOPTIMIZATION 2
+#define XFRM_MODE_IN_TRIGGER 3
+#define XFRM_MODE_BEET 4
+#define XFRM_MODE_MAX 5
+
+/* Netlink configuration messages.  */
+enum {
+	XFRM_MSG_BASE = 0x10,
+
+	XFRM_MSG_NEWSA = 0x10,
+#define XFRM_MSG_NEWSA XFRM_MSG_NEWSA
+	XFRM_MSG_DELSA,
+#define XFRM_MSG_DELSA XFRM_MSG_DELSA
+	XFRM_MSG_GETSA,
+#define XFRM_MSG_GETSA XFRM_MSG_GETSA
+
+	XFRM_MSG_NEWPOLICY,
+#define XFRM_MSG_NEWPOLICY XFRM_MSG_NEWPOLICY
+	XFRM_MSG_DELPOLICY,
+#define XFRM_MSG_DELPOLICY XFRM_MSG_DELPOLICY
+	XFRM_MSG_GETPOLICY,
+#define XFRM_MSG_GETPOLICY XFRM_MSG_GETPOLICY
+
+	XFRM_MSG_ALLOCSPI,
+#define XFRM_MSG_ALLOCSPI XFRM_MSG_ALLOCSPI
+	XFRM_MSG_ACQUIRE,
+#define XFRM_MSG_ACQUIRE XFRM_MSG_ACQUIRE
+	XFRM_MSG_EXPIRE,
+#define XFRM_MSG_EXPIRE XFRM_MSG_EXPIRE
+
+	XFRM_MSG_UPDPOLICY,
+#define XFRM_MSG_UPDPOLICY XFRM_MSG_UPDPOLICY
+	XFRM_MSG_UPDSA,
+#define XFRM_MSG_UPDSA XFRM_MSG_UPDSA
+
+	XFRM_MSG_POLEXPIRE,
+#define XFRM_MSG_POLEXPIRE XFRM_MSG_POLEXPIRE
+
+	XFRM_MSG_FLUSHSA,
+#define XFRM_MSG_FLUSHSA XFRM_MSG_FLUSHSA
+	XFRM_MSG_FLUSHPOLICY,
+#define XFRM_MSG_FLUSHPOLICY XFRM_MSG_FLUSHPOLICY
+
+	XFRM_MSG_NEWAE,
+#define XFRM_MSG_NEWAE XFRM_MSG_NEWAE
+	XFRM_MSG_GETAE,
+#define XFRM_MSG_GETAE XFRM_MSG_GETAE
+
+	XFRM_MSG_REPORT,
+#define XFRM_MSG_REPORT XFRM_MSG_REPORT
+
+	XFRM_MSG_MIGRATE,
+#define XFRM_MSG_MIGRATE XFRM_MSG_MIGRATE
+
+	XFRM_MSG_NEWSADINFO,
+#define XFRM_MSG_NEWSADINFO XFRM_MSG_NEWSADINFO
+	XFRM_MSG_GETSADINFO,
+#define XFRM_MSG_GETSADINFO XFRM_MSG_GETSADINFO
+
+	XFRM_MSG_NEWSPDINFO,
+#define XFRM_MSG_NEWSPDINFO XFRM_MSG_NEWSPDINFO
+	XFRM_MSG_GETSPDINFO,
+#define XFRM_MSG_GETSPDINFO XFRM_MSG_GETSPDINFO
+
+	XFRM_MSG_MAPPING,
+#define XFRM_MSG_MAPPING XFRM_MSG_MAPPING
+	__XFRM_MSG_MAX
+};
+#define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1)
+
+#define XFRM_NR_MSGTYPES (XFRM_MSG_MAX + 1 - XFRM_MSG_BASE)
+
+/*
+ * Generic LSM security context for comunicating to user space
+ * NOTE: Same format as sadb_x_sec_ctx
+ */
+struct xfrm_user_sec_ctx {
+	__u16			len;
+	__u16			exttype;
+	__u8			ctx_alg;  /* LSMs: e.g., selinux == 1 */
+	__u8			ctx_doi;
+	__u16			ctx_len;
+};
+
+struct xfrm_user_tmpl {
+	struct xfrm_id		id;
+	__u16			family;
+	xfrm_address_t		saddr;
+	__u32			reqid;
+	__u8			mode;
+	__u8			share;
+	__u8			optional;
+	__u32			aalgos;
+	__u32			ealgos;
+	__u32			calgos;
+};
+
+struct xfrm_encap_tmpl {
+	__u16		encap_type;
+	__be16		encap_sport;
+	__be16		encap_dport;
+	xfrm_address_t	encap_oa;
+};
+
+/* AEVENT flags  */
+enum xfrm_ae_ftype_t {
+	XFRM_AE_UNSPEC,
+	XFRM_AE_RTHR=1,	/* replay threshold*/
+	XFRM_AE_RVAL=2, /* replay value */
+	XFRM_AE_LVAL=4, /* lifetime value */
+	XFRM_AE_ETHR=8, /* expiry timer threshold */
+	XFRM_AE_CR=16, /* Event cause is replay update */
+	XFRM_AE_CE=32, /* Event cause is timer expiry */
+	XFRM_AE_CU=64, /* Event cause is policy update */
+	__XFRM_AE_MAX
+
+#define XFRM_AE_MAX (__XFRM_AE_MAX - 1)
+};
+
+struct xfrm_userpolicy_type {
+	__u8		type;
+	__u16		reserved1;
+	__u8		reserved2;
+};
+
+/* Netlink message attributes.  */
+enum xfrm_attr_type_t {
+	XFRMA_UNSPEC,
+	XFRMA_ALG_AUTH,		/* struct xfrm_algo */
+	XFRMA_ALG_CRYPT,	/* struct xfrm_algo */
+	XFRMA_ALG_COMP,		/* struct xfrm_algo */
+	XFRMA_ENCAP,		/* struct xfrm_algo + struct xfrm_encap_tmpl */
+	XFRMA_TMPL,		/* 1 or more struct xfrm_user_tmpl */
+	XFRMA_SA,		/* struct xfrm_usersa_info  */
+	XFRMA_POLICY,		/*struct xfrm_userpolicy_info */
+	XFRMA_SEC_CTX,		/* struct xfrm_sec_ctx */
+	XFRMA_LTIME_VAL,
+	XFRMA_REPLAY_VAL,
+	XFRMA_REPLAY_THRESH,
+	XFRMA_ETIMER_THRESH,
+	XFRMA_SRCADDR,		/* xfrm_address_t */
+	XFRMA_COADDR,		/* xfrm_address_t */
+	XFRMA_LASTUSED,		/* unsigned long  */
+	XFRMA_POLICY_TYPE,	/* struct xfrm_userpolicy_type */
+	XFRMA_MIGRATE,
+	XFRMA_ALG_AEAD,		/* struct xfrm_algo_aead */
+	XFRMA_KMADDRESS,        /* struct xfrm_user_kmaddress */
+	XFRMA_ALG_AUTH_TRUNC,	/* struct xfrm_algo_auth */
+	XFRMA_MARK,		/* struct xfrm_mark */
+	XFRMA_TFCPAD,		/* __u32 */
+	XFRMA_REPLAY_ESN_VAL,	/* struct xfrm_replay_state_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)
+};
+
+struct xfrm_mark {
+	__u32           v; /* value */
+	__u32           m; /* mask */
+};
+
+enum xfrm_sadattr_type_t {
+	XFRMA_SAD_UNSPEC,
+	XFRMA_SAD_CNT,
+	XFRMA_SAD_HINFO,
+	__XFRMA_SAD_MAX
+
+#define XFRMA_SAD_MAX (__XFRMA_SAD_MAX - 1)
+};
+
+struct xfrmu_sadhinfo {
+	__u32 sadhcnt; /* current hash bkts */
+	__u32 sadhmcnt; /* max allowed hash bkts */
+};
+
+enum xfrm_spdattr_type_t {
+	XFRMA_SPD_UNSPEC,
+	XFRMA_SPD_INFO,
+	XFRMA_SPD_HINFO,
+	XFRMA_SPD_IPV4_HTHRESH,
+	XFRMA_SPD_IPV6_HTHRESH,
+	__XFRMA_SPD_MAX
+
+#define XFRMA_SPD_MAX (__XFRMA_SPD_MAX - 1)
+};
+
+struct xfrmu_spdinfo {
+	__u32 incnt;
+	__u32 outcnt;
+	__u32 fwdcnt;
+	__u32 inscnt;
+	__u32 outscnt;
+	__u32 fwdscnt;
+};
+
+struct xfrmu_spdhinfo {
+	__u32 spdhcnt;
+	__u32 spdhmcnt;
+};
+
+struct xfrmu_spdhthresh {
+	__u8 lbits;
+	__u8 rbits;
+};
+
+struct xfrm_usersa_info {
+	struct xfrm_selector		sel;
+	struct xfrm_id			id;
+	xfrm_address_t			saddr;
+	struct xfrm_lifetime_cfg	lft;
+	struct xfrm_lifetime_cur	curlft;
+	struct xfrm_stats		stats;
+	__u32				seq;
+	__u32				reqid;
+	__u16				family;
+	__u8				mode;		/* XFRM_MODE_xxx */
+	__u8				replay_window;
+	__u8				flags;
+#define XFRM_STATE_NOECN	1
+#define XFRM_STATE_DECAP_DSCP	2
+#define XFRM_STATE_NOPMTUDISC	4
+#define XFRM_STATE_WILDRECV	8
+#define XFRM_STATE_ICMP		16
+#define XFRM_STATE_AF_UNSPEC	32
+#define XFRM_STATE_ALIGN4	64
+#define XFRM_STATE_ESN		128
+};
+
+#define XFRM_SA_XFLAG_DONT_ENCAP_DSCP	1
+
+struct xfrm_usersa_id {
+	xfrm_address_t			daddr;
+	__be32				spi;
+	__u16				family;
+	__u8				proto;
+};
+
+struct xfrm_aevent_id {
+	struct xfrm_usersa_id		sa_id;
+	xfrm_address_t			saddr;
+	__u32				flags;
+	__u32				reqid;
+};
+
+struct xfrm_userspi_info {
+	struct xfrm_usersa_info		info;
+	__u32				min;
+	__u32				max;
+};
+
+struct xfrm_userpolicy_info {
+	struct xfrm_selector		sel;
+	struct xfrm_lifetime_cfg	lft;
+	struct xfrm_lifetime_cur	curlft;
+	__u32				priority;
+	__u32				index;
+	__u8				dir;
+	__u8				action;
+#define XFRM_POLICY_ALLOW	0
+#define XFRM_POLICY_BLOCK	1
+	__u8				flags;
+#define XFRM_POLICY_LOCALOK	1	/* Allow user to override global policy */
+	/* Automatically expand selector to include matching ICMP payloads. */
+#define XFRM_POLICY_ICMP	2
+	__u8				share;
+};
+
+struct xfrm_userpolicy_id {
+	struct xfrm_selector		sel;
+	__u32				index;
+	__u8				dir;
+};
+
+struct xfrm_user_acquire {
+	struct xfrm_id			id;
+	xfrm_address_t			saddr;
+	struct xfrm_selector		sel;
+	struct xfrm_userpolicy_info	policy;
+	__u32				aalgos;
+	__u32				ealgos;
+	__u32				calgos;
+	__u32				seq;
+};
+
+struct xfrm_user_expire {
+	struct xfrm_usersa_info		state;
+	__u8				hard;
+};
+
+struct xfrm_user_polexpire {
+	struct xfrm_userpolicy_info	pol;
+	__u8				hard;
+};
+
+struct xfrm_usersa_flush {
+	__u8				proto;
+};
+
+struct xfrm_user_report {
+	__u8				proto;
+	struct xfrm_selector		sel;
+};
+
+/* Used by MIGRATE to pass addresses IKE should use to perform
+ * SA negotiation with the peer */
+struct xfrm_user_kmaddress {
+	xfrm_address_t                  local;
+	xfrm_address_t                  remote;
+	__u32				reserved;
+	__u16				family;
+};
+
+struct xfrm_user_migrate {
+	xfrm_address_t			old_daddr;
+	xfrm_address_t			old_saddr;
+	xfrm_address_t			new_daddr;
+	xfrm_address_t			new_saddr;
+	__u8				proto;
+	__u8				mode;
+	__u16				reserved;
+	__u32				reqid;
+	__u16				old_family;
+	__u16				new_family;
+};
+
+struct xfrm_user_mapping {
+	struct xfrm_usersa_id		id;
+	__u32				reqid;
+	xfrm_address_t			old_saddr;
+	xfrm_address_t			new_saddr;
+	__be16				old_sport;
+	__be16				new_sport;
+};
+
+struct xfrm_address_filter {
+	xfrm_address_t			saddr;
+	xfrm_address_t			daddr;
+	__u16				family;
+	__u8				splen;
+	__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
+#define XFRMGRP_SA		4
+#define XFRMGRP_POLICY		8
+#define XFRMGRP_REPORT		0x20
+
+enum xfrm_nlgroups {
+	XFRMNLGRP_NONE,
+#define XFRMNLGRP_NONE		XFRMNLGRP_NONE
+	XFRMNLGRP_ACQUIRE,
+#define XFRMNLGRP_ACQUIRE	XFRMNLGRP_ACQUIRE
+	XFRMNLGRP_EXPIRE,
+#define XFRMNLGRP_EXPIRE	XFRMNLGRP_EXPIRE
+	XFRMNLGRP_SA,
+#define XFRMNLGRP_SA		XFRMNLGRP_SA
+	XFRMNLGRP_POLICY,
+#define XFRMNLGRP_POLICY	XFRMNLGRP_POLICY
+	XFRMNLGRP_AEVENTS,
+#define XFRMNLGRP_AEVENTS	XFRMNLGRP_AEVENTS
+	XFRMNLGRP_REPORT,
+#define XFRMNLGRP_REPORT	XFRMNLGRP_REPORT
+	XFRMNLGRP_MIGRATE,
+#define XFRMNLGRP_MIGRATE	XFRMNLGRP_MIGRATE
+	XFRMNLGRP_MAPPING,
+#define XFRMNLGRP_MAPPING	XFRMNLGRP_MAPPING
+	__XFRMNLGRP_MAX
+};
+#define XFRMNLGRP_MAX	(__XFRMNLGRP_MAX - 1)
+
+#endif /* _LINUX_XFRM_H */
diff --git a/include/linux/if_bad.h b/include/linux/if_bad.h
deleted file mode 100644
index bbb3ea3..0000000
--- a/include/linux/if_bad.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * 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.
- *
- *		Global definitions for the INET interface module.
- *
- * Version:	@(#)if.h	1.0.2	04/18/93
- *
- * Authors:	Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988
- *		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_IF_H
-#define _LINUX_IF_H
-
-#define	IFNAMSIZ	16
-#define	IFALIASZ	256
-
-/* Standard interface flags (netdevice->flags). */
-#define	IFF_UP		0x1		/* interface is up		*/
-#define	IFF_BROADCAST	0x2		/* broadcast address valid	*/
-#define	IFF_DEBUG	0x4		/* turn on debugging		*/
-#define	IFF_LOOPBACK	0x8		/* is a loopback net		*/
-#define	IFF_POINTOPOINT	0x10		/* interface is has p-p link	*/
-#define	IFF_NOTRAILERS	0x20		/* avoid use of trailers	*/
-#define	IFF_RUNNING	0x40		/* interface RFC2863 OPER_UP	*/
-#define	IFF_NOARP	0x80		/* no ARP protocol		*/
-#define	IFF_PROMISC	0x100		/* receive all packets		*/
-#define	IFF_ALLMULTI	0x200		/* receive all multicast packets*/
-
-#define IFF_MASTER	0x400		/* master of a load balancer 	*/
-#define IFF_SLAVE	0x800		/* slave of a load balancer	*/
-
-#define IFF_MULTICAST	0x1000		/* Supports multicast		*/
-
-#define IFF_PORTSEL	0x2000          /* can set media type		*/
-#define IFF_AUTOMEDIA	0x4000		/* auto media select active	*/
-#define IFF_DYNAMIC	0x8000		/* dialup device with changing addresses*/
-
-#define IFF_LOWER_UP	0x10000		/* driver signals L1 up		*/
-#define IFF_DORMANT	0x20000		/* driver signals dormant	*/
-
-#define IFF_ECHO	0x40000		/* echo sent packets		*/
-
-#define IFF_VOLATILE	(IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\
-		IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT)
-
-/* Private (from user) interface flags (netdevice->priv_flags). */
-#define IFF_802_1Q_VLAN 0x1             /* 802.1Q VLAN device.          */
-#define IFF_EBRIDGE	0x2		/* Ethernet bridging device.	*/
-#define IFF_SLAVE_INACTIVE	0x4	/* bonding slave not the curr. active */
-#define IFF_MASTER_8023AD	0x8	/* bonding master, 802.3ad. 	*/
-#define IFF_MASTER_ALB	0x10		/* bonding master, balance-alb.	*/
-#define IFF_BONDING	0x20		/* bonding master or slave	*/
-#define IFF_SLAVE_NEEDARP 0x40		/* need ARPs for validation	*/
-#define IFF_ISATAP	0x80		/* ISATAP interface (RFC4214)	*/
-#define IFF_MASTER_ARPMON 0x100		/* bonding master, ARP mon in use */
-#define IFF_WAN_HDLC	0x200		/* WAN HDLC device		*/
-#define IFF_XMIT_DST_RELEASE 0x400	/* dev_hard_start_xmit() is allowed to
-					 * release skb->dst
-					 */
-#define IFF_DONT_BRIDGE 0x800		/* disallow bridging this ether dev */
-#define IFF_IN_NETPOLL	0x1000		/* whether we are processing netpoll */
-#define IFF_DISABLE_NETPOLL	0x2000	/* disable netpoll at run-time */
-#define IFF_MACVLAN_PORT	0x4000	/* device used as macvlan port */
-#define IFF_BRIDGE_PORT	0x8000		/* device used as bridge port */
-#define IFF_OVS_DATAPATH	0x10000	/* device used as Open vSwitch
-					 * datapath port */
-
-#define IF_GET_IFACE	0x0001		/* for querying only */
-#define IF_GET_PROTO	0x0002
-
-/* For definitions see hdlc.h */
-#define IF_IFACE_V35	0x1000		/* V.35 serial interface	*/
-#define IF_IFACE_V24	0x1001		/* V.24 serial interface	*/
-#define IF_IFACE_X21	0x1002		/* X.21 serial interface	*/
-#define IF_IFACE_T1	0x1003		/* T1 telco serial interface	*/
-#define IF_IFACE_E1	0x1004		/* E1 telco serial interface	*/
-#define IF_IFACE_SYNC_SERIAL 0x1005	/* can't be set by software	*/
-#define IF_IFACE_X21D   0x1006          /* X.21 Dual Clocking (FarSite) */
-
-/* For definitions see hdlc.h */
-#define IF_PROTO_HDLC	0x2000		/* raw HDLC protocol		*/
-#define IF_PROTO_PPP	0x2001		/* PPP protocol			*/
-#define IF_PROTO_CISCO	0x2002		/* Cisco HDLC protocol		*/
-#define IF_PROTO_FR	0x2003		/* Frame Relay protocol		*/
-#define IF_PROTO_FR_ADD_PVC 0x2004	/*    Create FR PVC		*/
-#define IF_PROTO_FR_DEL_PVC 0x2005	/*    Delete FR PVC		*/
-#define IF_PROTO_X25	0x2006		/* X.25				*/
-#define IF_PROTO_HDLC_ETH 0x2007	/* raw HDLC, Ethernet emulation	*/
-#define IF_PROTO_FR_ADD_ETH_PVC 0x2008	/*  Create FR Ethernet-bridged PVC */
-#define IF_PROTO_FR_DEL_ETH_PVC 0x2009	/*  Delete FR Ethernet-bridged PVC */
-#define IF_PROTO_FR_PVC	0x200A		/* for reading PVC status	*/
-#define IF_PROTO_FR_ETH_PVC 0x200B
-#define IF_PROTO_RAW    0x200C          /* RAW Socket                   */
-
-/* RFC 2863 operational status */
-enum {
-	IF_OPER_UNKNOWN,
-	IF_OPER_NOTPRESENT,
-	IF_OPER_DOWN,
-	IF_OPER_LOWERLAYERDOWN,
-	IF_OPER_TESTING,
-	IF_OPER_DORMANT,
-	IF_OPER_UP,
-};
-
-/* link modes */
-enum {
-	IF_LINK_MODE_DEFAULT,
-	IF_LINK_MODE_DORMANT,	/* limit upward transition to dormant */
-};
-
-/* carrier state */
-enum {
-	IF_CARRIER_DOWN,
-	IF_CARRIER_UP
-};
-
-/*
- *	Device mapping structure. I'd just gone off and designed a
- *	beautiful scheme using only loadable modules with arguments
- *	for driver options and along come the PCMCIA people 8)
- *
- *	Ah well. The get() side of this is good for WDSETUP, and it'll
- *	be handy for debugging things. The set side is fine for now and
- *	being very small might be worth keeping for clean configuration.
- */
-
-struct ifmap {
-	unsigned long mem_start;
-	unsigned long mem_end;
-	unsigned short base_addr;
-	unsigned char irq;
-	unsigned char dma;
-	unsigned char port;
-	/* 3 bytes spare */
-};
-
-#endif /* _LINUX_IF_H */
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
deleted file mode 100644
index 5db2975..0000000
--- a/include/linux/if_bridge.h
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- *	Linux ethernet bridge
- *
- *	Authors:
- *	Lennert Buytenhek		<buytenh@gnu.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 _UAPI_LINUX_IF_BRIDGE_H
-#define _UAPI_LINUX_IF_BRIDGE_H
-
-#include <linux/types.h>
-
-#define SYSFS_BRIDGE_ATTR	"bridge"
-#define SYSFS_BRIDGE_FDB	"brforward"
-#define SYSFS_BRIDGE_PORT_SUBDIR "brif"
-#define SYSFS_BRIDGE_PORT_ATTR	"brport"
-#define SYSFS_BRIDGE_PORT_LINK	"bridge"
-
-#define BRCTL_VERSION 1
-
-#define BRCTL_GET_VERSION 0
-#define BRCTL_GET_BRIDGES 1
-#define BRCTL_ADD_BRIDGE 2
-#define BRCTL_DEL_BRIDGE 3
-#define BRCTL_ADD_IF 4
-#define BRCTL_DEL_IF 5
-#define BRCTL_GET_BRIDGE_INFO 6
-#define BRCTL_GET_PORT_LIST 7
-#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8
-#define BRCTL_SET_BRIDGE_HELLO_TIME 9
-#define BRCTL_SET_BRIDGE_MAX_AGE 10
-#define BRCTL_SET_AGEING_TIME 11
-#define BRCTL_SET_GC_INTERVAL 12
-#define BRCTL_GET_PORT_INFO 13
-#define BRCTL_SET_BRIDGE_STP_STATE 14
-#define BRCTL_SET_BRIDGE_PRIORITY 15
-#define BRCTL_SET_PORT_PRIORITY 16
-#define BRCTL_SET_PATH_COST 17
-#define BRCTL_GET_FDB_ENTRIES 18
-
-#define BR_STATE_DISABLED 0
-#define BR_STATE_LISTENING 1
-#define BR_STATE_LEARNING 2
-#define BR_STATE_FORWARDING 3
-#define BR_STATE_BLOCKING 4
-
-struct __bridge_info {
-	__u64 designated_root;
-	__u64 bridge_id;
-	__u32 root_path_cost;
-	__u32 max_age;
-	__u32 hello_time;
-	__u32 forward_delay;
-	__u32 bridge_max_age;
-	__u32 bridge_hello_time;
-	__u32 bridge_forward_delay;
-	__u8 topology_change;
-	__u8 topology_change_detected;
-	__u8 root_port;
-	__u8 stp_enabled;
-	__u32 ageing_time;
-	__u32 gc_interval;
-	__u32 hello_timer_value;
-	__u32 tcn_timer_value;
-	__u32 topology_change_timer_value;
-	__u32 gc_timer_value;
-};
-
-struct __port_info {
-	__u64 designated_root;
-	__u64 designated_bridge;
-	__u16 port_id;
-	__u16 designated_port;
-	__u32 path_cost;
-	__u32 designated_cost;
-	__u8 state;
-	__u8 top_change_ack;
-	__u8 config_pending;
-	__u8 unused0;
-	__u32 message_age_timer_value;
-	__u32 forward_delay_timer_value;
-	__u32 hold_timer_value;
-};
-
-struct __fdb_entry {
-	__u8 mac_addr[6];
-	__u8 port_no;
-	__u8 is_local;
-	__u32 ageing_timer_value;
-	__u8 port_hi;
-	__u8 pad0;
-	__u16 unused;
-};
-
-/* Bridge Flags */
-#define BRIDGE_FLAGS_MASTER	1	/* Bridge command to/from master */
-#define BRIDGE_FLAGS_SELF	2	/* Bridge command to/from lowerdev */
-
-#define BRIDGE_MODE_VEB		0	/* Default loopback mode */
-#define BRIDGE_MODE_VEPA	1	/* 802.1Qbg defined VEPA mode */
-
-/* Bridge management nested attributes
- * [IFLA_AF_SPEC] = {
- *     [IFLA_BRIDGE_FLAGS]
- *     [IFLA_BRIDGE_MODE]
- * }
- */
-enum {
-	IFLA_BRIDGE_FLAGS,
-	IFLA_BRIDGE_MODE,
-	__IFLA_BRIDGE_MAX,
-};
-#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
-
-/* Bridge multicast database attributes
- * [MDBA_MDB] = {
- *     [MDBA_MDB_ENTRY] = {
- *         [MDBA_MDB_ENTRY_INFO]
- *     }
- * }
- * [MDBA_ROUTER] = {
- *    [MDBA_ROUTER_PORT]
- * }
- */
-enum {
-	MDBA_UNSPEC,
-	MDBA_MDB,
-	MDBA_ROUTER,
-	__MDBA_MAX,
-};
-#define MDBA_MAX (__MDBA_MAX - 1)
-
-enum {
-	MDBA_MDB_UNSPEC,
-	MDBA_MDB_ENTRY,
-	__MDBA_MDB_MAX,
-};
-#define MDBA_MDB_MAX (__MDBA_MDB_MAX - 1)
-
-enum {
-	MDBA_MDB_ENTRY_UNSPEC,
-	MDBA_MDB_ENTRY_INFO,
-	__MDBA_MDB_ENTRY_MAX,
-};
-#define MDBA_MDB_ENTRY_MAX (__MDBA_MDB_ENTRY_MAX - 1)
-
-enum {
-	MDBA_ROUTER_UNSPEC,
-	MDBA_ROUTER_PORT,
-	__MDBA_ROUTER_MAX,
-};
-#define MDBA_ROUTER_MAX (__MDBA_ROUTER_MAX - 1)
-
-struct br_port_msg {
-	__u8  family;
-	__u32 ifindex;
-};
-
-struct br_mdb_entry {
-	__u32 ifindex;
-#define MDB_TEMPORARY 0
-#define MDB_PERMANENT 1
-	__u8 state;
-	struct {
-		union {
-			__be32	ip4;
-			struct in6_addr ip6;
-		} u;
-		__be16		proto;
-	} addr;
-};
-
-enum {
-	MDBA_SET_ENTRY_UNSPEC,
-	MDBA_SET_ENTRY,
-	__MDBA_SET_ENTRY_MAX,
-};
-#define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1)
-
-#endif /* _UAPI_LINUX_IF_BRIDGE_H */
diff --git a/include/linux/if_link.h b/include/linux/if_link.h
deleted file mode 100644
index 8b84939..0000000
--- a/include/linux/if_link.h
+++ /dev/null
@@ -1,456 +0,0 @@
-#ifndef _UAPI_LINUX_IF_LINK_H
-#define _UAPI_LINUX_IF_LINK_H
-
-#include <linux/types.h>
-#include <linux/netlink.h>
-
-/* This struct should be in sync with struct rtnl_link_stats64 */
-struct rtnl_link_stats {
-	__u32	rx_packets;		/* total packets received	*/
-	__u32	tx_packets;		/* total packets transmitted	*/
-	__u32	rx_bytes;		/* total bytes received 	*/
-	__u32	tx_bytes;		/* total bytes transmitted	*/
-	__u32	rx_errors;		/* bad packets received		*/
-	__u32	tx_errors;		/* packet transmit problems	*/
-	__u32	rx_dropped;		/* no space in linux buffers	*/
-	__u32	tx_dropped;		/* no space available in linux	*/
-	__u32	multicast;		/* multicast packets received	*/
-	__u32	collisions;
-
-	/* detailed rx_errors: */
-	__u32	rx_length_errors;
-	__u32	rx_over_errors;		/* receiver ring buff overflow	*/
-	__u32	rx_crc_errors;		/* recved pkt with crc error	*/
-	__u32	rx_frame_errors;	/* recv'd frame alignment error */
-	__u32	rx_fifo_errors;		/* recv'r fifo overrun		*/
-	__u32	rx_missed_errors;	/* receiver missed packet	*/
-
-	/* detailed tx_errors */
-	__u32	tx_aborted_errors;
-	__u32	tx_carrier_errors;
-	__u32	tx_fifo_errors;
-	__u32	tx_heartbeat_errors;
-	__u32	tx_window_errors;
-
-	/* for cslip etc */
-	__u32	rx_compressed;
-	__u32	tx_compressed;
-};
-
-/* The main device statistics structure */
-struct rtnl_link_stats64 {
-	__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;		/* no space in linux buffers	*/
-	__u64	tx_dropped;		/* no space available in linux	*/
-	__u64	multicast;		/* multicast packets received	*/
-	__u64	collisions;
-
-	/* detailed rx_errors: */
-	__u64	rx_length_errors;
-	__u64	rx_over_errors;		/* receiver ring buff overflow	*/
-	__u64	rx_crc_errors;		/* recved pkt with crc error	*/
-	__u64	rx_frame_errors;	/* recv'd frame alignment error */
-	__u64	rx_fifo_errors;		/* recv'r fifo overrun		*/
-	__u64	rx_missed_errors;	/* receiver missed packet	*/
-
-	/* detailed tx_errors */
-	__u64	tx_aborted_errors;
-	__u64	tx_carrier_errors;
-	__u64	tx_fifo_errors;
-	__u64	tx_heartbeat_errors;
-	__u64	tx_window_errors;
-
-	/* for cslip etc */
-	__u64	rx_compressed;
-	__u64	tx_compressed;
-};
-
-/* The struct should be in sync with struct ifmap */
-struct rtnl_link_ifmap {
-	__u64	mem_start;
-	__u64	mem_end;
-	__u64	base_addr;
-	__u16	irq;
-	__u8	dma;
-	__u8	port;
-};
-
-/*
- * IFLA_AF_SPEC
- *   Contains nested attributes for address family specific attributes.
- *   Each address family may create a attribute with the address family
- *   number as type and create its own attribute structure in it.
- *
- *   Example:
- *   [IFLA_AF_SPEC] = {
- *       [AF_INET] = {
- *           [IFLA_INET_CONF] = ...,
- *       },
- *       [AF_INET6] = {
- *           [IFLA_INET6_FLAGS] = ...,
- *           [IFLA_INET6_CONF] = ...,
- *       }
- *   }
- */
-
-enum {
-	IFLA_UNSPEC,
-	IFLA_ADDRESS,
-	IFLA_BROADCAST,
-	IFLA_IFNAME,
-	IFLA_MTU,
-	IFLA_LINK,
-	IFLA_QDISC,
-	IFLA_STATS,
-	IFLA_COST,
-#define IFLA_COST IFLA_COST
-	IFLA_PRIORITY,
-#define IFLA_PRIORITY IFLA_PRIORITY
-	IFLA_MASTER,
-#define IFLA_MASTER IFLA_MASTER
-	IFLA_WIRELESS,		/* Wireless Extension event - see wireless.h */
-#define IFLA_WIRELESS IFLA_WIRELESS
-	IFLA_PROTINFO,		/* Protocol specific information for a link */
-#define IFLA_PROTINFO IFLA_PROTINFO
-	IFLA_TXQLEN,
-#define IFLA_TXQLEN IFLA_TXQLEN
-	IFLA_MAP,
-#define IFLA_MAP IFLA_MAP
-	IFLA_WEIGHT,
-#define IFLA_WEIGHT IFLA_WEIGHT
-	IFLA_OPERSTATE,
-	IFLA_LINKMODE,
-	IFLA_LINKINFO,
-#define IFLA_LINKINFO IFLA_LINKINFO
-	IFLA_NET_NS_PID,
-	IFLA_IFALIAS,
-	IFLA_NUM_VF,		/* Number of VFs if device is SR-IOV PF */
-	IFLA_VFINFO_LIST,
-	IFLA_STATS64,
-	IFLA_VF_PORTS,
-	IFLA_PORT_SELF,
-	IFLA_AF_SPEC,
-	IFLA_GROUP,		/* Group the device belongs to */
-	IFLA_NET_NS_FD,
-	IFLA_EXT_MASK,		/* Extended info mask, VFs, etc */
-	IFLA_PROMISCUITY,	/* Promiscuity count: > 0 means acts PROMISC */
-#define IFLA_PROMISCUITY IFLA_PROMISCUITY
-	IFLA_NUM_TX_QUEUES,
-	IFLA_NUM_RX_QUEUES,
-	IFLA_CARRIER,
-	IFLA_PHYS_PORT_ID,
-	__IFLA_MAX
-};
-
-
-#define IFLA_MAX (__IFLA_MAX - 1)
-
-enum {
-	IFLA_INET_UNSPEC,
-	IFLA_INET_CONF,
-	__IFLA_INET_MAX,
-};
-
-#define IFLA_INET_MAX (__IFLA_INET_MAX - 1)
-
-/* ifi_flags.
-
-   IFF_* flags.
-
-   The only change is:
-   IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are
-   more not changeable by user. They describe link media
-   characteristics and set by device driver.
-
-   Comments:
-   - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid
-   - If neither of these three flags are set;
-     the interface is NBMA.
-
-   - IFF_MULTICAST does not mean anything special:
-   multicasts can be used on all not-NBMA links.
-   IFF_MULTICAST means that this media uses special encapsulation
-   for multicast frames. Apparently, all IFF_POINTOPOINT and
-   IFF_BROADCAST devices are able to use multicasts too.
- */
-
-/* IFLA_LINK.
-   For usual devices it is equal ifi_index.
-   If it is a "virtual interface" (f.e. tunnel), ifi_link
-   can point to real physical interface (f.e. for bandwidth calculations),
-   or maybe 0, what means, that real media is unknown (usual
-   for IPIP tunnels, when route to endpoint is allowed to change)
- */
-
-/* Subtype attributes for IFLA_PROTINFO */
-enum {
-	IFLA_INET6_UNSPEC,
-	IFLA_INET6_FLAGS,	/* link flags			*/
-	IFLA_INET6_CONF,	/* sysctl parameters		*/
-	IFLA_INET6_STATS,	/* statistics			*/
-	IFLA_INET6_MCAST,	/* MC things. What of them?	*/
-	IFLA_INET6_CACHEINFO,	/* time values and max reasm size */
-	IFLA_INET6_ICMP6STATS,	/* statistics (icmpv6)		*/
-	__IFLA_INET6_MAX
-};
-
-#define IFLA_INET6_MAX	(__IFLA_INET6_MAX - 1)
-
-enum {
-	BRIDGE_MODE_UNSPEC,
-	BRIDGE_MODE_HAIRPIN,
-};
-
-enum {
-	IFLA_BRPORT_UNSPEC,
-	IFLA_BRPORT_STATE,	/* Spanning tree state     */
-	IFLA_BRPORT_PRIORITY,	/* "             priority  */
-	IFLA_BRPORT_COST,	/* "             cost      */
-	IFLA_BRPORT_MODE,	/* mode (hairpin)          */
-	IFLA_BRPORT_GUARD,	/* bpdu guard              */
-	IFLA_BRPORT_PROTECT,	/* root port protection    */
-	IFLA_BRPORT_FAST_LEAVE,	/* multicast fast leave    */
-	__IFLA_BRPORT_MAX
-};
-#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
-
-struct ifla_cacheinfo {
-	__u32	max_reasm_len;
-	__u32	tstamp;		/* ipv6InterfaceTable updated timestamp */
-	__u32	reachable_time;
-	__u32	retrans_time;
-};
-
-enum {
-	IFLA_INFO_UNSPEC,
-	IFLA_INFO_KIND,
-	IFLA_INFO_DATA,
-	IFLA_INFO_XSTATS,
-	__IFLA_INFO_MAX,
-};
-
-#define IFLA_INFO_MAX	(__IFLA_INFO_MAX - 1)
-
-/* VLAN section */
-
-enum {
-	IFLA_VLAN_UNSPEC,
-	IFLA_VLAN_ID,
-	IFLA_VLAN_FLAGS,
-	IFLA_VLAN_EGRESS_QOS,
-	IFLA_VLAN_INGRESS_QOS,
-	IFLA_VLAN_PROTOCOL,
-	__IFLA_VLAN_MAX,
-};
-
-#define IFLA_VLAN_MAX	(__IFLA_VLAN_MAX - 1)
-
-struct ifla_vlan_flags {
-	__u32	flags;
-	__u32	mask;
-};
-
-enum {
-	IFLA_VLAN_QOS_UNSPEC,
-	IFLA_VLAN_QOS_MAPPING,
-	__IFLA_VLAN_QOS_MAX
-};
-
-#define IFLA_VLAN_QOS_MAX	(__IFLA_VLAN_QOS_MAX - 1)
-
-struct ifla_vlan_qos_mapping {
-	__u32 from;
-	__u32 to;
-};
-
-/* MACVLAN section */
-enum {
-	IFLA_MACVLAN_UNSPEC,
-	IFLA_MACVLAN_MODE,
-	IFLA_MACVLAN_FLAGS,
-	__IFLA_MACVLAN_MAX,
-};
-
-#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1)
-
-enum macvlan_mode {
-	MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */
-	MACVLAN_MODE_VEPA    = 2, /* talk to other ports through ext bridge */
-	MACVLAN_MODE_BRIDGE  = 4, /* talk to bridge ports directly */
-	MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */
-};
-
-#define MACVLAN_FLAG_NOPROMISC	1
-
-/* VXLAN section */
-enum {
-	IFLA_VXLAN_UNSPEC,
-	IFLA_VXLAN_ID,
-	IFLA_VXLAN_GROUP,
-	IFLA_VXLAN_LINK,
-	IFLA_VXLAN_LOCAL,
-	IFLA_VXLAN_TTL,
-	IFLA_VXLAN_TOS,
-	IFLA_VXLAN_LEARNING,
-	IFLA_VXLAN_AGEING,
-	IFLA_VXLAN_LIMIT,
-	IFLA_VXLAN_PORT_RANGE,
-	IFLA_VXLAN_PROXY,
-	IFLA_VXLAN_RSC,
-	IFLA_VXLAN_L2MISS,
-	IFLA_VXLAN_L3MISS,
-	__IFLA_VXLAN_MAX
-};
-#define IFLA_VXLAN_MAX	(__IFLA_VXLAN_MAX - 1)
-
-struct ifla_vxlan_port_range {
-	__be16	low;
-	__be16	high;
-};
-
-enum {
-	VETH_INFO_UNSPEC,
-	VETH_INFO_PEER,
-
-	__VETH_INFO_MAX
-#define VETH_INFO_MAX   (__VETH_INFO_MAX - 1)
-};
-
-/* SR-IOV virtual function management section */
-
-enum {
-	IFLA_VF_INFO_UNSPEC,
-	IFLA_VF_INFO,
-	__IFLA_VF_INFO_MAX,
-};
-
-#define IFLA_VF_INFO_MAX (__IFLA_VF_INFO_MAX - 1)
-
-enum {
-	IFLA_VF_UNSPEC,
-	IFLA_VF_MAC,		/* Hardware queue specific attributes */
-	IFLA_VF_VLAN,
-	IFLA_VF_TX_RATE,	/* TX Bandwidth Allocation */
-	IFLA_VF_SPOOFCHK,	/* Spoof Checking on/off switch */
-	__IFLA_VF_MAX,
-};
-
-#define IFLA_VF_MAX (__IFLA_VF_MAX - 1)
-
-struct ifla_vf_mac {
-	__u32 vf;
-	__u8 mac[32]; /* MAX_ADDR_LEN */
-};
-
-struct ifla_vf_vlan {
-	__u32 vf;
-	__u32 vlan; /* 0 - 4095, 0 disables VLAN filter */
-	__u32 qos;
-};
-
-struct ifla_vf_tx_rate {
-	__u32 vf;
-	__u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */
-};
-
-struct ifla_vf_spoofchk {
-	__u32 vf;
-	__u32 setting;
-};
-
-/* VF ports management section
- *
- *	Nested layout of set/get msg is:
- *
- *		[IFLA_NUM_VF]
- *		[IFLA_VF_PORTS]
- *			[IFLA_VF_PORT]
- *				[IFLA_PORT_*], ...
- *			[IFLA_VF_PORT]
- *				[IFLA_PORT_*], ...
- *			...
- *		[IFLA_PORT_SELF]
- *			[IFLA_PORT_*], ...
- */
-
-enum {
-	IFLA_VF_PORT_UNSPEC,
-	IFLA_VF_PORT,			/* nest */
-	__IFLA_VF_PORT_MAX,
-};
-
-#define IFLA_VF_PORT_MAX (__IFLA_VF_PORT_MAX - 1)
-
-enum {
-	IFLA_PORT_UNSPEC,
-	IFLA_PORT_VF,			/* __u32 */
-	IFLA_PORT_PROFILE,		/* string */
-	IFLA_PORT_VSI_TYPE,		/* 802.1Qbg (pre-)standard VDP */
-	IFLA_PORT_INSTANCE_UUID,	/* binary UUID */
-	IFLA_PORT_HOST_UUID,		/* binary UUID */
-	IFLA_PORT_REQUEST,		/* __u8 */
-	IFLA_PORT_RESPONSE,		/* __u16, output only */
-	__IFLA_PORT_MAX,
-};
-
-#define IFLA_PORT_MAX (__IFLA_PORT_MAX - 1)
-
-#define PORT_PROFILE_MAX	40
-#define PORT_UUID_MAX		16
-#define PORT_SELF_VF		-1
-
-enum {
-	PORT_REQUEST_PREASSOCIATE = 0,
-	PORT_REQUEST_PREASSOCIATE_RR,
-	PORT_REQUEST_ASSOCIATE,
-	PORT_REQUEST_DISASSOCIATE,
-};
-
-enum {
-	PORT_VDP_RESPONSE_SUCCESS = 0,
-	PORT_VDP_RESPONSE_INVALID_FORMAT,
-	PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES,
-	PORT_VDP_RESPONSE_UNUSED_VTID,
-	PORT_VDP_RESPONSE_VTID_VIOLATION,
-	PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION,
-	PORT_VDP_RESPONSE_OUT_OF_SYNC,
-	/* 0x08-0xFF reserved for future VDP use */
-	PORT_PROFILE_RESPONSE_SUCCESS = 0x100,
-	PORT_PROFILE_RESPONSE_INPROGRESS,
-	PORT_PROFILE_RESPONSE_INVALID,
-	PORT_PROFILE_RESPONSE_BADSTATE,
-	PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES,
-	PORT_PROFILE_RESPONSE_ERROR,
-};
-
-struct ifla_port_vsi {
-	__u8 vsi_mgr_id;
-	__u8 vsi_type_id[3];
-	__u8 vsi_type_version;
-	__u8 pad[3];
-};
-
-
-/* IPoIB section */
-
-enum {
-	IFLA_IPOIB_UNSPEC,
-	IFLA_IPOIB_PKEY,
-	IFLA_IPOIB_MODE,
-	IFLA_IPOIB_UMCAST,
-	__IFLA_IPOIB_MAX
-};
-
-enum {
-	IPOIB_MODE_DATAGRAM  = 0, /* using unreliable datagram QPs */
-	IPOIB_MODE_CONNECTED = 1, /* using connected QPs */
-};
-
-#define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1)
-
-#endif /* _UAPI_LINUX_IF_LINK_H */
diff --git a/include/linux/ip_mp_alg.h b/include/linux/ip_mp_alg.h
deleted file mode 100644
index e234e20..0000000
--- a/include/linux/ip_mp_alg.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* ip_mp_alg.h: IPV4 multipath algorithm support, user-visible values.
- *
- * Copyright (C) 2004, 2005 Einar Lueck <elueck@de.ibm.com>
- * Copyright (C) 2005 David S. Miller <davem@davemloft.net>
- */
-
-#ifndef _LINUX_IP_MP_ALG_H
-#define _LINUX_IP_MP_ALG_H
-
-enum ip_mp_alg {
-	IP_MP_ALG_NONE,
-	IP_MP_ALG_RR,
-	IP_MP_ALG_DRR,
-	IP_MP_ALG_RANDOM,
-	IP_MP_ALG_WRANDOM,
-	__IP_MP_ALG_MAX
-};
-
-#define IP_MP_ALG_MAX (__IP_MP_ALG_MAX - 1)
-
-#endif /* _LINUX_IP_MP_ALG_H */
-
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
deleted file mode 100644
index 7999885..0000000
--- a/include/linux/netfilter.h
+++ /dev/null
@@ -1,57 +0,0 @@
-#ifndef __LINUX_NETFILTER_H
-#define __LINUX_NETFILTER_H
-
-#include <linux/types.h>
-
-/* Responses from hook functions. */
-#define NF_DROP 0
-#define NF_ACCEPT 1
-#define NF_STOLEN 2
-#define NF_QUEUE 3
-#define NF_REPEAT 4
-#define NF_STOP 5
-#define NF_MAX_VERDICT NF_STOP
-
-/* we overload the higher bits for encoding auxiliary data such as the queue
- * number. Not nice, but better than additional function arguments. */
-#define NF_VERDICT_MASK 0x0000ffff
-#define NF_VERDICT_BITS 16
-
-#define NF_VERDICT_QMASK 0xffff0000
-#define NF_VERDICT_QBITS 16
-
-#define NF_QUEUE_NR(x) ((((x) << NF_VERDICT_BITS) & NF_VERDICT_QMASK) | NF_QUEUE)
-
-/* Generic cache responses from hook functions.
-   <= 0x2000 is used for protocol-flags. */
-#define NFC_UNKNOWN 0x4000
-#define NFC_ALTERED 0x8000
-
-enum nf_inet_hooks {
-	NF_INET_PRE_ROUTING,
-	NF_INET_LOCAL_IN,
-	NF_INET_FORWARD,
-	NF_INET_LOCAL_OUT,
-	NF_INET_POST_ROUTING,
-	NF_INET_NUMHOOKS
-};
-
-enum {
-	NFPROTO_UNSPEC =  0,
-	NFPROTO_IPV4   =  2,
-	NFPROTO_ARP    =  3,
-	NFPROTO_BRIDGE =  7,
-	NFPROTO_IPV6   = 10,
-	NFPROTO_DECNET = 12,
-	NFPROTO_NUMPROTO,
-};
-
-union nf_inet_addr {
-	__u32		all[4];
-	__be32		ip;
-	__be32		ip6[4];
-	struct in_addr	in;
-	struct in6_addr	in6;
-};
-
-#endif /*__LINUX_NETFILTER_H*/
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
deleted file mode 100644
index 3925254..0000000
--- a/include/linux/netlink.h
+++ /dev/null
@@ -1,149 +0,0 @@
-#ifndef __LINUX_NETLINK_H
-#define __LINUX_NETLINK_H
-
-#include <linux/socket.h> /* for sa_family_t */
-#include <linux/types.h>
-
-#define NETLINK_ROUTE		0	/* Routing/device hook				*/
-#define NETLINK_UNUSED		1	/* Unused number				*/
-#define NETLINK_USERSOCK	2	/* Reserved for user mode socket protocols 	*/
-#define NETLINK_FIREWALL	3	/* Firewalling hook				*/
-#define NETLINK_INET_DIAG	4	/* INET socket monitoring			*/
-#define NETLINK_NFLOG		5	/* netfilter/iptables ULOG */
-#define NETLINK_XFRM		6	/* ipsec */
-#define NETLINK_SELINUX		7	/* SELinux event notifications */
-#define NETLINK_ISCSI		8	/* Open-iSCSI */
-#define NETLINK_AUDIT		9	/* auditing */
-#define NETLINK_FIB_LOOKUP	10	
-#define NETLINK_CONNECTOR	11
-#define NETLINK_NETFILTER	12	/* netfilter subsystem */
-#define NETLINK_IP6_FW		13
-#define NETLINK_DNRTMSG		14	/* DECnet routing messages */
-#define NETLINK_KOBJECT_UEVENT	15	/* Kernel messages to userspace */
-#define NETLINK_GENERIC		16
-/* leave room for NETLINK_DM (DM Events) */
-#define NETLINK_SCSITRANSPORT	18	/* SCSI Transports */
-#define NETLINK_ECRYPTFS	19
-#define NETLINK_RDMA		20
-
-#define MAX_LINKS 32		
-
-struct sockaddr_nl {
-	sa_family_t	nl_family;	/* AF_NETLINK	*/
-	unsigned short	nl_pad;		/* zero		*/
-	__u32		nl_pid;		/* port ID	*/
-       	__u32		nl_groups;	/* multicast groups mask */
-};
-
-struct nlmsghdr {
-	__u32		nlmsg_len;	/* Length of message including header */
-	__u16		nlmsg_type;	/* Message content */
-	__u16		nlmsg_flags;	/* Additional flags */
-	__u32		nlmsg_seq;	/* Sequence number */
-	__u32		nlmsg_pid;	/* Sending process port ID */
-};
-
-/* Flags values */
-
-#define NLM_F_REQUEST		1	/* It is request message. 	*/
-#define NLM_F_MULTI		2	/* Multipart message, terminated by NLMSG_DONE */
-#define NLM_F_ACK		4	/* Reply with ack, with zero or error code */
-#define NLM_F_ECHO		8	/* Echo this request 		*/
-#define NLM_F_DUMP_INTR		16	/* Dump was inconsistent due to sequence change */
-
-/* Modifiers to GET request */
-#define NLM_F_ROOT	0x100	/* specify tree	root	*/
-#define NLM_F_MATCH	0x200	/* return all matching	*/
-#define NLM_F_ATOMIC	0x400	/* atomic GET		*/
-#define NLM_F_DUMP	(NLM_F_ROOT|NLM_F_MATCH)
-
-/* Modifiers to NEW request */
-#define NLM_F_REPLACE	0x100	/* Override existing		*/
-#define NLM_F_EXCL	0x200	/* Do not touch, if it exists	*/
-#define NLM_F_CREATE	0x400	/* Create, if it does not exist	*/
-#define NLM_F_APPEND	0x800	/* Add to end of list		*/
-
-/*
-   4.4BSD ADD		NLM_F_CREATE|NLM_F_EXCL
-   4.4BSD CHANGE	NLM_F_REPLACE
-
-   True CHANGE		NLM_F_CREATE|NLM_F_REPLACE
-   Append		NLM_F_CREATE
-   Check		NLM_F_EXCL
- */
-
-#define NLMSG_ALIGNTO	4U
-#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
-#define NLMSG_HDRLEN	 ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
-#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN))
-#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
-#define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
-#define NLMSG_NEXT(nlh,len)	 ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
-				  (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
-#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
-			   (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
-			   (nlh)->nlmsg_len <= (len))
-#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
-
-#define NLMSG_NOOP		0x1	/* Nothing.		*/
-#define NLMSG_ERROR		0x2	/* Error		*/
-#define NLMSG_DONE		0x3	/* End of a dump	*/
-#define NLMSG_OVERRUN		0x4	/* Data lost		*/
-
-#define NLMSG_MIN_TYPE		0x10	/* < 0x10: reserved control messages */
-
-struct nlmsgerr {
-	int		error;
-	struct nlmsghdr msg;
-};
-
-#define NETLINK_ADD_MEMBERSHIP	1
-#define NETLINK_DROP_MEMBERSHIP	2
-#define NETLINK_PKTINFO		3
-#define NETLINK_BROADCAST_ERROR	4
-#define NETLINK_NO_ENOBUFS	5
-
-struct nl_pktinfo {
-	__u32	group;
-};
-
-#define NET_MAJOR 36		/* Major 36 is reserved for networking 						*/
-
-enum {
-	NETLINK_UNCONNECTED = 0,
-	NETLINK_CONNECTED,
-};
-
-/*
- *  <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
- * +---------------------+- - -+- - - - - - - - - -+- - -+
- * |        Header       | Pad |     Payload       | Pad |
- * |   (struct nlattr)   | ing |                   | ing |
- * +---------------------+- - -+- - - - - - - - - -+- - -+
- *  <-------------- nlattr->nla_len -------------->
- */
-
-struct nlattr {
-	__u16           nla_len;
-	__u16           nla_type;
-};
-
-/*
- * nla_type (16 bits)
- * +---+---+-------------------------------+
- * | N | O | Attribute Type                |
- * +---+---+-------------------------------+
- * N := Carries nested attributes
- * O := Payload stored in network byte order
- *
- * Note: The N and O flag are mutually exclusive.
- */
-#define NLA_F_NESTED		(1 << 15)
-#define NLA_F_NET_BYTEORDER	(1 << 14)
-#define NLA_TYPE_MASK		~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
-
-#define NLA_ALIGNTO		4
-#define NLA_ALIGN(len)		(((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
-#define NLA_HDRLEN		((int) NLA_ALIGN(sizeof(struct nlattr)))
-
-#endif	/* __LINUX_NETLINK_H */
diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h
deleted file mode 100644
index defbde2..0000000
--- a/include/linux/pkt_cls.h
+++ /dev/null
@@ -1,467 +0,0 @@
-#ifndef __LINUX_PKT_CLS_H
-#define __LINUX_PKT_CLS_H
-
-#include <linux/types.h>
-#include <linux/pkt_sched.h>
-
-/* I think i could have done better macros ; for now this is stolen from
- * some arch/mips code - jhs
-*/
-#define _TC_MAKE32(x) ((x))
-
-#define _TC_MAKEMASK1(n) (_TC_MAKE32(1) << _TC_MAKE32(n))
-#define _TC_MAKEMASK(v,n) (_TC_MAKE32((_TC_MAKE32(1)<<(v))-1) << _TC_MAKE32(n))
-#define _TC_MAKEVALUE(v,n) (_TC_MAKE32(v) << _TC_MAKE32(n))
-#define _TC_GETVALUE(v,n,m) ((_TC_MAKE32(v) & _TC_MAKE32(m)) >> _TC_MAKE32(n))
-
-/* verdict bit breakdown 
- *
-bit 0: when set -> this packet has been munged already
-
-bit 1: when set -> It is ok to munge this packet
-
-bit 2,3,4,5: Reclassify counter - sort of reverse TTL - if exceeded
-assume loop
-
-bit 6,7: Where this packet was last seen 
-0: Above the transmit example at the socket level
-1: on the Ingress
-2: on the Egress
-
-bit 8: when set --> Request not to classify on ingress. 
-
-bits 9,10,11: redirect counter -  redirect TTL. Loop avoidance
-
- *
- * */
-
-#define TC_MUNGED          _TC_MAKEMASK1(0)
-#define SET_TC_MUNGED(v)   ( TC_MUNGED | (v & ~TC_MUNGED))
-#define CLR_TC_MUNGED(v)   ( v & ~TC_MUNGED)
-
-#define TC_OK2MUNGE        _TC_MAKEMASK1(1)
-#define SET_TC_OK2MUNGE(v)   ( TC_OK2MUNGE | (v & ~TC_OK2MUNGE))
-#define CLR_TC_OK2MUNGE(v)   ( v & ~TC_OK2MUNGE)
-
-#define S_TC_VERD          _TC_MAKE32(2)
-#define M_TC_VERD          _TC_MAKEMASK(4,S_TC_VERD)
-#define G_TC_VERD(x)       _TC_GETVALUE(x,S_TC_VERD,M_TC_VERD)
-#define V_TC_VERD(x)       _TC_MAKEVALUE(x,S_TC_VERD)
-#define SET_TC_VERD(v,n)   ((V_TC_VERD(n)) | (v & ~M_TC_VERD))
-
-#define S_TC_FROM          _TC_MAKE32(6)
-#define M_TC_FROM          _TC_MAKEMASK(2,S_TC_FROM)
-#define G_TC_FROM(x)       _TC_GETVALUE(x,S_TC_FROM,M_TC_FROM)
-#define V_TC_FROM(x)       _TC_MAKEVALUE(x,S_TC_FROM)
-#define SET_TC_FROM(v,n)   ((V_TC_FROM(n)) | (v & ~M_TC_FROM))
-#define AT_STACK	0x0
-#define AT_INGRESS	0x1
-#define AT_EGRESS	0x2
-
-#define TC_NCLS          _TC_MAKEMASK1(8)
-#define SET_TC_NCLS(v)   ( TC_NCLS | (v & ~TC_NCLS))
-#define CLR_TC_NCLS(v)   ( v & ~TC_NCLS)
-
-#define S_TC_RTTL          _TC_MAKE32(9)
-#define M_TC_RTTL          _TC_MAKEMASK(3,S_TC_RTTL)
-#define G_TC_RTTL(x)       _TC_GETVALUE(x,S_TC_RTTL,M_TC_RTTL)
-#define V_TC_RTTL(x)       _TC_MAKEVALUE(x,S_TC_RTTL)
-#define SET_TC_RTTL(v,n)   ((V_TC_RTTL(n)) | (v & ~M_TC_RTTL))
-
-#define S_TC_AT          _TC_MAKE32(12)
-#define M_TC_AT          _TC_MAKEMASK(2,S_TC_AT)
-#define G_TC_AT(x)       _TC_GETVALUE(x,S_TC_AT,M_TC_AT)
-#define V_TC_AT(x)       _TC_MAKEVALUE(x,S_TC_AT)
-#define SET_TC_AT(v,n)   ((V_TC_AT(n)) | (v & ~M_TC_AT))
-
-/* Action attributes */
-enum {
-	TCA_ACT_UNSPEC,
-	TCA_ACT_KIND,
-	TCA_ACT_OPTIONS,
-	TCA_ACT_INDEX,
-	TCA_ACT_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 MAX_REC_LOOP 4
-#define MAX_RED_LOOP 4
-
-#define TC_ACT_UNSPEC	(-1)
-#define TC_ACT_OK		0
-#define TC_ACT_RECLASSIFY	1
-#define TC_ACT_SHOT		2
-#define TC_ACT_PIPE		3
-#define TC_ACT_STOLEN		4
-#define TC_ACT_QUEUED		5
-#define TC_ACT_REPEAT		6
-#define TC_ACT_JUMP		0x10000000
-
-/* Action type identifiers*/
-enum {
-	TCA_ID_UNSPEC=0,
-	TCA_ID_POLICE=1,
-	/* other actions go here */
-	__TCA_ID_MAX=255
-};
-
-#define TCA_ID_MAX __TCA_ID_MAX
-
-struct tc_police {
-	__u32			index;
-	int			action;
-#define TC_POLICE_UNSPEC	TC_ACT_UNSPEC
-#define TC_POLICE_OK		TC_ACT_OK
-#define TC_POLICE_RECLASSIFY	TC_ACT_RECLASSIFY
-#define TC_POLICE_SHOT		TC_ACT_SHOT
-#define TC_POLICE_PIPE		TC_ACT_PIPE
-
-	__u32			limit;
-	__u32			burst;
-	__u32			mtu;
-	struct tc_ratespec	rate;
-	struct tc_ratespec	peakrate;
-	int 			refcnt;
-	int 			bindcnt;
-	__u32			capab;
-};
-
-struct tcf_t {
-	__u64   install;
-	__u64   lastuse;
-	__u64   expires;
-};
-
-struct tc_cnt {
-	int                   refcnt; 
-	int                   bindcnt;
-};
-
-#define tc_gen \
-	__u32                 index; \
-	__u32                 capab; \
-	int                   action; \
-	int                   refcnt; \
-	int                   bindcnt
-
-enum {
-	TCA_POLICE_UNSPEC,
-	TCA_POLICE_TBF,
-	TCA_POLICE_RATE,
-	TCA_POLICE_PEAKRATE,
-	TCA_POLICE_AVRATE,
-	TCA_POLICE_RESULT,
-	__TCA_POLICE_MAX
-#define TCA_POLICE_RESULT TCA_POLICE_RESULT
-};
-
-#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1)
-
-/* U32 filters */
-
-#define TC_U32_HTID(h) ((h)&0xFFF00000)
-#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20)
-#define TC_U32_HASH(h) (((h)>>12)&0xFF)
-#define TC_U32_NODE(h) ((h)&0xFFF)
-#define TC_U32_KEY(h) ((h)&0xFFFFF)
-#define TC_U32_UNSPEC	0
-#define TC_U32_ROOT	(0xFFF00000)
-
-enum {
-	TCA_U32_UNSPEC,
-	TCA_U32_CLASSID,
-	TCA_U32_HASH,
-	TCA_U32_LINK,
-	TCA_U32_DIVISOR,
-	TCA_U32_SEL,
-	TCA_U32_POLICE,
-	TCA_U32_ACT,   
-	TCA_U32_INDEV,
-	TCA_U32_PCNT,
-	TCA_U32_MARK,
-	__TCA_U32_MAX
-};
-
-#define TCA_U32_MAX (__TCA_U32_MAX - 1)
-
-struct tc_u32_key {
-	__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)
-
-/* 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_MAX		6
-
-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
deleted file mode 100644
index a0837a0..0000000
--- a/include/linux/pkt_sched.h
+++ /dev/null
@@ -1,642 +0,0 @@
-#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 enqueues bytes */
-	__u32	packets;		/* Number of enqueued packets	*/
-	__u32	drops;			/* Packets dropped because of lack of resources */
-	__u32	overlimits;		/* Number of throttle events when this
-					 * flow goes out of allocated bandwidth */
-	__u32	bps;			/* Current flow byte rate */
-	__u32	pps;			/* Current flow packet rate */
-	__u32	qlen;
-	__u32	backlog;
-};
-
-struct tc_estimator {
-	signed char	interval;
-	unsigned char	ewma_log;
-};
-
-/* "Handles"
-   ---------
-
-    All the traffic control objects have 32bit identifiers, or "handles".
-
-    They can be considered as opaque numbers from user API viewpoint,
-    but actually they always consist of two fields: major and
-    minor numbers, which are interpreted by kernel specially,
-    that may be used by applications, though not recommended.
-
-    F.e. qdisc handles always have minor number equal to zero,
-    classes (or flows) have major equal to parent qdisc major, and
-    minor uniquely identifying class inside qdisc.
-
-    Macros to manipulate handles:
- */
-
-#define TC_H_MAJ_MASK (0xFFFF0000U)
-#define TC_H_MIN_MASK (0x0000FFFFU)
-#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK)
-#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK)
-#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK))
-
-#define TC_H_UNSPEC	(0U)
-#define TC_H_ROOT	(0xFFFFFFFFU)
-#define TC_H_INGRESS    (0xFFFFFFF1U)
-
-struct tc_ratespec {
-	unsigned char	cell_log;
-	unsigned char	__reserved;
-	unsigned short	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_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_sfq_xstats {
-	__s32		allot;
-};
-
-/*
- *  NOTE: limit, divisor and flows are hardwired to code at the moment.
- *
- *	limit=flows=128, divisor=1024;
- *
- *	The only reason for this is efficiency, it is possible
- *	to change these parameters in compile time.
- */
-
-/* RED section */
-
-enum {
-	TCA_RED_UNSPEC,
-	TCA_RED_PARMS,
-	TCA_RED_STAB,
-	__TCA_RED_MAX,
-};
-
-#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
-};
-
-struct tc_red_xstats {
-	__u32           early;          /* Early drops */
-	__u32           pdrop;          /* Drops due to queue limits */
-	__u32           other;          /* Drops due to drop() calls */
-	__u32           marked;         /* Marked packets */
-};
-
-/* GRED section */
-
-#define MAX_DPs 16
-
-enum {
-       TCA_GRED_UNSPEC,
-       TCA_GRED_PARMS,
-       TCA_GRED_STAB,
-       TCA_GRED_DPS,
-	   __TCA_GRED_MAX,
-};
-
-#define TCA_GRED_MAX (__TCA_GRED_MAX - 1)
-
-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,
-};
-
-#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 shapped packets */
-};
-enum {
-	TCA_HTB_UNSPEC,
-	TCA_HTB_PARMS,
-	TCA_HTB_INIT,
-	TCA_HTB_CTAB,
-	TCA_HTB_RTAB,
-	__TCA_HTB_MAX,
-};
-
-#define TCA_HTB_MAX (__TCA_HTB_MAX - 1)
-
-struct tc_htb_xstats {
-	__u32 lends;
-	__u32 borrows;
-	__u32 giants;	/* too big packets (rate will not be accurate) */
-	__u32 tokens;
-	__u32 ctokens;
-};
-
-/* HFSC section */
-
-struct tc_hfsc_qopt {
-	__u16	defcls;		/* default class */
-};
-
-struct tc_service_curve {
-	__u32	m1;		/* slope of the first segment in bps */
-	__u32	d;		/* x-projection of the first segment in us */
-	__u32	m2;		/* slope of the second segment in bps */
-};
-
-struct tc_hfsc_stats {
-	__u64	work;		/* total work done */
-	__u64	rtwork;		/* work done by real-time criteria */
-	__u32	period;		/* current period */
-	__u32	level;		/* class level in hierarchy */
-};
-
-enum {
-	TCA_HFSC_UNSPEC,
-	TCA_HFSC_RSC,
-	TCA_HFSC_FSC,
-	TCA_HFSC_USC,
-	__TCA_HFSC_MAX,
-};
-
-#define TCA_HFSC_MAX (__TCA_HFSC_MAX - 1)
-
-
-/* CBQ section */
-
-#define TC_CBQ_MAXPRIO		8
-#define TC_CBQ_MAXLEVEL		8
-#define TC_CBQ_DEF_EWMA		5
-
-struct tc_cbq_lssopt {
-	unsigned char	change;
-	unsigned char	flags;
-#define TCF_CBQ_LSS_BOUNDED	1
-#define TCF_CBQ_LSS_ISOLATED	2
-	unsigned char  	ewma_log;
-	unsigned char  	level;
-#define TCF_CBQ_LSS_FLAGS	1
-#define TCF_CBQ_LSS_EWMA	2
-#define TCF_CBQ_LSS_MAXIDLE	4
-#define TCF_CBQ_LSS_MINIDLE	8
-#define TCF_CBQ_LSS_OFFTIME	0x10
-#define TCF_CBQ_LSS_AVPKT	0x20
-	__u32		maxidle;
-	__u32		minidle;
-	__u32		offtime;
-	__u32		avpkt;
-};
-
-struct tc_cbq_wrropt {
-	unsigned char	flags;
-	unsigned char	priority;
-	unsigned char	cpriority;
-	unsigned char	__reserved;
-	__u32		allot;
-	__u32		weight;
-};
-
-struct tc_cbq_ovl {
-	unsigned char	strategy;
-#define	TC_CBQ_OVL_CLASSIC	0
-#define	TC_CBQ_OVL_DELAY	1
-#define	TC_CBQ_OVL_LOWPRIO	2
-#define	TC_CBQ_OVL_DROP		3
-#define	TC_CBQ_OVL_RCLASSIC	4
-	unsigned char	priority2;
-	__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)
-
-/* fq_codel section */
-
-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_MAX
-};
-
-#define TCA_FQ_CODEL_MAX        (__TCA_FQ_CODEL_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_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;
-};
-
-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 probablities 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;
-};
-
-#endif
diff --git a/include/netlink-private/cache-api.h b/include/netlink-private/cache-api.h
index f3d9f01..c684e79 100644
--- a/include/netlink-private/cache-api.h
+++ b/include/netlink-private/cache-api.h
@@ -237,7 +237,8 @@
 	 * @see nl_cache_include()
 	 */
 	int   (*co_include_event)(struct nl_cache *cache, struct nl_object *obj,
-				  change_func_t change_cb, void *data);
+				  change_func_t change_cb, change_func_v2_t change_cb_v2,
+				  void *data);
 
 	void (*reserved_1)(void);
 	void (*reserved_2)(void);
diff --git a/include/netlink-private/netlink.h b/include/netlink-private/netlink.h
index e366d1e..5f6e3f7 100644
--- a/include/netlink-private/netlink.h
+++ b/include/netlink-private/netlink.h
@@ -18,7 +18,6 @@
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <math.h>
 #include <time.h>
 #include <stdarg.h>
 #include <ctype.h>
@@ -49,11 +48,11 @@
 #include <linux/pkt_sched.h>
 #include <linux/pkt_cls.h>
 #include <linux/gen_stats.h>
-#include <linux/ip_mp_alg.h>
 #include <linux/atm.h>
 #include <linux/ip.h>
 #include <linux/ipv6.h>
 #include <linux/snmp.h>
+#include <linux/xfrm.h>
 
 #ifndef DISABLE_PTHREADS
 #include <pthread.h>
@@ -70,11 +69,11 @@
 #define NSEC_PER_SEC	1000000000L
 
 struct trans_tbl {
-	int i;
+	uint64_t i;
 	const char *a;
 };
 
-#define __ADD(id, name) { .i = id, .a = #name },
+#define __ADD(id, name) { .i = id, .a = #name }
 
 struct trans_list {
 	int i;
@@ -85,11 +84,14 @@
 #ifdef NL_DEBUG
 #define NL_DBG(LVL,FMT,ARG...)						\
 	do {								\
-		if (LVL <= nl_debug)					\
+		if (LVL <= nl_debug) {					\
+			int _errsv = errno;				\
 			fprintf(stderr,					\
 				"DBG<" #LVL ">%20s:%-4u %s: " FMT,	\
 				__FILE__, __LINE__,			\
-				__PRETTY_FUNCTION__, ##ARG);		\
+				__func__, ##ARG);			\
+			errno = _errsv;					\
+		}							\
 	} while (0)
 #else /* NL_DEBUG */
 #define NL_DBG(LVL,FMT,ARG...) do { } while(0)
@@ -98,7 +100,7 @@
 #define BUG()                            				\
 	do {                                 				\
 		fprintf(stderr, "BUG at file position %s:%d:%s\n",  	\
-			__FILE__, __LINE__, __PRETTY_FUNCTION__); 	\
+			__FILE__, __LINE__, __func__); 			\
 		assert(0);						\
 	} while (0)
 
@@ -112,7 +114,7 @@
 #define APPBUG(msg)							\
 	do {								\
 		fprintf(stderr, "APPLICATION BUG: %s:%d:%s: %s\n",	\
-			__FILE__, __LINE__, __PRETTY_FUNCTION__, msg);	\
+			__FILE__, __LINE__, __func__, msg);		\
 		assert(0);						\
 	} while(0)
 
@@ -132,8 +134,9 @@
 extern int __str2flags(const char *, const struct trans_tbl *, size_t);
 
 extern void dump_from_ops(struct nl_object *, struct nl_dump_params *);
+extern struct rtnl_link *link_lookup(struct nl_cache *cache, int ifindex);
 
-static inline int nl_cb_call(struct nl_cb *cb, int type, struct nl_msg *msg)
+static inline int nl_cb_call(struct nl_cb *cb, enum nl_cb_type type, struct nl_msg *msg)
 {
 	int ret;
 
@@ -156,14 +159,14 @@
 #define __deprecated __attribute__ ((deprecated))
 
 #define min(x,y) ({ \
-	typeof(x) _x = (x);	\
-	typeof(y) _y = (y);	\
+	__typeof__(x) _x = (x);	\
+	__typeof__(y) _y = (y);	\
 	(void) (&_x == &_y);		\
 	_x < _y ? _x : _y; })
 
 #define max(x,y) ({ \
-	typeof(x) _x = (x);	\
-	typeof(y) _y = (y);	\
+	__typeof__(x) _x = (x);	\
+	__typeof__(y) _y = (y);	\
 	(void) (&_x == &_y);		\
 	_x > _y ? _x : _y; })
 
@@ -183,7 +186,7 @@
 	dst->rs_overhead = src->overhead;
 	dst->rs_cell_align = src->cell_align;
 	dst->rs_mpu = src->mpu;
-	dst->rs_rate = src->rate;
+	dst->rs_rate64 = src->rate;
 }
 
 static inline void rtnl_rcopy_ratespec(struct tc_ratespec *dst,
@@ -193,10 +196,10 @@
 	dst->overhead = src->rs_overhead;
 	dst->cell_align = src->rs_cell_align;
 	dst->mpu = src->rs_mpu;
-	dst->rate = src->rs_rate;
+	dst->rate = src->rs_rate64 > 0xFFFFFFFFull ? 0xFFFFFFFFull : (uint32_t) src->rs_rate64;
 }
 
-static inline char *nl_cache_name(struct nl_cache *cache)
+static inline const char *nl_cache_name(struct nl_cache *cache)
 {
 	return cache->c_ops ? cache->c_ops->co_name : "unknown";
 }
@@ -273,4 +276,14 @@
 #define nl_write_unlock(LOCK) do { } while(0)
 #endif
 
+static inline int rtnl_tc_calc_txtime64(int bufsize, uint64_t rate)
+{
+	return ((double) bufsize / (double) rate) * 1000000.0;
+}
+
+static inline int rtnl_tc_calc_bufsize64(int txtime, uint64_t rate)
+{
+	return ((double) txtime * (double) rate) / 1000000.0;
+}
+
 #endif
diff --git a/include/netlink-private/object-api.h b/include/netlink-private/object-api.h
index f4fd71e..517e672 100644
--- a/include/netlink-private/object-api.h
+++ b/include/netlink-private/object-api.h
@@ -126,6 +126,8 @@
  * #define MY_ATTR_FOO		(1<<0)
  * #define MY_ATTR_BAR		(1<<1)
  *
+ * // Bit 31 for attributes is reserved for 32-bit API.
+ *
  * // When assigning an optional attribute to the object, make sure
  * // to mark its availability.
  * my_obj->foo = 123123;
@@ -189,7 +191,7 @@
 	struct nl_list_head	ce_list;	\
 	int			ce_msgtype;	\
 	int			ce_flags;	\
-	uint32_t		ce_mask;
+	uint64_t		ce_mask;
 
 struct nl_object
 {
@@ -258,7 +260,7 @@
  * @endcode
  */
 #define ATTR_DIFF(LIST, ATTR, A, B, EXPR) \
-({	int diff = 0; \
+({	uint64_t diff = 0; \
 	if (((LIST) & (ATTR)) && ATTR_MISMATCH(A, B, ATTR, EXPR)) \
 		diff = ATTR; \
 	diff; })
@@ -333,8 +335,8 @@
 	 * The function must return a bitmask with the relevant bit
 	 * set for each attribute that mismatches.
 	 */
-	int   (*oo_compare)(struct nl_object *, struct nl_object *,
-			    uint32_t, int);
+	uint64_t (*oo_compare)(struct nl_object *, struct nl_object *,
+			       uint64_t, int);
 
 
 	/**
diff --git a/include/netlink-private/route/link/api.h b/include/netlink-private/route/link/api.h
index bb98ccc..6d30bf7 100644
--- a/include/netlink-private/route/link/api.h
+++ b/include/netlink-private/route/link/api.h
@@ -60,6 +60,10 @@
 	 * in either io_alloc() or io_parse(). */
 	void	      (*io_free)(struct rtnl_link *);
 
+	/** Called to compare link info parameters between two links. */
+	int	      (*io_compare)(struct rtnl_link *, struct rtnl_link *,
+				    int flags);
+
 	struct nl_list_head		io_list;
 };
 
@@ -115,6 +119,16 @@
 	int		      (*ao_fill_af)(struct rtnl_link *,
 					    struct nl_msg *msg, void *);
 
+	/** Called if the full IFLA_AF_SPEC data needs to be parsed. Typically
+	 * stores the parsed data in the address family specific buffer. */
+	int                   (*ao_parse_af_full)(struct rtnl_link *,
+	                                          struct nlattr *, void *);
+
+	/** Called for GETLINK message to the kernel. Used to append
+	 * link address family specific attributes to the request message. */
+	int		      (*ao_get_af)(struct nl_msg *msg,
+					   uint32_t *ext_filter_mask);
+
 	/** Dump address family specific link attributes */
 	void		      (*ao_dump[NL_DUMP_MAX+1])(struct rtnl_link *,
 							struct nl_dump_params *,
@@ -132,6 +146,35 @@
 	 */
 	int		      (*ao_compare)(struct rtnl_link *,
 					    struct rtnl_link *, int, uint32_t, int);
+
+	/* RTM_NEWLINK override
+	 *
+	 * Called if a change link request is set to the kernel. If this returns
+	 * anything other than zero, RTM_NEWLINK will be overriden with
+	 * RTM_SETLINK when rtnl_link_build_change_request() is called.
+	 */
+	int		      (*ao_override_rtm)(struct rtnl_link *);
+
+	/** Called if a link message is sent to the kernel. Must append the
+	 * link protocol specific attributes to the message. (IFLA_PROTINFO) */
+	int		      (*ao_fill_pi)(struct rtnl_link *,
+								struct nl_msg *msg, void *);
+
+	/** PROTINFO type
+	 *
+	 * Called if a link message is sent to the kernel. If this is set,
+	 * the default IFLA_PROTINFO is bitmasked with what is specified
+	 * here. (eg. NLA_F_NESTED)
+	 */
+	const int ao_fill_pi_flags;
+
+	/** IFLA_AF_SPEC nesting override
+	 *
+	 * Called if a link message is sent to the kernel. If this is set,
+	 * the AF specific nest is not created. Instead, AF specific attributes
+	 * are nested directly in the IFLA_AF_SPEC attribute.
+	 */
+	 const int ao_fill_af_no_nest;
 };
 
 extern struct rtnl_link_af_ops *rtnl_link_af_ops_lookup(unsigned int);
@@ -145,6 +188,9 @@
 extern int			rtnl_link_af_data_compare(struct rtnl_link *a,
 							  struct rtnl_link *b,
 							  int family);
+extern int			rtnl_link_info_data_compare(struct rtnl_link *a,
+							    struct rtnl_link *b,
+							    int flags);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink-private/route/link/sriov.h b/include/netlink-private/route/link/sriov.h
new file mode 100644
index 0000000..ac653ed
--- /dev/null
+++ b/include/netlink-private/route/link/sriov.h
@@ -0,0 +1,34 @@
+/*
+ * include/netlink-private/route/link/sriov.h      SRIOV VF Info
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2016 Intel Corp. All rights reserved.
+ * Copyright (c) 2016 Jef Oliver <jef.oliver@intel.com>
+ */
+
+#ifndef NETLINK_PRIV_LINK_SRIOV_H_
+#define NETLINK_PRIV_LINK_SRIOV_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link/sriov.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int rtnl_link_sriov_clone(struct rtnl_link *, struct rtnl_link *);
+extern void rtnl_link_sriov_dump_details(struct rtnl_link *, struct nl_dump_params *);
+extern void rtnl_link_sriov_dump_stats(struct rtnl_link *, struct nl_dump_params *);
+extern int rtnl_link_sriov_fill_vflist(struct nl_msg *, struct rtnl_link *);
+extern void rtnl_link_sriov_free_data(struct rtnl_link *);
+extern int rtnl_link_sriov_parse_vflist(struct rtnl_link *, struct nlattr **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink-private/route/mpls.h b/include/netlink-private/route/mpls.h
new file mode 100644
index 0000000..502fd34
--- /dev/null
+++ b/include/netlink-private/route/mpls.h
@@ -0,0 +1,15 @@
+#ifndef MPLS_H_
+#define MPLS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const char *mpls_ntop(int af, const void *addr, char *buf, size_t buflen);
+extern int mpls_pton(int af, const char *src, void *addr, size_t alen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink-private/route/nexthop-encap.h b/include/netlink-private/route/nexthop-encap.h
new file mode 100644
index 0000000..dde1bfb
--- /dev/null
+++ b/include/netlink-private/route/nexthop-encap.h
@@ -0,0 +1,35 @@
+#ifndef NETLINK_NEXTHOP_ENCAP_H_
+#define	NETLINK_NEXTHOP_ENCAP_H_
+
+struct nh_encap_ops {
+	uint16_t encap_type;
+
+	int	(*build_msg)(struct nl_msg *msg, void *priv);
+	int	(*parse_msg)(struct nlattr *nla, struct rtnl_nexthop *rtnh);
+
+	int	(*compare)(void *a, void *b);
+
+	void	(*dump)(void *priv, struct nl_dump_params *dp);
+	void	(*destructor)(void *priv);
+};
+
+struct rtnl_nh_encap;
+
+/*
+ * generic nexthop encap
+ */
+void nh_set_encap(struct rtnl_nexthop *nh, struct rtnl_nh_encap *rtnh_encap);
+
+int nh_encap_parse_msg(struct nlattr *encap, struct nlattr *encap_type,
+		       struct rtnl_nexthop *rtnh);
+int nh_encap_build_msg(struct nl_msg *msg, struct rtnl_nh_encap *rtnh_encap);
+
+void nh_encap_dump(struct rtnl_nh_encap *rtnh_encap, struct nl_dump_params *dp);
+
+int nh_encap_compare(struct rtnl_nh_encap *a, struct rtnl_nh_encap *b);
+
+/*
+ * MPLS encap
+ */
+extern struct nh_encap_ops mpls_encap_ops;
+#endif
diff --git a/include/netlink-private/route/tc-api.h b/include/netlink-private/route/tc-api.h
index bf0c8a3..7158ce5 100644
--- a/include/netlink-private/route/tc-api.h
+++ b/include/netlink-private/route/tc-api.h
@@ -110,13 +110,14 @@
 						     struct nl_dump_params *);
 extern void			rtnl_tc_dump_stats(struct nl_object *,
 						   struct nl_dump_params *);
-extern int			rtnl_tc_compare(struct nl_object *,
+extern uint64_t			rtnl_tc_compare(struct nl_object *,
 						struct nl_object *,
-						uint32_t, int);
+						uint64_t, int);
 
+void *                          rtnl_tc_data_peek(struct rtnl_tc *tc);
 extern void *			rtnl_tc_data(struct rtnl_tc *);
 extern void *			rtnl_tc_data_check(struct rtnl_tc *,
-						   struct rtnl_tc_ops *);
+						   struct rtnl_tc_ops *, int *);
 
 extern struct rtnl_tc_ops *	rtnl_tc_lookup_ops(enum rtnl_tc_type,
 						   const char *);
diff --git a/include/netlink-private/socket.h b/include/netlink-private/socket.h
index 86a440c..9ceecfd 100644
--- a/include/netlink-private/socket.h
+++ b/include/netlink-private/socket.h
@@ -19,7 +19,7 @@
 #endif
 
 int _nl_socket_is_local_port_unspecified (struct nl_sock *sk);
-uint32_t _nl_socket_generate_local_port_no_release(struct nl_sock *sk);
+uint32_t _nl_socket_set_local_port_no_release(struct nl_sock *sk, int generate_other);
 
 void _nl_socket_used_ports_release_all(const uint32_t *used_ports);
 void _nl_socket_used_ports_set(uint32_t *used_ports, uint32_t port);
diff --git a/include/netlink-private/tc.h b/include/netlink-private/tc.h
index d0cb283..b0f9c50 100644
--- a/include/netlink-private/tc.h
+++ b/include/netlink-private/tc.h
@@ -32,10 +32,11 @@
 #define TCA_ATTR_MPU		0x0800
 #define TCA_ATTR_OVERHEAD	0x1000
 #define TCA_ATTR_LINKTYPE	0x2000
-#define TCA_ATTR_MAX		TCA_ATTR_LINKTYPE
+#define TCA_ATTR_CHAIN          0x4000
+#define TCA_ATTR_MAX            TCA_ATTR_CHAIN
 
 extern int tca_parse(struct nlattr **, int, struct rtnl_tc *,
-		     struct nla_policy *);
+                     const struct nla_policy *);
 
 #define RTNL_TC_RTABLE_SIZE	256
 
diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h
index 3ff4fe1..97af3e5 100644
--- a/include/netlink-private/types.h
+++ b/include/netlink-private/types.h
@@ -20,14 +20,25 @@
 #include <netlink/route/route.h>
 #include <netlink/idiag/idiagnl.h>
 #include <netlink/netfilter/ct.h>
+#include <netlink-private/object-api.h>
 #include <netlink-private/route/tc-api.h>
+#include <netlink-private/route/link/sriov.h>
+#include <netlink-private/route/nexthop-encap.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/genetlink.h>
 #include <linux/tc_act/tc_mirred.h>
+#include <linux/tc_act/tc_skbedit.h>
+#include <linux/tc_act/tc_gact.h>
+#include <linux/tc_act/tc_vlan.h>
+#include <linux/sock_diag.h>
+#include <linux/fib_rules.h>
 
-#define NL_SOCK_BUFSIZE_SET	(1<<0)
 #define NL_SOCK_PASSCRED	(1<<1)
 #define NL_OWN_PORT		(1<<2)
 #define NL_MSG_PEEK		(1<<3)
-#define NL_NO_AUTO_ACK		(1<<4)
+#define NL_MSG_PEEK_EXPLICIT	(1<<4)
+#define NL_NO_AUTO_ACK		(1<<5)
 
 #define NL_MSG_CRED_PRESENT 1
 
@@ -35,6 +46,7 @@
 struct nl_sock;
 struct nl_object;
 struct nl_hash_table;
+struct nl_vf_vlans;
 
 struct nl_cb
 {
@@ -95,6 +107,7 @@
 {
 	struct nl_cache *	ca_cache;
 	change_func_t		ca_change;
+	change_func_v2_t	ca_change_v2;
 	void *			ca_change_data;
 };
 
@@ -111,6 +124,7 @@
 struct nl_parser_param;
 
 #define LOOSE_COMPARISON	1
+#define ID_COMPARISON           2
 
 #define NL_OBJ_MARK		1
 
@@ -152,6 +166,26 @@
 	uint8_t  lm_port;
 };
 
+struct rtnl_link_vf
+{
+	struct nl_list_head	vf_list;
+	int			ce_refcnt;
+	uint32_t		ce_mask;
+	uint32_t		vf_index;
+	uint64_t		vf_guid_node;
+	uint64_t		vf_guid_port;
+	uint32_t		vf_linkstate;
+	struct nl_addr *	vf_lladdr;
+	uint32_t		vf_max_tx_rate;
+	uint32_t		vf_min_tx_rate;
+	uint32_t		vf_rate;
+	uint32_t		vf_rss_query_en;
+	uint32_t		vf_spoofchk;
+	uint64_t		vf_stats[RTNL_LINK_VF_STATS_MAX+1];
+	uint32_t		vf_trust;
+	struct nl_vf_vlans *	vf_vlans;
+};
+
 #define IFQDISCSIZ	32
 
 struct rtnl_link
@@ -164,8 +198,9 @@
 	uint32_t			l_index;
 	uint32_t			l_flags;
 	uint32_t			l_change;
-	uint32_t 			l_mtu;
+	uint32_t			l_mtu;
 	uint32_t			l_link;
+	int32_t                         l_link_netnsid;
 	uint32_t			l_txqlen;
 	uint32_t			l_weight;
 	uint32_t			l_master;
@@ -180,6 +215,7 @@
 	uint8_t				l_linkmode;
 	/* 2 byte hole */
 	char *				l_info_kind;
+	char *				l_info_slave_kind;
 	struct rtnl_link_info_ops *	l_info_ops;
 	void *				l_af_data[AF_MAX];
 	void *				l_info;
@@ -187,13 +223,19 @@
 	uint32_t			l_promiscuity;
 	uint32_t			l_num_tx_queues;
 	uint32_t			l_num_rx_queues;
+	uint32_t			l_gso_max_segs;
+	uint32_t			l_gso_max_size;
 	uint32_t			l_group;
 	uint8_t				l_carrier;
 	/* 3 byte hole */
+	uint32_t			l_carrier_changes;
 	struct rtnl_link_af_ops *	l_af_ops;
 	struct nl_data *		l_phys_port_id;
+	char				l_phys_port_name[IFNAMSIZ];
+	struct nl_data *		l_phys_switch_id;
 	int				l_ns_fd;
 	pid_t				l_ns_pid;
+	struct rtnl_link_vf *		l_vf_list;
 };
 
 struct rtnl_ncacheinfo
@@ -220,6 +262,7 @@
 	uint32_t                n_state_mask;
 	uint32_t                n_flag_mask;
 	uint32_t		n_master;
+	uint16_t	n_vlan;
 };
 
 
@@ -261,6 +304,12 @@
 	struct rtnl_link *a_link;
 };
 
+struct rtnl_nh_encap
+{
+	struct nh_encap_ops *ops;
+	void *priv;    /* private data for encap type */
+};
+
 struct rtnl_nexthop
 {
 	uint8_t			rtnh_flags;
@@ -272,6 +321,9 @@
 	uint32_t		ce_mask; /* HACK to support attr macros */
 	struct nl_list_head	rtnh_list;
 	uint32_t		rtnh_realms;
+	struct nl_addr *	rtnh_newdst;
+	struct nl_addr *	rtnh_via;
+	struct rtnl_nh_encap *	rtnh_encap;
 };
 
 struct rtnl_route
@@ -286,6 +338,7 @@
 	uint8_t			rt_scope;
 	uint8_t			rt_type;
 	uint8_t			rt_nmetrics;
+	uint8_t			rt_ttl_propagate;
 	uint32_t		rt_flags;
 	struct nl_addr *	rt_dst;
 	struct nl_addr *	rt_src;
@@ -307,7 +360,9 @@
 	uint8_t		r_family;
 	uint8_t		r_action;
 	uint8_t		r_dsfield; /* ipv4 only */
-	uint8_t		r_unused;
+	uint8_t		r_l3mdev;
+	uint8_t		r_protocol; /* protocol that installed rule */
+	uint8_t		r_ip_proto; /* IP/IPv6 protocol */
 	uint32_t	r_table;
 	uint32_t	r_flags;
 	uint32_t	r_prio;
@@ -319,6 +374,9 @@
 	struct nl_addr *r_dst;
 	char		r_iifname[IFNAMSIZ];
 	char		r_oifname[IFNAMSIZ];
+
+	struct fib_rule_port_range	r_sport;
+	struct fib_rule_port_range	r_dport;
 };
 
 struct rtnl_neightbl_parms
@@ -437,11 +495,11 @@
 
 struct rtnl_ratespec
 {
-	uint8_t			rs_cell_log;
+	uint64_t		rs_rate64;
 	uint16_t		rs_overhead;
 	int16_t			rs_cell_align;
 	uint16_t		rs_mpu;
-	uint32_t		rs_rate;
+	uint8_t			rs_cell_log;
 };
 
 struct rtnl_tstats
@@ -485,7 +543,8 @@
 	struct nl_data *	pre ##_subdata;		\
 	struct rtnl_link *	pre ##_link;		\
 	struct rtnl_tc_ops *	pre ##_ops;		\
-	enum rtnl_tc_type	pre ##_type
+	enum rtnl_tc_type	pre ##_type;		\
+	uint32_t		pre ##_chain
 
 struct rtnl_tc
 {
@@ -520,6 +579,20 @@
 	struct tc_mirred m_parm;
 };
 
+struct rtnl_skbedit
+{
+	struct tc_skbedit s_parm;
+	uint32_t	  s_flags;
+	uint32_t	  s_mark;
+	uint32_t	  s_prio;
+	uint16_t	  s_queue_mapping;
+};
+
+struct rtnl_gact
+{
+	struct tc_gact g_parm;
+};
+
 struct rtnl_u32
 {
 	uint32_t		cu_divisor;
@@ -528,12 +601,21 @@
 	uint32_t		cu_link;
 	struct nl_data *	cu_pcnt;
 	struct nl_data *	cu_selector;
+	struct nl_data *	cu_mark;
 	struct rtnl_act*	cu_act;
 	struct nl_data *	cu_police;
 	char			cu_indev[IFNAMSIZ];
 	int			cu_mask;
 };
 
+struct rtnl_mall
+{
+	uint32_t         m_classid;
+	uint32_t         m_flags;
+	struct rtnl_act *m_act;
+	int              m_mask;
+};
+
 struct rtnl_cgroup
 {
 	struct rtnl_ematch_tree *cg_ematch;
@@ -600,6 +682,20 @@
 	uint32_t	qp_mask;
 };
 
+struct rtnl_mqprio
+{
+        uint8_t         qm_num_tc;
+        uint8_t         qm_prio_map[TC_QOPT_BITMASK + 1];
+        uint8_t         qm_hw;
+        uint16_t        qm_count[TC_QOPT_MAX_QUEUE];
+        uint16_t        qm_offset[TC_QOPT_MAX_QUEUE];
+        uint16_t        qm_mode;
+        uint16_t        qm_shaper;
+        uint64_t        qm_min_rate[TC_QOPT_MAX_QUEUE];
+        uint64_t        qm_max_rate[TC_QOPT_MAX_QUEUE];
+        uint32_t        qm_mask;
+};
+
 struct rtnl_tbf
 {
 	uint32_t		qt_limit;
@@ -721,6 +817,20 @@
 	uint32_t	fq_mask;
 };
 
+struct rtnl_hfsc_qdisc
+{
+	uint32_t		qh_defcls;
+	uint32_t		qh_mask;
+};
+
+struct rtnl_hfsc_class
+{
+	struct tc_service_curve ch_rsc;
+	struct tc_service_curve ch_fsc;
+	struct tc_service_curve ch_usc;
+	uint32_t		ch_mask;
+};
+
 struct flnl_request
 {
 	NLHDR_COMMON
@@ -986,7 +1096,7 @@
 	struct idiagnl_meminfo *    idiag_meminfo;
 	struct idiagnl_vegasinfo *  idiag_vegasinfo;
 	struct tcp_info		    idiag_tcpinfo;
-	uint32_t		    idiag_skmeminfo[IDIAG_SK_MEMINFO_VARS];
+	uint32_t		    idiag_skmeminfo[SK_MEMINFO_VARS];
 };
 
 struct idiagnl_req {
@@ -1000,4 +1110,232 @@
 	uint32_t		idiag_states;
 	uint32_t		idiag_dbs;
 };
+
+// XFRM related definitions
+
+/* Selector, used as selector both on policy rules (SPD) and SAs. */
+struct xfrmnl_sel {
+	uint32_t        refcnt;
+	struct nl_addr* daddr;
+	struct nl_addr* saddr;
+	uint16_t        dport;
+	uint16_t        dport_mask;
+	uint16_t        sport;
+	uint16_t        sport_mask;
+	uint16_t        family;
+	uint8_t         prefixlen_d;
+	uint8_t         prefixlen_s;
+	uint8_t         proto;
+	int32_t         ifindex;
+	uint32_t        user;
+};
+
+/* Lifetime configuration, used for both policy rules (SPD) and SAs. */
+struct xfrmnl_ltime_cfg {
+	uint32_t        refcnt;
+	uint64_t        soft_byte_limit;
+	uint64_t        hard_byte_limit;
+	uint64_t        soft_packet_limit;
+	uint64_t        hard_packet_limit;
+	uint64_t        soft_add_expires_seconds;
+	uint64_t        hard_add_expires_seconds;
+	uint64_t        soft_use_expires_seconds;
+	uint64_t        hard_use_expires_seconds;
+};
+
+/* Current lifetime, used for both policy rules (SPD) and SAs. */
+struct xfrmnl_lifetime_cur {
+	uint64_t        bytes;
+	uint64_t        packets;
+	uint64_t        add_time;
+	uint64_t        use_time;
+};
+
+struct xfrmnl_replay_state {
+	uint32_t        oseq;
+	uint32_t        seq;
+	uint32_t        bitmap;
+};
+
+struct xfrmnl_replay_state_esn {
+	uint32_t        bmp_len;
+	uint32_t        oseq;
+	uint32_t        seq;
+	uint32_t        oseq_hi;
+	uint32_t        seq_hi;
+	uint32_t        replay_window;
+	uint32_t        bmp[0];
+};
+
+struct xfrmnl_mark {
+	uint32_t        v; /* value */
+	uint32_t        m; /* mask */
+};
+
+/* XFRM AE related definitions */
+
+struct xfrmnl_sa_id {
+	struct nl_addr* daddr;
+	uint32_t        spi;
+	uint16_t        family;
+	uint8_t         proto;
+};
+
+struct xfrmnl_ae {
+	NLHDR_COMMON
+
+	struct xfrmnl_sa_id             sa_id;
+	struct nl_addr*                 saddr;
+	uint32_t                        flags;
+	uint32_t                        reqid;
+	struct xfrmnl_mark              mark;
+	struct xfrmnl_lifetime_cur      lifetime_cur;
+	uint32_t                        replay_maxage;
+	uint32_t                        replay_maxdiff;
+	struct xfrmnl_replay_state      replay_state;
+	struct xfrmnl_replay_state_esn* replay_state_esn;
+};
+
+/* XFRM SA related definitions */
+
+struct xfrmnl_id {
+	struct nl_addr* daddr;
+	uint32_t        spi;
+	uint8_t         proto;
+};
+
+struct xfrmnl_stats {
+	uint32_t        replay_window;
+	uint32_t        replay;
+	uint32_t        integrity_failed;
+};
+
+struct xfrmnl_algo_aead {
+	char            alg_name[64];
+	uint32_t        alg_key_len;    /* in bits */
+	uint32_t        alg_icv_len;    /* in bits */
+	char            alg_key[0];
+};
+
+struct xfrmnl_algo_auth {
+	char            alg_name[64];
+	uint32_t        alg_key_len;    /* in bits */
+	uint32_t        alg_trunc_len;  /* in bits */
+	char            alg_key[0];
+};
+
+struct xfrmnl_algo {
+	char            alg_name[64];
+	uint32_t        alg_key_len;    /* in bits */
+	char            alg_key[0];
+};
+
+struct xfrmnl_encap_tmpl {
+	uint16_t        encap_type;
+	uint16_t        encap_sport;
+	uint16_t        encap_dport;
+	struct nl_addr* encap_oa;
+};
+
+struct xfrmnl_sa {
+	NLHDR_COMMON
+
+	struct xfrmnl_sel*              sel;
+	struct xfrmnl_id                id;
+	struct nl_addr*                 saddr;
+	struct xfrmnl_ltime_cfg*        lft;
+	struct xfrmnl_lifetime_cur      curlft;
+	struct xfrmnl_stats             stats;
+	uint32_t                        seq;
+	uint32_t                        reqid;
+	uint16_t                        family;
+	uint8_t                         mode;        /* XFRM_MODE_xxx */
+	uint8_t                         replay_window;
+	uint8_t                         flags;
+	struct xfrmnl_algo_aead*        aead;
+	struct xfrmnl_algo_auth*        auth;
+	struct xfrmnl_algo*             crypt;
+	struct xfrmnl_algo*             comp;
+	struct xfrmnl_encap_tmpl*       encap;
+	uint32_t                        tfcpad;
+	struct nl_addr*                 coaddr;
+	struct xfrmnl_mark              mark;
+	struct xfrmnl_user_sec_ctx*     sec_ctx;
+	uint32_t                        replay_maxage;
+	uint32_t                        replay_maxdiff;
+	struct xfrmnl_replay_state      replay_state;
+	struct xfrmnl_replay_state_esn* replay_state_esn;
+	uint8_t                         hard;
+};
+
+struct xfrmnl_usersa_flush {
+	uint8_t                         proto;
+};
+
+
+/* XFRM SP related definitions */
+
+struct xfrmnl_userpolicy_id {
+	struct xfrmnl_sel               sel;
+	uint32_t                        index;
+	uint8_t                         dir;
+};
+
+struct xfrmnl_user_sec_ctx {
+	uint16_t                        len;
+	uint16_t                        exttype;
+	uint8_t                         ctx_alg;
+	uint8_t                         ctx_doi;
+	uint16_t                        ctx_len;
+	char                            ctx[0];
+};
+
+struct xfrmnl_userpolicy_type {
+	uint8_t                         type;
+	uint16_t                        reserved1;
+	uint16_t                        reserved2;
+};
+
+struct xfrmnl_user_tmpl {
+	struct xfrmnl_id                id;
+	uint16_t                        family;
+	struct nl_addr*                 saddr;
+	uint32_t                        reqid;
+	uint8_t                         mode;
+	uint8_t                         share;
+	uint8_t                         optional;
+	uint32_t                        aalgos;
+	uint32_t                        ealgos;
+	uint32_t                        calgos;
+	struct nl_list_head             utmpl_list;
+};
+
+struct xfrmnl_sp {
+	NLHDR_COMMON
+
+	struct xfrmnl_sel*              sel;
+	struct xfrmnl_ltime_cfg*        lft;
+	struct xfrmnl_lifetime_cur      curlft;
+	uint32_t                        priority;
+	uint32_t                        index;
+	uint8_t                         dir;
+	uint8_t                         action;
+	uint8_t                         flags;
+	uint8_t                         share;
+	struct xfrmnl_user_sec_ctx*     sec_ctx;
+	struct xfrmnl_userpolicy_type   uptype;
+	uint32_t                        nr_user_tmpl;
+	struct nl_list_head             usertmpl_list;
+	struct xfrmnl_mark              mark;
+};
+
+struct rtnl_vlan
+{
+	struct tc_vlan v_parm;
+	uint16_t       v_vid;
+	uint16_t       v_proto;
+	uint8_t        v_prio;
+	uint32_t       v_flags;
+};
+
 #endif
diff --git a/include/netlink-private/utils.h b/include/netlink-private/utils.h
new file mode 100644
index 0000000..f33a2f8
--- /dev/null
+++ b/include/netlink-private/utils.h
@@ -0,0 +1,223 @@
+/*
+ * netlink-private/utils.h	Local Utility Functions
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_UTILS_PRIV_H_
+#define NETLINK_UTILS_PRIV_H_
+
+#include <byteswap.h>
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define ntohll(x) (x)
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+#define ntohll(x) bswap_64((x))
+#endif
+#define htonll(x) ntohll(x)
+
+/*****************************************************************************/
+
+#define _NL_STRINGIFY_ARG(contents)       #contents
+#define _NL_STRINGIFY(macro_or_string)    _NL_STRINGIFY_ARG (macro_or_string)
+
+/*****************************************************************************/
+
+#if defined (__GNUC__)
+#define _NL_PRAGMA_WARNING_DO(warning)       _NL_STRINGIFY(GCC diagnostic ignored warning)
+#elif defined (__clang__)
+#define _NL_PRAGMA_WARNING_DO(warning)       _NL_STRINGIFY(clang diagnostic ignored warning)
+#endif
+
+/* you can only suppress a specific warning that the compiler
+ * understands. Otherwise you will get another compiler warning
+ * about invalid pragma option.
+ * It's not that bad however, because gcc and clang often have the
+ * same name for the same warning. */
+
+#if defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#define _NL_PRAGMA_WARNING_DISABLE(warning) \
+        _Pragma("GCC diagnostic push") \
+        _Pragma(_NL_PRAGMA_WARNING_DO("-Wpragmas")) \
+        _Pragma(_NL_PRAGMA_WARNING_DO(warning))
+#elif defined (__clang__)
+#define _NL_PRAGMA_WARNING_DISABLE(warning) \
+        _Pragma("clang diagnostic push") \
+        _Pragma(_NL_PRAGMA_WARNING_DO("-Wunknown-warning-option")) \
+        _Pragma(_NL_PRAGMA_WARNING_DO(warning))
+#else
+#define _NL_PRAGMA_WARNING_DISABLE(warning)
+#endif
+
+#if defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#define _NL_PRAGMA_WARNING_REENABLE \
+    _Pragma("GCC diagnostic pop")
+#elif defined (__clang__)
+#define _NL_PRAGMA_WARNING_REENABLE \
+    _Pragma("clang diagnostic pop")
+#else
+#define _NL_PRAGMA_WARNING_REENABLE
+#endif
+
+/*****************************************************************************/
+
+#define _nl_unused                  __attribute__ ((__unused__))
+#define _nl_auto(fcn)               __attribute__ ((__cleanup__(fcn)))
+
+/*****************************************************************************/
+
+#define _NL_STATIC_ASSERT(cond) ((void) sizeof (char[(cond) ? 1 : -1]))
+
+/*****************************************************************************/
+
+#if defined(NL_MORE_ASSERTS) && NL_MORE_ASSERTS > 0
+#define _nl_assert(cond) assert(cond)
+#else
+#define _nl_assert(cond) do { if (0) { assert(cond); } } while (0)
+#endif
+
+/*****************************************************************************/
+
+#define _NL_AUTO_DEFINE_FCN_VOID0(CastType, name, func) \
+static inline void name (void *v) \
+{ \
+	if (*((CastType *) v)) \
+		func (*((CastType *) v)); \
+}
+
+#define _nl_auto_free _nl_auto(_nl_auto_free_fcn)
+_NL_AUTO_DEFINE_FCN_VOID0 (void *, _nl_auto_free_fcn, free)
+
+/*****************************************************************************/
+
+extern const char *nl_strerror_l(int err);
+
+/*****************************************************************************/
+
+/* internal macro to calculate the size of a struct @type up to (and including) @field.
+ * this will be used for .minlen policy fields, so that we require only a field of up
+ * to the given size. */
+#define _nl_offsetofend(type, field) (offsetof (type, field) + sizeof (((type *) NULL)->field))
+
+/*****************************************************************************/
+
+#define _nl_clear_pointer(pp, destroy) \
+	({ \
+		__typeof__ (*(pp)) *_pp = (pp); \
+		__typeof__ (*_pp) _p; \
+		int _changed = 0; \
+		\
+		if (   _pp \
+			&& (_p = *_pp)) { \
+			_nl_unused const void *const _p_check_is_pointer = _p; \
+			\
+			*_pp = NULL; \
+			\
+			(destroy) (_p); \
+			\
+			_changed = 1; \
+		} \
+		_changed; \
+	})
+
+#define _nl_clear_free(pp) _nl_clear_pointer (pp, free)
+
+#define _nl_steal_pointer(pp) \
+	({ \
+		__typeof__ (*(pp)) *const _pp = (pp); \
+		__typeof__ (*_pp) _p = NULL; \
+		\
+		if (   _pp \
+		    && (_p = *_pp)) { \
+			*_pp = NULL; \
+		} \
+		\
+		_p; \
+	})
+
+/*****************************************************************************/
+
+#define _nl_malloc_maybe_a(alloca_maxlen, bytes, to_free) \
+	({ \
+		const size_t _bytes = (bytes); \
+		__typeof__ (to_free) _to_free = (to_free); \
+		__typeof__ (*_to_free) _ptr; \
+		\
+		_NL_STATIC_ASSERT ((alloca_maxlen) <= 500); \
+		_nl_assert (_to_free && !*_to_free); \
+		\
+		if (_bytes <= (alloca_maxlen)) { \
+			_ptr = alloca (_bytes); \
+		} else { \
+			_ptr = malloc (_bytes); \
+			*_to_free = _ptr; \
+		}; \
+		\
+		_ptr; \
+	})
+
+/*****************************************************************************/
+
+static inline char *
+_nl_strncpy_trunc(char *dst, const char *src, size_t len)
+{
+	/* we don't use/reimplement strlcpy(), because we want the fill-all-with-NUL
+	 * behavior of strncpy(). This is just strncpy() with gracefully handling trunction
+	 * (and disabling the "-Wstringop-truncation" warning).
+	 *
+	 * Note that truncation is silently accepted.
+	 */
+
+	_NL_PRAGMA_WARNING_DISABLE ("-Wstringop-truncation");
+	_NL_PRAGMA_WARNING_DISABLE ("-Wstringop-overflow");
+
+	if (len > 0) {
+		_nl_assert(dst);
+		_nl_assert(src);
+
+		strncpy(dst, src, len);
+
+		dst[len - 1] = '\0';
+	}
+
+	_NL_PRAGMA_WARNING_REENABLE;
+	_NL_PRAGMA_WARNING_REENABLE;
+
+	return dst;
+}
+
+static inline char *
+_nl_strncpy(char *dst, const char *src, size_t len)
+{
+	/* we don't use/reimplement strlcpy(), because we want the fill-all-with-NUL
+	 * behavior of strncpy(). This is just strncpy() with gracefully handling trunction
+	 * (and disabling the "-Wstringop-truncation" warning).
+	 *
+	 * Note that truncation is still a bug and there is an _nl_assert()
+	 * against that.
+	 */
+
+	if (len > 0) {
+		_nl_assert(dst);
+		_nl_assert(src);
+
+		strncpy(dst, src, len);
+
+		/* Truncation is a bug and we assert against it. But note that this
+		 * assertion is disabled by default because we cannot be sure that
+		 * there are not wrong uses of _nl_strncpy() where truncation might
+		 * happen (wrongly!!). */
+		_nl_assert (memchr(dst, '\0', len));
+
+		dst[len - 1] = '\0';
+	}
+
+	return dst;
+}
+
+#endif
diff --git a/include/netlink/addr.h b/include/netlink/addr.h
index db3e4c2..00ca784 100644
--- a/include/netlink/addr.h
+++ b/include/netlink/addr.h
@@ -18,46 +18,51 @@
 extern "C" {
 #endif
 
+struct nlattr;
+
 struct nl_addr;
 
 /* Creation */
 extern struct nl_addr *	nl_addr_alloc(size_t);
-extern struct nl_addr *	nl_addr_alloc_attr(struct nlattr *, int);
-extern struct nl_addr *	nl_addr_build(int, void *, size_t);
+extern struct nl_addr *	nl_addr_alloc_attr(const struct nlattr *, int);
+extern struct nl_addr *	nl_addr_build(int, const void *, size_t);
 extern int		nl_addr_parse(const char *, int, struct nl_addr **);
-extern struct nl_addr *	nl_addr_clone(struct nl_addr *);
+extern struct nl_addr *	nl_addr_clone(const struct nl_addr *);
 
 /* Usage Management */
 extern struct nl_addr *	nl_addr_get(struct nl_addr *);
 extern void		nl_addr_put(struct nl_addr *);
-extern int		nl_addr_shared(struct nl_addr *);
+extern int		nl_addr_shared(const struct nl_addr *);
 
-extern int		nl_addr_cmp(struct nl_addr *, struct nl_addr *);
-extern int		nl_addr_cmp_prefix(struct nl_addr *, struct nl_addr *);
-extern int		nl_addr_iszero(struct nl_addr *);
-extern int		nl_addr_valid(char *, int);
-extern int      	nl_addr_guess_family(struct nl_addr *);
-extern int		nl_addr_fill_sockaddr(struct nl_addr *,
+extern int		nl_addr_cmp(const struct nl_addr *,
+				    const struct nl_addr *);
+extern int		nl_addr_cmp_prefix(const struct nl_addr *,
+					   const struct nl_addr *);
+extern int		nl_addr_iszero(const struct nl_addr *);
+extern int		nl_addr_valid(const char *, int);
+extern int      	nl_addr_guess_family(const struct nl_addr *);
+extern int		nl_addr_fill_sockaddr(const struct nl_addr *,
 					      struct sockaddr *, socklen_t *);
-extern int		nl_addr_info(struct nl_addr *, struct addrinfo **);
-extern int		nl_addr_resolve(struct nl_addr *, char *, size_t);
+extern int		nl_addr_info(const struct nl_addr *,
+				     struct addrinfo **);
+extern int		nl_addr_resolve(const struct nl_addr *, char *, size_t);
 
 /* Access Functions */
 extern void		nl_addr_set_family(struct nl_addr *, int);
-extern int		nl_addr_get_family(struct nl_addr *);
-extern int		nl_addr_set_binary_addr(struct nl_addr *, void *,
+extern int		nl_addr_get_family(const struct nl_addr *);
+extern int		nl_addr_set_binary_addr(struct nl_addr *, const void *,
 						size_t);
-extern void *		nl_addr_get_binary_addr(struct nl_addr *);
-extern unsigned int	nl_addr_get_len(struct nl_addr *);
+extern void *		nl_addr_get_binary_addr(const struct nl_addr *);
+extern unsigned int	nl_addr_get_len(const struct nl_addr *);
 extern void		nl_addr_set_prefixlen(struct nl_addr *, int);
-extern unsigned int	nl_addr_get_prefixlen(struct nl_addr *);
+extern unsigned int	nl_addr_get_prefixlen(const struct nl_addr *);
 
 /* Address Family Translations */
 extern char *		nl_af2str(int, char *, size_t);
 extern int		nl_str2af(const char *);
 
 /* Translations to Strings */
-extern char *		nl_addr2str(struct nl_addr *, char *, size_t);
+extern char *		nl_addr2str(const struct nl_addr *, char *, size_t);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/attr.h b/include/netlink/attr.h
index 82e4c38..e47aa5d 100644
--- a/include/netlink/attr.h
+++ b/include/netlink/attr.h
@@ -21,6 +21,8 @@
 extern "C" {
 #endif
 
+struct nlattr;
+
 struct nl_msg;
 
 /**
@@ -44,6 +46,13 @@
 	NLA_FLAG,	/**< Flag */
 	NLA_MSECS,	/**< Micro seconds (64bit) */
 	NLA_NESTED,	/**< Nested attributes */
+	NLA_NESTED_COMPAT,
+	NLA_NUL_STRING,
+	NLA_BINARY,
+	NLA_S8,
+	NLA_S16,
+	NLA_S32,
+	NLA_S64,
 	__NLA_TYPE_MAX,
 };
 
@@ -80,13 +89,13 @@
 extern int		nla_ok(const struct nlattr *, int);
 extern struct nlattr *	nla_next(const struct nlattr *, int *);
 extern int		nla_parse(struct nlattr **, int, struct nlattr *,
-				  int, struct nla_policy *);
-extern int		nla_validate(struct nlattr *, int, int,
-				     struct nla_policy *);
-extern struct nlattr *	nla_find(struct nlattr *, int, int);
+				  int, const struct nla_policy *);
+extern int		nla_validate(const struct nlattr *, int, int,
+				     const struct nla_policy *);
+extern struct nlattr *	nla_find(const struct nlattr *, int, int);
 
 /* Helper Functions */
-extern int		nla_memcpy(void *, struct nlattr *, int);
+extern int		nla_memcpy(void *, const struct nlattr *, int);
 extern size_t		nla_strlcpy(char *, const struct nlattr *, size_t);
 extern int		nla_memcmp(const struct nlattr *, const void *, size_t);
 extern int		nla_strcmp(const struct nlattr *, const char *);
@@ -94,40 +103,51 @@
 /* Unspecific attribute */
 extern struct nlattr *	nla_reserve(struct nl_msg *, int, int);
 extern int		nla_put(struct nl_msg *, int, int, const void *);
-extern int		nla_put_data(struct nl_msg *, int, struct nl_data *);
+extern int		nla_put_data(struct nl_msg *, int,
+				     const struct nl_data *);
 extern int		nla_put_addr(struct nl_msg *, int, struct nl_addr *);
 
 /* Integer attribute */
-extern uint8_t		nla_get_u8(struct nlattr *);
+extern int8_t           nla_get_s8(const struct nlattr *);
+extern int              nla_put_s8(struct nl_msg *, int, int8_t);
+extern uint8_t		nla_get_u8(const struct nlattr *);
 extern int		nla_put_u8(struct nl_msg *, int, uint8_t);
-extern uint16_t		nla_get_u16(struct nlattr *);
+extern int16_t          nla_get_s16(const struct nlattr *);
+extern int              nla_put_s16(struct nl_msg *, int, int16_t);
+extern uint16_t		nla_get_u16(const struct nlattr *);
 extern int		nla_put_u16(struct nl_msg *, int, uint16_t);
-extern uint32_t		nla_get_u32(struct nlattr *);
+extern int32_t          nla_get_s32(const struct nlattr *);
+extern int              nla_put_s32(struct nl_msg *, int, int32_t);
+extern uint32_t		nla_get_u32(const struct nlattr *);
 extern int		nla_put_u32(struct nl_msg *, int, uint32_t);
-extern uint64_t		nla_get_u64(struct nlattr *);
+extern int64_t          nla_get_s64(const struct nlattr *);
+extern int              nla_put_s64(struct nl_msg *, int, int64_t);
+extern uint64_t		nla_get_u64(const struct nlattr *);
 extern int		nla_put_u64(struct nl_msg *, int, uint64_t);
 
 /* String attribute */
-extern char *		nla_get_string(struct nlattr *);
-extern char *		nla_strdup(struct nlattr *);
+extern char *		nla_get_string(const struct nlattr *);
+extern char *		nla_strdup(const struct nlattr *);
 extern int		nla_put_string(struct nl_msg *, int, const char *);
 
 /* Flag attribute */
-extern int		nla_get_flag(struct nlattr *);
+extern int		nla_get_flag(const struct nlattr *);
 extern int		nla_put_flag(struct nl_msg *, int);
 
 /* Msec attribute */
-extern unsigned long	nla_get_msecs(struct nlattr *);
+extern unsigned long	nla_get_msecs(const struct nlattr *);
 extern int		nla_put_msecs(struct nl_msg *, int, unsigned long);
 
 /* Attribute nesting */
-extern int		nla_put_nested(struct nl_msg *, int, struct nl_msg *);
+extern int		nla_put_nested(struct nl_msg *, int,
+				       const struct nl_msg *);
 extern struct nlattr *	nla_nest_start(struct nl_msg *, int);
 extern int		nla_nest_end(struct nl_msg *, struct nlattr *);
-extern void		nla_nest_cancel(struct nl_msg *, struct nlattr *);
+extern int		nla_nest_end_keep_empty(struct nl_msg *, struct nlattr *);
+extern void		nla_nest_cancel(struct nl_msg *, const struct nlattr *);
 extern int		nla_parse_nested(struct nlattr **, int, struct nlattr *,
-					 struct nla_policy *);
-extern int		nla_is_nested(struct nlattr *);
+					 const struct nla_policy *);
+extern int		nla_is_nested(const struct nlattr *);
 
 /**
  * @name Attribute Construction (Exception Based)
@@ -163,6 +183,15 @@
 	} while(0)
 
 /**
+ * Add 8 bit signed integer attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg value		Numeric value.
+ */
+#define NLA_PUT_S8(msg, attrtype, value) \
+	NLA_PUT_TYPE(msg, int8_t, attrtype, value)
+
+/**
  * Add 8 bit integer attribute to netlink message.
  * @arg msg		Netlink message.
  * @arg attrtype	Attribute type.
@@ -172,6 +201,15 @@
 	NLA_PUT_TYPE(msg, uint8_t, attrtype, value)
 
 /**
+ * Add 16 bit signed integer attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg value		Numeric value.
+ */
+#define NLA_PUT_S16(msg, attrtype, value) \
+	NLA_PUT_TYPE(msg, int16_t, attrtype, value)
+
+/**
  * Add 16 bit integer attribute to netlink message.
  * @arg msg		Netlink message.
  * @arg attrtype	Attribute type.
@@ -181,6 +219,15 @@
 	NLA_PUT_TYPE(msg, uint16_t, attrtype, value)
 
 /**
+ * Add 32 bit signed integer attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg value		Numeric value.
+ */
+#define NLA_PUT_S32(msg, attrtype, value) \
+	NLA_PUT_TYPE(msg, int32_t, attrtype, value)
+
+/**
  * Add 32 bit integer attribute to netlink message.
  * @arg msg		Netlink message.
  * @arg attrtype	Attribute type.
@@ -190,6 +237,15 @@
 	NLA_PUT_TYPE(msg, uint32_t, attrtype, value)
 
 /**
+ * Add 64 bit signed integer attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg value		Numeric value.
+ */
+#define NLA_PUT_S64(msg, attrtype, value) \
+	NLA_PUT_TYPE(msg, int64_t, attrtype, value)
+
+/**
  * Add 64 bit integer attribute to netlink message.
  * @arg msg		Netlink message.
  * @arg attrtype	Attribute type.
@@ -272,7 +328,7 @@
  * @arg rem	initialized to len, holds bytes currently remaining in stream
  */
 #define nla_for_each_nested(pos, nla, rem) \
-	for (pos = nla_data(nla), rem = nla_len(nla); \
+	for (pos = (struct nlattr *) nla_data(nla), rem = nla_len(nla); \
 	     nla_ok(pos, rem); \
 	     pos = nla_next(pos, &(rem)))
 
diff --git a/include/netlink/cache.h b/include/netlink/cache.h
index e21aa1c..c0797d0 100644
--- a/include/netlink/cache.h
+++ b/include/netlink/cache.h
@@ -35,6 +35,8 @@
 
 struct nl_cache;
 typedef void (*change_func_t)(struct nl_cache *, struct nl_object *, int, void *);
+typedef void (*change_func_v2_t)(struct nl_cache *, struct nl_object *old_obj,
+	      struct nl_object *new_obj, uint64_t, int, void *);
 
 /**
  * @ingroup cache
@@ -78,6 +80,8 @@
 						struct nl_cache *);
 extern int			nl_cache_pickup(struct nl_sock *,
 						struct nl_cache *);
+extern int			nl_cache_pickup_checkdup(struct nl_sock *,
+						struct nl_cache *);
 extern int			nl_cache_resync(struct nl_sock *,
 						struct nl_cache *,
 						change_func_t,
@@ -86,6 +90,10 @@
 						 struct nl_object *,
 						 change_func_t,
 						 void *);
+extern int			nl_cache_include_v2(struct nl_cache *,
+						    struct nl_object *,
+						    change_func_v2_t,
+						    void *);
 extern void			nl_cache_set_arg1(struct nl_cache *, int);
 extern void			nl_cache_set_arg2(struct nl_cache *, int);
 extern void			nl_cache_set_flags(struct nl_cache *, unsigned int);
@@ -152,6 +160,9 @@
 extern int			nl_cache_mngr_add_cache(struct nl_cache_mngr *mngr,
 							struct nl_cache *cache,
 							change_func_t cb, void *data);
+extern int			nl_cache_mngr_add_cache_v2(struct nl_cache_mngr *mngr,
+							   struct nl_cache *cache,
+							   change_func_v2_t cb, void *data);
 extern int			nl_cache_mngr_get_fd(struct nl_cache_mngr *);
 extern int			nl_cache_mngr_poll(struct nl_cache_mngr *,
 						   int);
@@ -162,6 +173,8 @@
 
 extern void			nl_cache_ops_get(struct nl_cache_ops *);
 extern void			nl_cache_ops_put(struct nl_cache_ops *);
+extern void			nl_cache_ops_set_flags(struct nl_cache_ops *,
+						       unsigned int);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/cli/link.h b/include/netlink/cli/link.h
index 3f37948..f2c720b 100644
--- a/include/netlink/cli/link.h
+++ b/include/netlink/cli/link.h
@@ -17,7 +17,11 @@
 
 extern struct rtnl_link *nl_cli_link_alloc(void);
 extern struct nl_cache *nl_cli_link_alloc_cache_family(struct nl_sock *, int);
+extern struct nl_cache *nl_cli_link_alloc_cache_family_flags(struct nl_sock *, int,
+							     unsigned int);
 extern struct nl_cache *nl_cli_link_alloc_cache(struct nl_sock *);
+extern struct nl_cache *nl_cli_link_alloc_cache_flags(struct nl_sock *,
+						      unsigned int);
 
 extern void nl_cli_link_parse_family(struct rtnl_link *, char *);
 extern void nl_cli_link_parse_name(struct rtnl_link *, char *);
diff --git a/include/netlink/cli/neigh.h b/include/netlink/cli/neigh.h
index 5440012..1c1be91 100644
--- a/include/netlink/cli/neigh.h
+++ b/include/netlink/cli/neigh.h
@@ -15,7 +15,8 @@
 #include <netlink/route/neighbour.h>
 
 #define nl_cli_neigh_alloc_cache(sk) \
-		nl_cli_alloc_cache((sk), "neighbour", rtnl_neigh_alloc_cache)
+		nl_cli_alloc_cache_flags((sk), "neighbour", NL_CACHE_AF_ITER, \
+					 rtnl_neigh_alloc_cache_flags)
 
 extern struct rtnl_neigh *nl_cli_neigh_alloc(void);
 extern void nl_cli_neigh_parse_dst(struct rtnl_neigh *, char *);
diff --git a/include/netlink/cli/utils.h b/include/netlink/cli/utils.h
index da41c10..7d69543 100644
--- a/include/netlink/cli/utils.h
+++ b/include/netlink/cli/utils.h
@@ -22,9 +22,9 @@
 #include <stdint.h>
 #include <ctype.h>
 #include <getopt.h>
-#include <dlfcn.h>
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/select.h>
 
 #include <netlink/netlink.h>
 #include <netlink/utils.h>
@@ -61,8 +61,10 @@
 #endif
 
 extern uint32_t		nl_cli_parse_u32(const char *);
-extern void		nl_cli_print_version(void);
-extern void		nl_cli_fatal(int, const char *, ...);
+extern void		nl_cli_print_version(void)
+			__attribute__((noreturn));
+extern void		nl_cli_fatal(int, const char *, ...)
+			__attribute__((noreturn));
 extern struct nl_addr *	nl_cli_addr_parse(const char *, int);
 extern int		nl_cli_connect(struct nl_sock *, int);
 extern struct nl_sock *	nl_cli_alloc_socket(void);
@@ -73,6 +75,10 @@
 extern struct nl_cache *nl_cli_alloc_cache(struct nl_sock *, const char *,
 			     int (*ac)(struct nl_sock *, struct nl_cache **));
 
+extern struct nl_cache *nl_cli_alloc_cache_flags(struct nl_sock *, const char *,
+			     unsigned int flags,
+			     int (*ac)(struct nl_sock *, struct nl_cache **, unsigned int));
+
 extern void		nl_cli_load_module(const char *, const char *);
 
 #ifdef __cplusplus
diff --git a/include/netlink/data.h b/include/netlink/data.h
index 071159e..45010fe 100644
--- a/include/netlink/data.h
+++ b/include/netlink/data.h
@@ -18,21 +18,24 @@
 extern "C" {
 #endif
 
+struct nlattr;
+
 struct nl_data;
 
 /* General */
-extern struct nl_data *	nl_data_alloc(void *, size_t);
-extern struct nl_data * nl_data_alloc_attr(struct nlattr *);
-extern struct nl_data *	nl_data_clone(struct nl_data *);
-extern int		nl_data_append(struct nl_data *, void *, size_t);
+extern struct nl_data *	nl_data_alloc(const void *, size_t);
+extern struct nl_data * nl_data_alloc_attr(const struct nlattr *);
+extern struct nl_data *	nl_data_clone(const struct nl_data *);
+extern int		nl_data_append(struct nl_data *, const void *, size_t);
 extern void		nl_data_free(struct nl_data *);
 
 /* Access Functions */
-extern void *		nl_data_get(struct nl_data *);
-extern size_t		nl_data_get_size(struct nl_data *);
+extern void *		nl_data_get(const struct nl_data *);
+extern size_t		nl_data_get_size(const struct nl_data *);
 
 /* Misc */
-extern int		nl_data_cmp(struct nl_data *, struct nl_data *);
+extern int		nl_data_cmp(const struct nl_data *,
+				    const struct nl_data *);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/errno.h b/include/netlink/errno.h
index f8b5130..35710cf 100644
--- a/include/netlink/errno.h
+++ b/include/netlink/errno.h
@@ -50,8 +50,9 @@
 #define NLE_NODEV		31
 #define NLE_IMMUTABLE		32
 #define NLE_DUMP_INTR		33
+#define NLE_ATTRSIZE		34
 
-#define NLE_MAX			NLE_DUMP_INTR
+#define NLE_MAX		NLE_ATTRSIZE
 
 extern const char *	nl_geterror(int);
 extern void		nl_perror(int, const char *);
diff --git a/include/netlink/fib_lookup/lookup.h b/include/netlink/fib_lookup/lookup.h
index 8bf27b8..b3c7b5f 100644
--- a/include/netlink/fib_lookup/lookup.h
+++ b/include/netlink/fib_lookup/lookup.h
@@ -35,6 +35,13 @@
 					    struct flnl_request *,
 					    struct nl_cache *);
 
+extern int flnl_result_get_table_id(struct flnl_result *res);
+extern int flnl_result_get_prefixlen(struct flnl_result *res);
+extern int flnl_result_get_nexthop_sel(struct flnl_result *res);
+extern int flnl_result_get_type(struct flnl_result *res);
+extern int flnl_result_get_scope(struct flnl_result *res);
+extern int flnl_result_get_error(struct flnl_result *res);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/netlink/genl/genl.h b/include/netlink/genl/genl.h
index e455581..c4ac137 100644
--- a/include/netlink/genl/genl.h
+++ b/include/netlink/genl/genl.h
@@ -29,9 +29,9 @@
 
 extern int		genlmsg_valid_hdr(struct nlmsghdr *, int);
 extern int		genlmsg_validate(struct nlmsghdr *, int, int,
-					 struct nla_policy *);
+					 const struct nla_policy *);
 extern int		genlmsg_parse(struct nlmsghdr *, int, struct nlattr **,
-				      int, struct nla_policy *);
+				      int, const struct nla_policy *);
 extern struct genlmsghdr *
 			genlmsg_hdr(struct nlmsghdr *);
 extern void *		genlmsg_data(const struct genlmsghdr *);
diff --git a/include/netlink/handlers.h b/include/netlink/handlers.h
index e94cd34..4fac148 100644
--- a/include/netlink/handlers.h
+++ b/include/netlink/handlers.h
@@ -22,10 +22,13 @@
 extern "C" {
 #endif
 
+struct nlmsgerr;
+struct sockaddr_nl;
+struct ucred;
+
 struct nl_cb;
 struct nl_sock;
 struct nl_msg;
-struct ucred;
 
 /**
  * @name Callback Typedefs
diff --git a/include/netlink/hashtable.h b/include/netlink/hashtable.h
index d9e6ee4..3b40d86 100644
--- a/include/netlink/hashtable.h
+++ b/include/netlink/hashtable.h
@@ -12,6 +12,9 @@
 #ifndef NETLINK_HASHTABLE_H_
 #define NETLINK_HASHTABLE_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/include/netlink/idiag/idiagnl.h b/include/netlink/idiag/idiagnl.h
index d7434cd..b69cbf1 100644
--- a/include/netlink/idiag/idiagnl.h
+++ b/include/netlink/idiag/idiagnl.h
@@ -13,13 +13,22 @@
 #define NETLINK_IDIAGNL_H_
 
 #include <netlink/netlink.h>
+#include <linux/sock_diag.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/*************************************************************
+ * The following part contains DEPRECATED names and defines.
+ * Don't use them.
+ *************************************************************/
+
 /**
  * Inet Diag message types
+ *
+ * deprecated: use TCPDIAG_GETSOCK, DCCPDIAG_GETSOCK and
+ * INET_DIAG_GETSOCK_MAX from linux/inet_diag.h
  */
 #define IDIAG_TCPDIAG_GETSOCK	18
 #define IDIAG_DCCPDIAG_GETSOCK	19
@@ -28,80 +37,96 @@
 /**
  * Socket state identifiers
  * @ingroup idiag
+ * @deprecated: use instead the TCP_* defines from netinet/tcp.h.
  */
 enum {
-	IDIAG_SS_UNKNOWN,
-	IDIAG_SS_ESTABLISHED,
-	IDIAG_SS_SYN_SENT,
-	IDIAG_SS_SYN_RECV,
-	IDIAG_SS_FIN_WAIT1,
-	IDIAG_SS_FIN_WAIT2,
-	IDIAG_SS_TIME_WAIT,
-	IDIAG_SS_CLOSE,
-	IDIAG_SS_CLOSE_WAIT,
-	IDIAG_SS_LAST_ACK,
-	IDIAG_SS_LISTEN,
-	IDIAG_SS_CLOSING,
-	IDIAG_SS_MAX
+	IDIAG_SS_UNKNOWN           = 0,
+
+	IDIAG_SS_ESTABLISHED       = 1,  /* TCP_ESTABLISHED */
+	IDIAG_SS_SYN_SENT          = 2,  /* TCP_SYN_SENT */
+	IDIAG_SS_SYN_RECV          = 3,  /* TCP_SYN_RECV */
+	IDIAG_SS_FIN_WAIT1         = 4,  /* TCP_FIN_WAIT1 */
+	IDIAG_SS_FIN_WAIT2         = 5,  /* TCP_FIN_WAIT2 */
+	IDIAG_SS_TIME_WAIT         = 6,  /* TCP_TIME_WAIT */
+	IDIAG_SS_CLOSE             = 7,  /* TCP_CLOSE */
+	IDIAG_SS_CLOSE_WAIT        = 8,  /* TCP_CLOSE_WAIT */
+	IDIAG_SS_LAST_ACK          = 9,  /* TCP_LAST_ACK */
+	IDIAG_SS_LISTEN            = 10, /* TCP_LISTEN */
+	IDIAG_SS_CLOSING           = 11, /* TCP_CLOSING */
+
+	IDIAG_SS_MAX               = 12,
 };
 
 /**
  * Macro to represent all socket states.
  * @ingroup idiag
+ * @deprecated
  */
-#define IDIAG_SS_ALL ((1<<IDIAG_SS_MAX)-1)
+#define IDIAG_SS_ALL  IDIAGNL_SS_ALL
+
 
 /**
  * Inet Diag extended attributes
  * @ingroup idiag
- */
+ * @deprecated These attributes should not be used. They mirror the
+ * INET_DIAG_* extension flags from kernel headers. Use those instead. */
 enum {
-	IDIAG_ATTR_NONE,
-	IDIAG_ATTR_MEMINFO,
-	IDIAG_ATTR_INFO,
-	IDIAG_ATTR_VEGASINFO,
-	IDIAG_ATTR_CONG,
-	IDIAG_ATTR_TOS,
-	IDIAG_ATTR_TCLASS,
-	IDIAG_ATTR_SKMEMINFO,
-	IDIAG_ATTR_SHUTDOWN,
-	IDIAG_ATTR_MAX,
+	IDIAG_ATTR_NONE         = 0, /* INET_DIAG_NONE */
+	IDIAG_ATTR_MEMINFO      = 1, /* INET_DIAG_MEMINFO */
+	IDIAG_ATTR_INFO         = 2, /* INET_DIAG_INFO */
+	IDIAG_ATTR_VEGASINFO    = 3, /* INET_DIAG_VEGASINFO */
+	IDIAG_ATTR_CONG         = 4, /* INET_DIAG_CONG */
+	IDIAG_ATTR_TOS          = 5, /* INET_DIAG_TOS */
+	IDIAG_ATTR_TCLASS       = 6, /* INET_DIAG_TCLASS */
+	IDIAG_ATTR_SKMEMINFO    = 7, /* INET_DIAG_SKMEMINFO */
+	IDIAG_ATTR_SHUTDOWN     = 8, /* INET_DIAG_SHUTDOWN */
+
+	/* IDIAG_ATTR_MAX was wrong, because it did not correspond to
+	 * INET_DIAG_MAX. Anyway, freeze it to the previous value. */
+	IDIAG_ATTR_MAX          = 9,
+
+	IDIAG_ATTR_ALL          = (1<<IDIAG_ATTR_MAX) - 1,
 };
 
-/**
- * Macro to represent all socket attributes.
- * @ingroup idiag
- */
-#define IDIAG_ATTR_ALL ((1<<IDIAG_ATTR_MAX)-1)
+
+/* deprectated keep these only for compatibility, DO NOT USE THEM */
+#define IDIAG_SK_MEMINFO_RMEM_ALLOC                      0  /* SK_MEMINFO_RMEM_ALLOC */
+#define IDIAG_SK_MEMINFO_RCVBUF                          1  /* SK_MEMINFO_RCVBUF */
+#define IDIAG_SK_MEMINFO_WMEM_ALLOC                      2  /* SK_MEMINFO_WMEM_ALLOC */
+#define IDIAG_SK_MEMINFO_SNDBUF                          3  /* SK_MEMINFO_SNDBUF */
+#define IDIAG_SK_MEMINFO_FWD_ALLOC                       4  /* SK_MEMINFO_FWD_ALLOC */
+#define IDIAG_SK_MEMINFO_WMEM_QUEUED                     5  /* SK_MEMINFO_WMEM_QUEUED */
+#define IDIAG_SK_MEMINFO_OPTMEM                          6  /* SK_MEMINFO_OPTMEM */
+#define IDIAG_SK_MEMINFO_BACKLOG                         7  /* SK_MEMINFO_BACKLOG */
+#define IDIAG_SK_MEMINFO_VARS              SK_MEMINFO_VARS
+
+/* deprecated names. */
+#define IDIAG_TIMER_OFF                 IDIAGNL_TIMER_OFF
+#define IDIAG_TIMER_ON                  IDIAGNL_TIMER_ON
+#define IDIAG_TIMER_KEEPALIVE           IDIAGNL_TIMER_KEEPALIVE
+#define IDIAG_TIMER_TIMEWAIT            IDIAGNL_TIMER_TIMEWAIT
+#define IDIAG_TIMER_PERSIST             IDIAGNL_TIMER_PERSIST
+#define IDIAG_TIMER_UNKNOWN             IDIAGNL_TIMER_UNKNOWN
+
+/*************************************************************/
 
 /**
- * Socket memory info identifiers
+ * Macro to represent all socket states.
  * @ingroup idiag
  */
-enum {
-	IDIAG_SK_MEMINFO_RMEM_ALLOC,
-	IDIAG_SK_MEMINFO_RCVBUF,
-	IDIAG_SK_MEMINFO_WMEM_ALLOC,
-	IDIAG_SK_MEMINFO_SNDBUF,
-	IDIAG_SK_MEMINFO_FWD_ALLOC,
-	IDIAG_SK_MEMINFO_WMEM_QUEUED,
-	IDIAG_SK_MEMINFO_OPTMEM,
-	IDIAG_SK_MEMINFO_BACKLOG,
-
-	IDIAG_SK_MEMINFO_VARS,
-};
+#define IDIAGNL_SS_ALL (((1<<12)-1))
 
 /**
  * Socket timer indentifiers
  * @ingroupd idiag
  */
 enum {
-	IDIAG_TIMER_OFF,
-	IDIAG_TIMER_ON,
-	IDIAG_TIMER_KEEPALIVE,
-	IDIAG_TIMER_TIMEWAIT,
-	IDIAG_TIMER_PERSIST,
-	IDIAG_TIMER_UNKNOWN,
+	IDIAGNL_TIMER_OFF               = 0,
+	IDIAGNL_TIMER_ON                = 1,
+	IDIAGNL_TIMER_KEEPALIVE         = 2,
+	IDIAGNL_TIMER_TIMEWAIT          = 3,
+	IDIAGNL_TIMER_PERSIST           = 4,
+	IDIAGNL_TIMER_UNKNOWN           = 5,
 };
 
 extern char *	idiagnl_state2str(int, char *, size_t);
diff --git a/include/netlink/idiag/msg.h b/include/netlink/idiag/msg.h
index 4aae606..01e30db 100644
--- a/include/netlink/idiag/msg.h
+++ b/include/netlink/idiag/msg.h
@@ -19,6 +19,8 @@
 #endif /* __cplusplus */
 
 struct idiagnl_msg;
+
+/* @deprecated: DO NOT USE this variable. */
 extern struct nl_object_ops  idiagnl_msg_obj_ops;
 
 extern struct idiagnl_msg * idiagnl_msg_alloc(void);
diff --git a/include/netlink/idiag/req.h b/include/netlink/idiag/req.h
index 3c9f8ac..b63a4ce 100644
--- a/include/netlink/idiag/req.h
+++ b/include/netlink/idiag/req.h
@@ -43,6 +43,10 @@
 extern struct nl_addr *	    idiagnl_req_get_dst(const struct idiagnl_req *);
 extern int		    idiagnl_req_set_dst(struct idiagnl_req *,
                                                 struct nl_addr *);
+
+extern int		    idiagnl_req_parse(struct nlmsghdr *nlh,
+					      struct idiagnl_req **result);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/include/netlink/list.h b/include/netlink/list.h
index 28712ed..2f20634 100644
--- a/include/netlink/list.h
+++ b/include/netlink/list.h
@@ -12,6 +12,8 @@
 #ifndef NETLINK_LIST_H_
 #define NETLINK_LIST_H_
 
+#include <stddef.h>
+
 struct nl_list_head
 {
 	struct nl_list_head *	next;
@@ -58,8 +60,8 @@
 }
 
 #define nl_container_of(ptr, type, member) ({			\
-        const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
-        (type *)( (char *)__mptr - ((size_t) &((type *)0)->member));})
+        const __typeof__( ((type *)0)->member ) *__mptr = (ptr);\
+        (type *)( (char *)__mptr - (offsetof(type, member)));})
 
 #define nl_list_entry(ptr, type, member) \
 	nl_container_of(ptr, type, member)
@@ -77,15 +79,15 @@
 	nl_list_entry((head)->next, type, member)
 
 #define nl_list_for_each_entry(pos, head, member)				\
-	for (pos = nl_list_entry((head)->next, typeof(*pos), member);	\
+	for (pos = nl_list_entry((head)->next, __typeof__(*pos), member);	\
 	     &(pos)->member != (head); 	\
-	     (pos) = nl_list_entry((pos)->member.next, typeof(*(pos)), member))
+	     (pos) = nl_list_entry((pos)->member.next, __typeof__(*(pos)), member))
 
 #define nl_list_for_each_entry_safe(pos, n, head, member)			\
-	for (pos = nl_list_entry((head)->next, typeof(*pos), member),	\
-		n = nl_list_entry(pos->member.next, typeof(*pos), member);	\
+	for (pos = nl_list_entry((head)->next, __typeof__(*pos), member),	\
+		n = nl_list_entry(pos->member.next, __typeof__(*pos), member);	\
 	     &(pos)->member != (head); 					\
-	     pos = n, n = nl_list_entry(n->member.next, typeof(*n), member))
+	     pos = n, n = nl_list_entry(n->member.next, __typeof__(*n), member))
 
 #define nl_init_list_head(head) \
 	do { (head)->next = (head); (head)->prev = (head); } while (0)
diff --git a/include/netlink/msg.h b/include/netlink/msg.h
index f3d50ae..51d9aeb 100644
--- a/include/netlink/msg.h
+++ b/include/netlink/msg.h
@@ -20,6 +20,8 @@
 extern "C" {
 #endif
 
+struct nlmsghdr;
+
 #define NL_DONTPAD	0
 
 /**
@@ -64,10 +66,10 @@
 extern int		  nlmsg_ok(const struct nlmsghdr *, int);
 extern struct nlmsghdr *  nlmsg_next(struct nlmsghdr *, int *);
 extern int		  nlmsg_parse(struct nlmsghdr *, int, struct nlattr **,
-				      int, struct nla_policy *);
+				      int, const struct nla_policy *);
 extern struct nlattr *	  nlmsg_find_attr(struct nlmsghdr *, int, int);
 extern int		  nlmsg_validate(struct nlmsghdr *, int, int,
-					 struct nla_policy *);
+					 const struct nla_policy *);
 
 extern struct nl_msg *	  nlmsg_alloc(void);
 extern struct nl_msg *	  nlmsg_alloc_size(size_t);
diff --git a/include/netlink/netfilter/exp.h b/include/netlink/netfilter/exp.h
index 4e95014..736af24 100644
--- a/include/netlink/netfilter/exp.h
+++ b/include/netlink/netfilter/exp.h
@@ -82,7 +82,10 @@
 
 extern void nfnl_exp_set_flags(struct nfnl_exp *, uint32_t);
 extern int  nfnl_exp_test_flags(const struct nfnl_exp *);
+extern void nfnl_exp_unset_flags(struct nfnl_exp *exp, uint32_t flags);
 extern uint32_t nfnl_exp_get_flags(const struct nfnl_exp *);
+extern char * nfnl_exp_flags2str(int flags, char *buf, size_t len);
+int nfnl_exp_str2flags(const char *name);
 
 extern void nfnl_exp_set_class(struct nfnl_exp *, uint32_t);
 extern int  nfnl_exp_test_class(const struct nfnl_exp *);
diff --git a/include/netlink/netfilter/log.h b/include/netlink/netfilter/log.h
index 2002fa8..e48eddf 100644
--- a/include/netlink/netfilter/log.h
+++ b/include/netlink/netfilter/log.h
@@ -58,7 +58,7 @@
 
 extern char *			nfnl_log_copy_mode2str(enum nfnl_log_copy_mode,
 						       char *, size_t);
-extern enum nfnl_log_copy_mode	nfnl_log_str2copy_mode(const char *);
+extern int			nfnl_log_str2copy_mode(const char *);
 
 extern void			nfnl_log_set_copy_range(struct nfnl_log *, uint32_t);
 extern int			nfnl_log_test_copy_range(const struct nfnl_log *);
diff --git a/include/netlink/netfilter/queue.h b/include/netlink/netfilter/queue.h
index 664610d..224d469 100644
--- a/include/netlink/netfilter/queue.h
+++ b/include/netlink/netfilter/queue.h
@@ -54,7 +54,7 @@
 
 extern char *			nfnl_queue_copy_mode2str(enum nfnl_queue_copy_mode,
 							 char *, size_t);
-extern enum nfnl_queue_copy_mode nfnl_queue_str2copy_mode(const char *);
+extern int			nfnl_queue_str2copy_mode(const char *);
 
 extern void			nfnl_queue_set_copy_range(struct nfnl_queue *,
 							  uint32_t);
diff --git a/include/netlink/netfilter/queue_msg.h b/include/netlink/netfilter/queue_msg.h
index 9befee7..86f4b86 100644
--- a/include/netlink/netfilter/queue_msg.h
+++ b/include/netlink/netfilter/queue_msg.h
@@ -93,6 +93,8 @@
 extern struct nl_msg *		nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *);
 extern int			nfnl_queue_msg_send_verdict(struct nl_sock *,
 							    const struct nfnl_queue_msg *);
+
+extern struct nl_msg *		nfnl_queue_msg_build_verdict_batch(const struct nfnl_queue_msg *msg);
 extern int			nfnl_queue_msg_send_verdict_batch(struct nl_sock *,
 							    const struct nfnl_queue_msg *);
 extern int			nfnl_queue_msg_send_verdict_payload(struct nl_sock *,
diff --git a/include/netlink/netlink-compat.h b/include/netlink/netlink-compat.h
index 17ec9fc..2839ed0 100644
--- a/include/netlink/netlink-compat.h
+++ b/include/netlink/netlink-compat.h
@@ -47,4 +47,8 @@
 #define AF_LLC		26
 #endif
 
+#ifndef AF_MPLS
+#define AF_MPLS		28
+#endif
+
 #endif
diff --git a/include/netlink/netlink.h b/include/netlink/netlink.h
index 28dba06..41d48c6 100644
--- a/include/netlink/netlink.h
+++ b/include/netlink/netlink.h
@@ -16,7 +16,7 @@
 #include <stdint.h>
 #include <string.h>
 #include <stdlib.h>
-#include <sys/poll.h>
+#include <poll.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/time.h>
@@ -38,6 +38,7 @@
 extern "C" {
 #endif
 
+struct nlmsghdr;
 struct ucred;
 struct nl_cache_ops;
 struct nl_parser_param;
@@ -87,6 +88,11 @@
 						struct nlmsghdr *,
 						struct nl_parser_param *),
 					  struct nl_object **);
+extern int                      nl_pickup_keep_syserr(struct nl_sock *sk,
+                                                      int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *,
+                                                                    struct nlmsghdr *, struct nl_parser_param *),
+                                                      struct nl_object **result,
+                                                      int *syserror);
 /* Netlink Family Translations */
 extern char *			nl_nlfamily2str(int, char *, size_t);
 extern int			nl_str2nlfamily(const char *);
diff --git a/include/netlink/object.h b/include/netlink/object.h
index a95feda..b0c32c9 100644
--- a/include/netlink/object.h
+++ b/include/netlink/object.h
@@ -43,6 +43,8 @@
 						    struct nl_object *);
 extern uint32_t			nl_object_diff(struct nl_object *,
 					       struct nl_object *);
+extern uint64_t			nl_object_diff64(struct nl_object *,
+						 struct nl_object *);
 extern int			nl_object_match_filter(struct nl_object *,
 						       struct nl_object *);
 extern char *			nl_object_attrs2str(struct nl_object *,
diff --git a/include/netlink/route/act/gact.h b/include/netlink/route/act/gact.h
new file mode 100644
index 0000000..9538711
--- /dev/null
+++ b/include/netlink/route/act/gact.h
@@ -0,0 +1,31 @@
+/*
+ * netlink/route/act/gact.h	gact action
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2016 Sushma Sitaram <sushma.sitaram@intel.com>
+ */
+
+#ifndef NETLINK_GACT_H_
+#define NETLINK_GACT_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/route/action.h>
+#include <linux/tc_act/tc_gact.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int rtnl_gact_set_action(struct rtnl_act *act, int action);
+extern int rtnl_gact_get_action(struct rtnl_act *act);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/act/skbedit.h b/include/netlink/route/act/skbedit.h
new file mode 100644
index 0000000..69829e8
--- /dev/null
+++ b/include/netlink/route/act/skbedit.h
@@ -0,0 +1,37 @@
+/*
+ * netlink/route/act/skbedit.h	skbedit action
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2015 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+#ifndef NETLINK_SKBEDIT_H_
+#define NETLINK_SKBEDIT_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/route/action.h>
+#include <linux/tc_act/tc_skbedit.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int rtnl_skbedit_set_action(struct rtnl_act *act, int action);
+extern int rtnl_skbedit_get_action(struct rtnl_act *act);
+extern int rtnl_skbedit_set_queue_mapping(struct rtnl_act *act, uint16_t index);
+extern int rtnl_skbedit_get_queue_mapping(struct rtnl_act *act, uint16_t *index);
+extern int rtnl_skbedit_set_mark(struct rtnl_act *act, uint32_t mark);
+extern int rtnl_skbedit_get_mark(struct rtnl_act *act, uint32_t *mark);
+extern int rtnl_skbedit_set_priority(struct rtnl_act *act, uint32_t prio);
+extern int rtnl_skbedit_get_priority(struct rtnl_act *act, uint32_t *prio);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/act/vlan.h b/include/netlink/route/act/vlan.h
new file mode 100644
index 0000000..3dcce7c
--- /dev/null
+++ b/include/netlink/route/act/vlan.h
@@ -0,0 +1,38 @@
+/*
+ * netlink/route/act/vlan.h     vlan action
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2018 Volodymyr Bendiuga <volodymyr.bendiuga@gmail.com>
+ */
+
+#ifndef NETLINK_VLAN_H_
+#define NETLINK_VLAN_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/route/action.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int rtnl_vlan_set_mode(struct rtnl_act *act, int mode);
+extern int rtnl_vlan_get_mode(struct rtnl_act *act, int *out_mode);
+extern int rtnl_vlan_set_action(struct rtnl_act *act, int action);
+extern int rtnl_vlan_get_action(struct rtnl_act *act, int *out_action);
+extern int rtnl_vlan_set_protocol(struct rtnl_act *act, uint16_t protocol);
+extern int rtnl_vlan_get_protocol(struct rtnl_act *act, uint16_t *out_protocol);
+extern int rtnl_vlan_set_vlan_id(struct rtnl_act *act, uint16_t vid);
+extern int rtnl_vlan_get_vlan_id(struct rtnl_act *act, uint16_t *out_vid);
+extern int rtnl_vlan_set_vlan_prio(struct rtnl_act *act, uint8_t prio);
+extern int rtnl_vlan_get_vlan_prio(struct rtnl_act *act, uint8_t *out_prio);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NETLINK_VLAN_H_ */
diff --git a/include/netlink/route/action.h b/include/netlink/route/action.h
index 054bdd8..7d4c185 100644
--- a/include/netlink/route/action.h
+++ b/include/netlink/route/action.h
@@ -22,11 +22,13 @@
 #endif
 
 extern struct rtnl_act *rtnl_act_alloc(void);
+extern struct rtnl_act *rtnl_act_next(struct rtnl_act *);
 extern void		rtnl_act_get(struct rtnl_act *);
 extern void		rtnl_act_put(struct rtnl_act *);
 extern int		rtnl_act_build_add_request(struct rtnl_act *, int,
 						   struct nl_msg **);
 extern int		rtnl_act_add(struct nl_sock *, struct rtnl_act *, int);
+extern int		rtnl_act_change(struct nl_sock *, struct rtnl_act *, int);
 
 extern int		rtnl_act_build_change_request(struct rtnl_act *, int,
 						      struct nl_msg **);
diff --git a/include/netlink/route/class.h b/include/netlink/route/class.h
index e73b60a..3833339 100644
--- a/include/netlink/route/class.h
+++ b/include/netlink/route/class.h
@@ -31,6 +31,9 @@
 extern struct rtnl_class *
 			rtnl_class_get(struct nl_cache *, int, uint32_t);
 
+extern struct rtnl_class *
+			rtnl_class_get_by_parent(struct nl_cache *, int, uint32_t);
+
 extern struct rtnl_qdisc *
 			rtnl_class_leaf_qdisc(struct rtnl_class *,
 						      struct nl_cache *);
diff --git a/include/netlink/route/classifier.h b/include/netlink/route/classifier.h
index a8c1179..18832ad 100644
--- a/include/netlink/route/classifier.h
+++ b/include/netlink/route/classifier.h
@@ -27,6 +27,8 @@
 extern int		rtnl_cls_alloc_cache(struct nl_sock *, int, uint32_t,
 					     struct nl_cache **);
 
+extern void 		rtnl_cls_cache_set_tc_params(struct nl_cache *, int, uint32_t);
+
 extern int		rtnl_cls_build_add_request(struct rtnl_cls *, int,
 						   struct nl_msg **);
 extern int		rtnl_cls_add(struct nl_sock *, struct rtnl_cls *, int);
diff --git a/include/netlink/route/cls/basic.h b/include/netlink/route/cls/basic.h
index f00793c..51232ae 100644
--- a/include/netlink/route/cls/basic.h
+++ b/include/netlink/route/cls/basic.h
@@ -28,6 +28,7 @@
 extern struct rtnl_ematch_tree *rtnl_basic_get_ematch(struct rtnl_cls *);
 extern int rtnl_basic_add_action(struct rtnl_cls *, struct rtnl_act *);
 extern int rtnl_basic_del_action(struct rtnl_cls *, struct rtnl_act *);
+extern struct rtnl_act* rtnl_basic_get_action(struct rtnl_cls *);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/route/cls/ematch.h b/include/netlink/route/cls/ematch.h
index 13f9c32..f4dac1d 100644
--- a/include/netlink/route/cls/ematch.h
+++ b/include/netlink/route/cls/ematch.h
@@ -73,6 +73,8 @@
 extern void			rtnl_ematch_tree_add(struct rtnl_ematch_tree *,
 						     struct rtnl_ematch *);
 
+extern struct rtnl_ematch_tree *rtnl_ematch_tree_clone(struct rtnl_ematch_tree *);
+
 extern int			rtnl_ematch_parse_attr(struct nlattr *,
 						       struct rtnl_ematch_tree **);
 extern int			rtnl_ematch_fill_attr(struct nl_msg *, int,
diff --git a/include/netlink/route/cls/ematch/cmp.h b/include/netlink/route/cls/ematch/cmp.h
index 308113e..7afb792 100644
--- a/include/netlink/route/cls/ematch/cmp.h
+++ b/include/netlink/route/cls/ematch/cmp.h
@@ -20,6 +20,8 @@
 extern "C" {
 #endif
 
+struct tcf_em_cmp;
+
 extern void	rtnl_ematch_cmp_set(struct rtnl_ematch *,
 				    struct tcf_em_cmp *);
 extern struct tcf_em_cmp *
diff --git a/include/netlink/route/cls/matchall.h b/include/netlink/route/cls/matchall.h
new file mode 100644
index 0000000..1955694
--- /dev/null
+++ b/include/netlink/route/cls/matchall.h
@@ -0,0 +1,36 @@
+/*
+ * netlink/route/cls/matchall.h	matchall classifier
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2017 Volodymyr Bendiuga <volodymyr.bendiuga@gmail.com>
+ */
+
+#ifndef NETLINK_MATCHALL_H_
+#define NETLINK_MATCHALL_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/action.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int	rtnl_mall_set_classid(struct rtnl_cls *, uint32_t);
+extern int	rtnl_mall_get_classid(struct rtnl_cls *, uint32_t *);
+extern int	rtnl_mall_set_flags(struct rtnl_cls *, uint32_t);
+extern int	rtnl_mall_get_flags(struct rtnl_cls *, uint32_t *);
+extern int	rtnl_mall_append_action(struct rtnl_cls *, struct rtnl_act *);
+extern struct rtnl_act *rtnl_mall_get_first_action(struct rtnl_cls *);
+extern int	rtnl_mall_del_action(struct rtnl_cls *, struct rtnl_act *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/cls/u32.h b/include/netlink/route/cls/u32.h
index f35d37a..2443f51 100644
--- a/include/netlink/route/cls/u32.h
+++ b/include/netlink/route/cls/u32.h
@@ -23,13 +23,17 @@
 
 extern void	rtnl_u32_set_handle(struct rtnl_cls *, int, int, int);
 extern int	rtnl_u32_set_classid(struct rtnl_cls *, uint32_t);
+extern int	rtnl_u32_get_classid(struct rtnl_cls *, uint32_t *);
 extern int	rtnl_u32_set_divisor(struct rtnl_cls *, uint32_t);
 extern int	rtnl_u32_set_link(struct rtnl_cls *, uint32_t);
 extern int	rtnl_u32_set_hashtable(struct rtnl_cls *, uint32_t);
 extern int	rtnl_u32_set_hashmask(struct rtnl_cls *, uint32_t, uint32_t);
+extern int	rtnl_u32_set_selector(struct rtnl_cls *, int, uint32_t, char, uint16_t, char);
 extern int	rtnl_u32_set_cls_terminal(struct rtnl_cls *);
 
 extern int	rtnl_u32_set_flags(struct rtnl_cls *, int);
+extern int	rtnl_u32_add_mark(struct rtnl_cls *, uint32_t, uint32_t);
+extern int	rtnl_u32_del_mark(struct rtnl_cls *);
 extern int	rtnl_u32_add_key(struct rtnl_cls *, uint32_t, uint32_t,
 				 int, int);
 extern int	rtnl_u32_get_key(struct rtnl_cls *, uint8_t, uint32_t *, uint32_t *,
@@ -46,6 +50,7 @@
 					  uint8_t, int, int);
 extern int	rtnl_u32_add_action(struct rtnl_cls *, struct rtnl_act *);
 extern int	rtnl_u32_del_action(struct rtnl_cls *, struct rtnl_act *);
+extern struct rtnl_act* rtnl_u32_get_action(struct rtnl_cls *);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/route/link.h b/include/netlink/route/link.h
index a7aa88b..8fd0994 100644
--- a/include/netlink/route/link.h
+++ b/include/netlink/route/link.h
@@ -15,7 +15,6 @@
 #include <netlink/netlink.h>
 #include <netlink/cache.h>
 #include <netlink/addr.h>
-#include <linux/if.h>
 #include <sys/types.h>
 
 #ifdef __cplusplus
@@ -99,6 +98,7 @@
 	RTNL_LINK_IP6_ECT1PKTS,		/*!< IPv6 SNMP InECT1Pkts */
 	RTNL_LINK_IP6_ECT0PKTS,		/*!< IPv6 SNMP InECT0Pkts */
 	RTNL_LINK_IP6_CEPKTS,		/*!< IPv6 SNMP InCEPkts */
+	RTNL_LINK_RX_NOHANDLER,		/*!< Received packets dropped on inactive device */
 	__RTNL_LINK_STATS_MAX,
 } rtnl_link_stat_id_t;
 
@@ -110,6 +110,9 @@
 extern void	rtnl_link_put(struct rtnl_link *);
 
 extern int	rtnl_link_alloc_cache(struct nl_sock *, int, struct nl_cache **);
+extern int	rtnl_link_alloc_cache_flags(struct nl_sock *, int,
+					    struct nl_cache **,
+					    unsigned int flags);
 extern struct rtnl_link *rtnl_link_get(struct nl_cache *, int);
 extern struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *, const char *);
 
@@ -197,12 +200,17 @@
 extern void	rtnl_link_set_carrier(struct rtnl_link *, uint8_t);
 extern uint8_t	rtnl_link_get_carrier(struct rtnl_link *);
 
+extern int	rtnl_link_get_carrier_changes(struct rtnl_link *, uint32_t *);
+
 extern void	rtnl_link_set_operstate(struct rtnl_link *, uint8_t);
 extern uint8_t	rtnl_link_get_operstate(struct rtnl_link *);
 
 extern void	rtnl_link_set_linkmode(struct rtnl_link *, uint8_t);
 extern uint8_t	rtnl_link_get_linkmode(struct rtnl_link *);
 
+int             rtnl_link_set_link_netnsid(struct rtnl_link *link, int32_t link_netnsid);
+int             rtnl_link_get_link_netnsid(const struct rtnl_link *link, int32_t *out_link_netnsid);
+
 extern const char *	rtnl_link_get_ifalias(struct rtnl_link *);
 extern void		rtnl_link_set_ifalias(struct rtnl_link *, const char *);
 
@@ -215,6 +223,9 @@
 extern int	rtnl_link_set_type(struct rtnl_link *, const char *);
 extern char *	rtnl_link_get_type(struct rtnl_link *);
 
+extern int		rtnl_link_set_slave_type(struct rtnl_link *, const char *);
+extern const char *	rtnl_link_get_slave_type(const struct rtnl_link *);
+
 extern void	rtnl_link_set_promiscuity(struct rtnl_link *, uint32_t);
 extern uint32_t	rtnl_link_get_promiscuity(struct rtnl_link *);
 
@@ -224,8 +235,16 @@
 extern void	rtnl_link_set_num_rx_queues(struct rtnl_link *, uint32_t);
 extern uint32_t	rtnl_link_get_num_rx_queues(struct rtnl_link *);
 
+extern int	rtnl_link_get_gso_max_segs(struct rtnl_link *, uint32_t *);
+
+extern int	rtnl_link_get_gso_max_size(struct rtnl_link *, uint32_t *);
+
 extern struct nl_data *	rtnl_link_get_phys_port_id(struct rtnl_link *);
 
+extern char*	rtnl_link_get_phys_port_name(struct rtnl_link *);
+
+extern struct nl_data *	rtnl_link_get_phys_switch_id(struct rtnl_link *);
+
 extern void	rtnl_link_set_ns_fd(struct rtnl_link *, int);
 extern int	rtnl_link_get_ns_fd(struct rtnl_link *);
 extern void	rtnl_link_set_ns_pid(struct rtnl_link *, pid_t);
@@ -239,6 +258,10 @@
 extern int	rtnl_link_fill_info(struct nl_msg *, struct rtnl_link *);
 extern int	rtnl_link_info_parse(struct rtnl_link *, struct nlattr **);
 
+extern int rtnl_link_has_vf_list(struct rtnl_link *);
+extern void rtnl_link_set_vf_list(struct rtnl_link *);
+extern void rtnl_link_unset_vf_list(struct rtnl_link *);
+
 
 /* deprecated */
 extern int	rtnl_link_set_info_type(struct rtnl_link *, const char *) __attribute__((deprecated));
diff --git a/include/netlink/route/link/bridge.h b/include/netlink/route/link/bridge.h
index 16a4505..f2e16e3 100644
--- a/include/netlink/route/link/bridge.h
+++ b/include/netlink/route/link/bridge.h
@@ -19,6 +19,16 @@
 extern "C" {
 #endif
 
+#define RTNL_LINK_BRIDGE_VLAN_BITMAP_MAX 4096
+#define RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN (RTNL_LINK_BRIDGE_VLAN_BITMAP_MAX / 32)
+
+struct rtnl_link_bridge_vlan
+{
+	uint16_t                pvid;
+	uint32_t                vlan_bitmap[RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN];
+	uint32_t                untagged_bitmap[RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN];
+};
+
 /**
  * Bridge flags
  * @ingroup bridge
@@ -28,8 +38,16 @@
 	RTNL_BRIDGE_BPDU_GUARD		= 0x0002,
 	RTNL_BRIDGE_ROOT_BLOCK		= 0x0004,
 	RTNL_BRIDGE_FAST_LEAVE		= 0x0008,
+	RTNL_BRIDGE_UNICAST_FLOOD	= 0x0010,
+	RTNL_BRIDGE_LEARNING		= 0x0020,
+	RTNL_BRIDGE_LEARNING_SYNC	= 0x0040,
 };
 
+#define RTNL_BRIDGE_HWMODE_VEB BRIDGE_MODE_VEB
+#define RTNL_BRIDGE_HWMODE_VEPA BRIDGE_MODE_VEPA
+#define RTNL_BRIDGE_HWMODE_MAX BRIDGE_MODE_VEPA
+#define RTNL_BRIDGE_HWMODE_UNDEF BRIDGE_MODE_UNDEF
+
 extern struct rtnl_link *rtnl_link_bridge_alloc(void);
 
 extern int	rtnl_link_is_bridge(struct rtnl_link *);
@@ -48,10 +66,26 @@
 extern int	rtnl_link_bridge_set_flags(struct rtnl_link *, unsigned int);
 extern int	rtnl_link_bridge_get_flags(struct rtnl_link *);
 
+extern int	rtnl_link_bridge_set_self(struct rtnl_link *);
+
+extern int	rtnl_link_bridge_get_hwmode(struct rtnl_link *, uint16_t *);
+extern int	rtnl_link_bridge_set_hwmode(struct rtnl_link *, uint16_t);
+
 extern char * rtnl_link_bridge_flags2str(int, char *, size_t);
 extern int	rtnl_link_bridge_str2flags(const char *);
 
+extern char * rtnl_link_bridge_portstate2str(int, char *, size_t);
+extern int  rtnl_link_bridge_str2portstate(const char *);
+
+extern char * rtnl_link_bridge_hwmode2str(uint16_t, char *, size_t);
+extern uint16_t rtnl_link_bridge_str2hwmode(const char *);
+
 extern int	rtnl_link_bridge_add(struct nl_sock *sk, const char *name);
+
+extern int	rtnl_link_bridge_pvid(struct rtnl_link *link);
+extern int	rtnl_link_bridge_has_vlan(struct rtnl_link *link);
+
+extern struct rtnl_link_bridge_vlan *rtnl_link_bridge_get_port_vlan(struct rtnl_link *link);
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/netlink/route/link/can.h b/include/netlink/route/link/can.h
index 61c9f47..1979a71 100644
--- a/include/netlink/route/link/can.h
+++ b/include/netlink/route/link/can.h
@@ -20,6 +20,10 @@
 extern "C" {
 #endif
 
+struct can_bittiming_const;
+struct can_bittiming;
+struct can_berr_counter;
+
 extern int rtnl_link_is_can(struct rtnl_link *link);
 
 extern char *rtnl_link_can_ctrlmode2str(int, char *, size_t);
diff --git a/include/netlink/route/link/geneve.h b/include/netlink/route/link/geneve.h
new file mode 100644
index 0000000..aaba1f9
--- /dev/null
+++ b/include/netlink/route/link/geneve.h
@@ -0,0 +1,60 @@
+/*
+ * netlink/route/link/geneve.h          GENEVE interface
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation version 2.1
+ *      of the License.
+ */
+
+#ifndef NETLINK_LINK_GENEVE_H_
+#define NETLINK_LINK_GENEVE_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTNL_GENEVE_ID_MAX 16777215
+
+#define RTNL_LINK_GENEVE_F_COLLECT_METADATA     (1<<0)
+
+extern struct rtnl_link *rtnl_link_geneve_alloc(void);
+extern int rtnl_link_is_geneve(struct rtnl_link *);
+
+extern int rtnl_link_geneve_set_id(struct rtnl_link *, uint32_t);
+extern int rtnl_link_geneve_get_id(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_geneve_set_remote(struct rtnl_link *, struct nl_addr *);
+extern int rtnl_link_geneve_get_remote(struct rtnl_link *, struct nl_addr **);
+
+extern int rtnl_link_geneve_set_ttl(struct rtnl_link *, uint8_t);
+extern int rtnl_link_geneve_get_ttl(struct rtnl_link *);
+
+extern int rtnl_link_geneve_set_tos(struct rtnl_link *, uint8_t);
+extern int rtnl_link_geneve_get_tos(struct rtnl_link *);
+
+extern int rtnl_link_geneve_set_port(struct rtnl_link *, uint32_t);
+extern int rtnl_link_geneve_get_port(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_geneve_set_label(struct rtnl_link *, uint32_t);
+extern int rtnl_link_geneve_get_label(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_geneve_set_udp_csum(struct rtnl_link *, uint8_t);
+extern int rtnl_link_geneve_get_udp_csum(struct rtnl_link *);
+
+extern int rtnl_link_geneve_set_udp_zero_csum6_tx(struct rtnl_link *, uint8_t);
+extern int rtnl_link_geneve_get_udp_zero_csum6_tx(struct rtnl_link *);
+
+extern int rtnl_link_geneve_set_udp_zero_csum6_rx(struct rtnl_link *, uint8_t);
+extern int rtnl_link_geneve_get_udp_zero_csum6_rx(struct rtnl_link *);
+
+extern int rtnl_link_geneve_set_flags(struct rtnl_link *, uint8_t flags, int enable);
+extern int rtnl_link_geneve_get_flags(struct rtnl_link *, uint8_t *flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/inet6.h b/include/netlink/route/link/inet6.h
new file mode 100644
index 0000000..666a9b8
--- /dev/null
+++ b/include/netlink/route/link/inet6.h
@@ -0,0 +1,53 @@
+/*
+ * netlink/route/link/inet6.h	INET6 Link Module
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2014 Dan Williams <dcbw@redhat.com>
+ */
+
+#ifndef NETLINK_LINK_INET6_H_
+#define NETLINK_LINK_INET6_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char *		rtnl_link_inet6_addrgenmode2str  (uint8_t mode,
+							  char *buf,
+							  size_t len);
+
+uint8_t			rtnl_link_inet6_str2addrgenmode  (const char *mode);
+
+extern int		rtnl_link_inet6_get_token(struct rtnl_link *,
+						  struct nl_addr **);
+
+extern int		rtnl_link_inet6_set_token(struct rtnl_link *,
+						  struct nl_addr *);
+
+extern int		rtnl_link_inet6_get_addr_gen_mode(struct rtnl_link *,
+							  uint8_t *);
+
+extern int		rtnl_link_inet6_set_addr_gen_mode(struct rtnl_link *,
+							  uint8_t);
+
+extern int		rtnl_link_inet6_get_flags(struct rtnl_link *,
+							  uint32_t *);
+
+extern int		rtnl_link_inet6_set_flags(struct rtnl_link *,
+							  uint32_t);
+
+/* Link Flags Translations */
+extern char *	rtnl_link_inet6_flags2str(int, char *, size_t);
+extern int		rtnl_link_inet6_str2flags(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/ip6tnl.h b/include/netlink/route/link/ip6tnl.h
index 7e0c295..87ab164 100644
--- a/include/netlink/route/link/ip6tnl.h
+++ b/include/netlink/route/link/ip6tnl.h
@@ -22,6 +22,8 @@
 	extern struct rtnl_link *rtnl_link_ip6_tnl_alloc(void);
 	extern int rtnl_link_ip6_tnl_add(struct nl_sock *sk, const char *name);
 
+	extern int rtnl_link_is_ip6_tnl(struct rtnl_link *link);
+
 	extern int rtnl_link_ip6_tnl_set_link(struct rtnl_link *link,  uint32_t index);
 	extern uint32_t rtnl_link_ip6_tnl_get_link(struct rtnl_link *link);
 
@@ -49,7 +51,7 @@
 	extern int rtnl_link_ip6_tnl_set_proto(struct rtnl_link *link, uint8_t proto);
 	extern uint8_t rtnl_link_ip6_tnl_get_proto(struct rtnl_link *link);
 
-#ifdef _cplusplus
+#ifdef __cplusplus
 }
 #endif
 
diff --git a/include/netlink/route/link/ipgre.h b/include/netlink/route/link/ipgre.h
index 5a0a295..4c5f86b 100644
--- a/include/netlink/route/link/ipgre.h
+++ b/include/netlink/route/link/ipgre.h
@@ -19,8 +19,13 @@
 extern "C" {
 #endif
 
+	extern int rtnl_link_is_ipgre(struct rtnl_link *link);
+	extern int rtnl_link_is_ipgretap(struct rtnl_link *link);
+
 	extern struct rtnl_link *rtnl_link_ipgre_alloc(void);
+	extern struct rtnl_link *rtnl_link_ipgretap_alloc(void);
 	extern int rtnl_link_ipgre_add(struct nl_sock *sk, const char *name);
+	extern int rtnl_link_ipgretap_add(struct nl_sock *sk, const char *name);
 
 	extern int rtnl_link_ipgre_set_link(struct rtnl_link *link,  uint32_t index);
 	extern uint32_t rtnl_link_ipgre_get_link(struct rtnl_link *link);
diff --git a/include/netlink/route/link/ipip.h b/include/netlink/route/link/ipip.h
index ccadb87..a7f5158 100644
--- a/include/netlink/route/link/ipip.h
+++ b/include/netlink/route/link/ipip.h
@@ -18,10 +18,11 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
-
 	extern struct rtnl_link *rtnl_link_ipip_alloc(void);
 	extern int rtnl_link_ipip_add(struct nl_sock *sk, const char *name);
 
+	extern int rtnl_link_is_ipip(struct rtnl_link *link);
+
 	extern uint32_t rtnl_link_ipip_get_link(struct rtnl_link *link);
 	extern int rtnl_link_ipip_set_link(struct rtnl_link *link,  uint32_t index);
 
diff --git a/include/netlink/route/link/ipvlan.h b/include/netlink/route/link/ipvlan.h
new file mode 100644
index 0000000..d13bcbb
--- /dev/null
+++ b/include/netlink/route/link/ipvlan.h
@@ -0,0 +1,37 @@
+/*
+ * netlink/route/link/ipvlan.h		IPVLAN interface
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2015 Cong Wang <cwang@twopensource.com>
+ */
+
+#ifndef NETLINK_LINK_IPVLAN_H_
+#define NETLINK_LINK_IPVLAN_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct rtnl_link *rtnl_link_ipvlan_alloc(void);
+
+extern int		rtnl_link_is_ipvlan(struct rtnl_link *);
+
+extern char *		rtnl_link_ipvlan_mode2str(int, char *, size_t);
+extern int		rtnl_link_ipvlan_str2mode(const char *);
+
+extern int		rtnl_link_ipvlan_set_mode(struct rtnl_link *,
+			                           uint16_t);
+extern int		rtnl_link_ipvlan_get_mode(struct rtnl_link *, uint16_t *out_mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/ipvti.h b/include/netlink/route/link/ipvti.h
index a3e7bba..c97e57f 100644
--- a/include/netlink/route/link/ipvti.h
+++ b/include/netlink/route/link/ipvti.h
@@ -21,20 +21,22 @@
 	extern struct rtnl_link *rtnl_link_ipvti_alloc(void);
 	extern int rtnl_link_ipvti_add(struct nl_sock *sk, const char *name);
 
+	extern int rtnl_link_is_ipvti(struct rtnl_link *link);
+
 	extern int rtnl_link_ipvti_set_link(struct rtnl_link *link,  uint32_t index);
 	extern uint32_t rtnl_link_ipvti_get_link(struct rtnl_link *link);
 
 	extern int rtnl_link_ipvti_set_ikey(struct rtnl_link *link, uint32_t ikey);
-	extern uint32_t rtnl_link_get_ikey(struct rtnl_link *link);
+	extern uint32_t rtnl_link_ipvti_get_ikey(struct rtnl_link *link);
 
 	extern int rtnl_link_ipvti_set_okey(struct rtnl_link *link, uint32_t okey);
-	extern uint32_t rtnl_link_get_okey(struct rtnl_link *link);
+	extern uint32_t rtnl_link_ipvti_get_okey(struct rtnl_link *link);
 
 	extern int rtnl_link_ipvti_set_local(struct rtnl_link *link, uint32_t addr);
-	extern uint32_t rtnl_link_get_local(struct rtnl_link *link);
+	extern uint32_t rtnl_link_ipvti_get_local(struct rtnl_link *link);
 
 	extern int rtnl_link_ipvti_set_remote(struct rtnl_link *link, uint32_t addr);
-	extern uint32_t rtnl_link_get_remote(struct rtnl_link *link);
+	extern uint32_t rtnl_link_ipvti_get_remote(struct rtnl_link *link);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/route/link/macsec.h b/include/netlink/route/link/macsec.h
new file mode 100644
index 0000000..ace4de2
--- /dev/null
+++ b/include/netlink/route/link/macsec.h
@@ -0,0 +1,75 @@
+/*
+ * netlink/route/link/macsec.h		MACsec Link Info
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2016 Sabrina Dubroca <sd@queasysnail.net>
+ */
+
+#ifndef NETLINK_LINK_MACSEC_H_
+#define NETLINK_LINK_MACSEC_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <linux/if_link.h>
+#include <linux/if_macsec.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum macsec_validation_type;
+
+struct rtnl_link *rtnl_link_macsec_alloc(void);
+
+int rtnl_link_macsec_set_sci(struct rtnl_link *, uint64_t);
+int rtnl_link_macsec_get_sci(struct rtnl_link *, uint64_t *);
+
+int rtnl_link_macsec_set_port(struct rtnl_link *, uint16_t);
+int rtnl_link_macsec_get_port(struct rtnl_link *, uint16_t *);
+
+int rtnl_link_macsec_set_cipher_suite(struct rtnl_link *, uint64_t);
+int rtnl_link_macsec_get_cipher_suite(struct rtnl_link *, uint64_t *);
+
+int rtnl_link_macsec_set_icv_len(struct rtnl_link *, uint16_t);
+int rtnl_link_macsec_get_icv_len(struct rtnl_link *, uint16_t *);
+
+int rtnl_link_macsec_set_protect(struct rtnl_link *, uint8_t);
+int rtnl_link_macsec_get_protect(struct rtnl_link *, uint8_t *);
+
+int rtnl_link_macsec_set_encrypt(struct rtnl_link *, uint8_t);
+int rtnl_link_macsec_get_encrypt(struct rtnl_link *, uint8_t *);
+
+int rtnl_link_macsec_set_encoding_sa(struct rtnl_link *, uint8_t);
+int rtnl_link_macsec_get_encoding_sa(struct rtnl_link *, uint8_t *);
+
+int rtnl_link_macsec_set_validation_type(struct rtnl_link *,
+					 enum macsec_validation_type);
+int rtnl_link_macsec_get_validation_type(struct rtnl_link *,
+					 enum macsec_validation_type *);
+
+int rtnl_link_macsec_set_replay_protect(struct rtnl_link *, uint8_t);
+int rtnl_link_macsec_get_replay_protect(struct rtnl_link *, uint8_t *);
+
+int rtnl_link_macsec_set_window(struct rtnl_link *, uint32_t);
+int rtnl_link_macsec_get_window(struct rtnl_link *, uint32_t *);
+
+int rtnl_link_macsec_set_send_sci(struct rtnl_link *, uint8_t);
+int rtnl_link_macsec_get_send_sci(struct rtnl_link *, uint8_t *);
+
+int rtnl_link_macsec_set_end_station(struct rtnl_link *, uint8_t);
+int rtnl_link_macsec_get_end_station(struct rtnl_link *, uint8_t *);
+
+int rtnl_link_macsec_set_scb(struct rtnl_link *, uint8_t);
+int rtnl_link_macsec_get_scb(struct rtnl_link *, uint8_t *);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/macvlan.h b/include/netlink/route/link/macvlan.h
index 2207c53..15a6cc1 100644
--- a/include/netlink/route/link/macvlan.h
+++ b/include/netlink/route/link/macvlan.h
@@ -29,6 +29,9 @@
 extern char *		rtnl_link_macvlan_flags2str(int, char *, size_t);
 extern int		rtnl_link_macvlan_str2flags(const char *);
 
+extern char *		rtnl_link_macvlan_macmode2str(int, char *, size_t);
+extern int		rtnl_link_macvlan_str2macmode(const char *);
+
 extern int		rtnl_link_macvlan_set_mode(struct rtnl_link *,
 			                           uint32_t);
 extern uint32_t		rtnl_link_macvlan_get_mode(struct rtnl_link *);
@@ -39,6 +42,21 @@
 						   uint16_t);
 extern uint16_t		rtnl_link_macvlan_get_flags(struct rtnl_link *);
 
+extern int		rtnl_link_macvlan_set_macmode(struct rtnl_link *,
+						      uint32_t);
+extern int		rtnl_link_macvlan_get_macmode(struct rtnl_link *link,
+						      uint32_t *out_macmode);
+
+extern int		rtnl_link_macvlan_count_macaddr(struct rtnl_link *link,
+							uint32_t *out_count);
+extern int		rtnl_link_macvlan_get_macaddr(struct rtnl_link *link,
+						      uint32_t idx,
+						      const struct nl_addr **addr);
+extern int		rtnl_link_macvlan_add_macaddr(struct rtnl_link *link,
+						      struct nl_addr *addr);
+extern int		rtnl_link_macvlan_del_macaddr(struct rtnl_link *link,
+						      struct nl_addr *addr);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/netlink/route/link/macvtap.h b/include/netlink/route/link/macvtap.h
new file mode 100644
index 0000000..affcddc
--- /dev/null
+++ b/include/netlink/route/link/macvtap.h
@@ -0,0 +1,46 @@
+/*
+ * netlink/route/link/macvtap.h         MACVTAP interface
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2015 Beniamino Galvani <bgalvani@redhat.com>
+ */
+
+#ifndef NETLINK_LINK_MACVTAP_H_
+#define NETLINK_LINK_MACVTAP_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct rtnl_link *rtnl_link_macvtap_alloc(void);
+
+extern int               rtnl_link_is_macvtap(struct rtnl_link *);
+
+extern char *            rtnl_link_macvtap_mode2str(int, char *, size_t);
+extern int               rtnl_link_macvtap_str2mode(const char *);
+
+extern char *            rtnl_link_macvtap_flags2str(int, char *, size_t);
+extern int               rtnl_link_macvtap_str2flags(const char *);
+
+extern int               rtnl_link_macvtap_set_mode(struct rtnl_link *,
+                                                    uint32_t);
+extern uint32_t          rtnl_link_macvtap_get_mode(struct rtnl_link *);
+
+extern int               rtnl_link_macvtap_set_flags(struct rtnl_link *,
+                                                     uint16_t);
+extern int               rtnl_link_macvtap_unset_flags(struct rtnl_link *,
+                                                       uint16_t);
+extern uint16_t          rtnl_link_macvtap_get_flags(struct rtnl_link *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/ppp.h b/include/netlink/route/link/ppp.h
new file mode 100644
index 0000000..4ff811d
--- /dev/null
+++ b/include/netlink/route/link/ppp.h
@@ -0,0 +1,30 @@
+/*
+ * netlink/route/link/ppp.h		PPP Interface
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2016 Jonas Johansson <jonasj76@gmail.com>
+ */
+
+#ifndef NETLINK_LINK_PPP_H_
+#define NETLINK_LINK_PPP_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct rtnl_link	*rtnl_link_ppp_alloc(void);
+extern int		rtnl_link_ppp_set_fd(struct rtnl_link *, int32_t);
+extern int		rtnl_link_ppp_get_fd(struct rtnl_link *, int32_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/sit.h b/include/netlink/route/link/sit.h
index 84dc44a..d6f5851 100644
--- a/include/netlink/route/link/sit.h
+++ b/include/netlink/route/link/sit.h
@@ -22,11 +22,13 @@
 	extern struct rtnl_link *rtnl_link_sit_alloc(void);
 	extern int rtnl_link_sit_add(struct nl_sock *sk, const char *name);
 
+	extern int rtnl_link_is_sit(struct rtnl_link *link);
+
 	extern int rtnl_link_sit_set_link(struct rtnl_link *link,  uint32_t index);
 	extern uint32_t rtnl_link_sit_get_link(struct rtnl_link *link);
 
 	extern int rtnl_link_sit_set_local(struct rtnl_link *link, uint32_t addr);
-	extern uint32_t rtnl_link_get_sit_local(struct rtnl_link *link);
+	extern uint32_t rtnl_link_sit_get_local(struct rtnl_link *link);
 
 	extern int rtnl_link_sit_set_remote(struct rtnl_link *link, uint32_t addr);
 	extern uint32_t rtnl_link_sit_get_remote(struct rtnl_link *link);
@@ -44,9 +46,21 @@
 	extern uint16_t rtnl_link_sit_get_flags(struct rtnl_link *link);
 
 	int rtnl_link_sit_set_proto(struct rtnl_link *link, uint8_t proto);
-	uint8_t rtnl_link_get_proto(struct rtnl_link *link);
+	uint8_t rtnl_link_sit_get_proto(struct rtnl_link *link);
 
-#ifdef _cplusplus
+	int rtnl_link_sit_set_ip6rd_prefix(struct rtnl_link *link, const struct in6_addr *prefix);
+	int rtnl_link_sit_get_ip6rd_prefix(const struct rtnl_link *link, struct in6_addr *prefix);
+
+	int rtnl_link_sit_set_ip6rd_prefixlen(struct rtnl_link *link, uint16_t prefixlen);
+	int rtnl_link_sit_get_ip6rd_prefixlen(struct rtnl_link *link, uint16_t *prefixlen);
+
+	int rtnl_link_sit_set_ip6rd_relay_prefix(struct rtnl_link *link, uint32_t prefix);
+	int rtnl_link_sit_get_ip6rd_relay_prefix(const struct rtnl_link *link, uint32_t *prefix);
+
+	int rtnl_link_sit_set_ip6rd_relay_prefixlen(struct rtnl_link *link, uint16_t prefix);
+	int rtnl_link_sit_get_ip6rd_relay_prefixlen(struct rtnl_link *link, uint16_t *prefix);
+
+#ifdef __cplusplus
 }
 #endif
 
diff --git a/include/netlink/route/link/sriov.h b/include/netlink/route/link/sriov.h
new file mode 100644
index 0000000..3f7cacf
--- /dev/null
+++ b/include/netlink/route/link/sriov.h
@@ -0,0 +1,145 @@
+/*
+ * include/netlink/route/link/sriov.h      SRIOV VF Info
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2016 Intel Corp. All rights reserved.
+ * Copyright (c) 2016 Jef Oliver <jef.oliver@intel.com>
+ */
+
+#ifndef NETLINK_LINK_SRIOV_H_
+#define NETLINK_LINK_SRIOV_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTNL_VF_GUID_STR_LEN 23
+
+/**
+ * @ingroup sriov
+ */
+typedef enum {
+	RTNL_LINK_VF_RATE_API_UNSPEC,	/*!< Unspecified API type */
+	RTNL_LINK_VF_RATE_API_OLD,	/*!< Old Rate setting API */
+	RTNL_LINK_VF_RATE_API_NEW,	/*!< New Rate setting API */
+	__RTNL_LINK_VF_RATE_API_MAX,
+} rtnl_link_rate_api_t;
+
+#define RTNL_LINK_VF_RATE_API_MAX (__RTNL_LINK_VF_RATE_API_MAX - 1)
+
+/**
+ * @ingroup sriov
+ */
+typedef enum {
+	RTNL_LINK_VF_STATS_RX_PACKETS,	/*!< Packets Received */
+	RTNL_LINK_VF_STATS_TX_PACKETS,	/*!< Packets Sent */
+	RTNL_LINK_VF_STATS_RX_BYTES,	/*!< Bytes Recieved */
+	RTNL_LINK_VF_STATS_TX_BYTES,	/*!< Bytes Sent */
+	RTNL_LINK_VF_STATS_BROADCAST,	/*!< Broadcast packets received */
+	RTNL_LINK_VF_STATS_MULTICAST,	/*!< Multicast packets received */
+	__RTNL_LINK_VF_STATS_MAX,
+} rtnl_link_vf_stats_t;
+
+#define RTNL_LINK_VF_STATS_MAX (__RTNL_LINK_VF_STATS_MAX - 1)
+
+/**
+ * @struct rtnl_link_vf sriov.h "netlink/route/link/sriov.h"
+ * @brief SRIOV VF object
+ * @ingroup sriov
+ *
+ * @copydoc private_struct
+ */
+struct rtnl_link_vf;
+
+/**
+ * @brief SRIOV VF VFLAN settings
+ * @ingroup sriov
+ */
+typedef struct nl_vf_vlan_info {
+	uint32_t	vf_vlan;	/*!< VLAN number */
+	uint32_t	vf_vlan_qos;	/*!< VLAN QOS value */
+	uint16_t	vf_vlan_proto;	/*!< VLAN protocol */
+} nl_vf_vlan_info_t;
+
+/**
+ * @brief SRIOV VF VLANs information
+ * @ingroup sriov
+ */
+typedef struct nl_vf_vlans {
+	int			ce_refcnt;	/*!< Reference counter. Don't change this value */
+	int			size;		/*!< Number of VLANs on the SRIOV VF */
+	nl_vf_vlan_info_t *	vlans;		/*!< nl_vf_vlan_info_t array of SRIOV VF VLANs */
+} nl_vf_vlans_t;
+
+/**
+ * @brief VF Rate information structure
+ * @ingroup sriov
+ */
+struct nl_vf_rate {
+	int		api;		/*!< rtnl_link_rate_api_t API Version to use */
+	uint32_t	rate;		/*!< Old API Max Rate in Mbps */
+	uint32_t	max_tx_rate;	/*!< New API Max Rate in Mbps */
+	uint32_t	min_tx_rate;	/*!< New API Mix Rate in Mbps */
+};
+
+extern int rtnl_link_vf_add(struct rtnl_link *, struct rtnl_link_vf *);
+extern struct rtnl_link_vf *rtnl_link_vf_alloc(void);
+extern void rtnl_link_vf_free(struct rtnl_link_vf *);
+extern struct rtnl_link_vf *rtnl_link_vf_get(struct rtnl_link *, uint32_t);
+extern void rtnl_link_vf_put(struct rtnl_link_vf *);
+
+extern int rtnl_link_vf_get_addr(struct rtnl_link_vf *, struct nl_addr **);
+extern void rtnl_link_vf_set_addr(struct rtnl_link_vf *, struct nl_addr *);
+
+extern void rtnl_link_vf_set_ib_node_guid(struct rtnl_link_vf *, uint64_t);
+extern void rtnl_link_vf_set_ib_port_guid(struct rtnl_link_vf *, uint64_t);
+
+extern int rtnl_link_vf_get_index(struct rtnl_link_vf *, uint32_t *);
+extern void rtnl_link_vf_set_index(struct rtnl_link_vf *, uint32_t);
+
+extern int rtnl_link_vf_get_linkstate(struct rtnl_link_vf *, uint32_t *);
+extern void rtnl_link_vf_set_linkstate(struct rtnl_link_vf *, uint32_t);
+
+extern int rtnl_link_vf_get_rate(struct rtnl_link_vf *, struct nl_vf_rate *);
+extern void rtnl_link_vf_set_rate(struct rtnl_link_vf *, struct nl_vf_rate *);
+
+extern int rtnl_link_vf_get_rss_query_en(struct rtnl_link_vf *, uint32_t *);
+extern void rtnl_link_vf_set_rss_query_en(struct rtnl_link_vf *, uint32_t);
+
+extern int rtnl_link_vf_get_spoofchk(struct rtnl_link_vf *, uint32_t *);
+extern void rtnl_link_vf_set_spoofchk(struct rtnl_link_vf *, uint32_t);
+
+extern int rtnl_link_vf_get_stat(struct rtnl_link_vf *, rtnl_link_vf_stats_t,
+				 uint64_t *);
+
+extern int rtnl_link_vf_get_trust(struct rtnl_link_vf *, uint32_t *);
+extern void rtnl_link_vf_set_trust(struct rtnl_link_vf *, uint32_t);
+
+extern int rtnl_link_vf_get_vlans(struct rtnl_link_vf *, nl_vf_vlans_t **);
+extern void rtnl_link_vf_set_vlans(struct rtnl_link_vf *, nl_vf_vlans_t *);
+
+extern int rtnl_link_vf_vlan_alloc(nl_vf_vlans_t **, int);
+extern void rtnl_link_vf_vlan_free(nl_vf_vlans_t *vf_vlans);
+extern void rtnl_link_vf_vlan_put(nl_vf_vlans_t *);
+
+/* Utility functions */
+extern char *rtnl_link_vf_linkstate2str(uint32_t, char *, size_t);
+extern int rtnl_link_vf_str2linkstate(const char *);
+
+extern char *rtnl_link_vf_vlanproto2str(uint16_t, char *, size_t);
+extern int rtnl_link_vf_str2vlanproto(const char *);
+
+extern int rtnl_link_vf_str2guid(uint64_t *, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/vrf.h b/include/netlink/route/link/vrf.h
new file mode 100644
index 0000000..0a56d91
--- /dev/null
+++ b/include/netlink/route/link/vrf.h
@@ -0,0 +1,32 @@
+/*
+ * netlink/route/link/vrf.h          VRF interface
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2015 Cumulus Networks. All rights reserved.
+ * Copyright (c) 2015 David Ahern <dsa@cumulusnetworks.com>
+ */
+
+#ifndef NETLINK_LINK_VRF_H_
+#define NETLINK_LINK_VRF_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct rtnl_link *rtnl_link_vrf_alloc(void);
+extern int rtnl_link_is_vrf(struct rtnl_link *link);
+extern int rtnl_link_vrf_get_tableid(struct rtnl_link *link, uint32_t *id);
+extern int rtnl_link_vrf_set_tableid(struct rtnl_link *link, uint32_t id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/vxlan.h b/include/netlink/route/link/vxlan.h
index f7f7b60..a929a9f 100644
--- a/include/netlink/route/link/vxlan.h
+++ b/include/netlink/route/link/vxlan.h
@@ -19,20 +19,31 @@
 extern "C" {
 #endif
 
+struct ifla_vxlan_port_range;
+
 #define VXLAN_ID_MAX 16777215
 
+enum {
+	RTNL_LINK_VXLAN_F_GBP                   = 1 << 0,
+#define RTNL_LINK_VXLAN_F_GBP RTNL_LINK_VXLAN_F_GBP
+	RTNL_LINK_VXLAN_F_GPE                   = 1 << 1,
+#define RTNL_LINK_VXLAN_F_GPE RTNL_LINK_VXLAN_F_GPE
+	RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL     = 1 << 2,
+#define RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL
+};
+
 extern struct rtnl_link *rtnl_link_vxlan_alloc(void);
 
-extern int		rtnl_link_is_vxlan(struct rtnl_link *);
+extern int	rtnl_link_is_vxlan(struct rtnl_link *);
 
-extern int		rtnl_link_vxlan_set_id(struct rtnl_link *, uint32_t);
-extern int		rtnl_link_vxlan_get_id(struct rtnl_link *, uint32_t *);
+extern int	rtnl_link_vxlan_set_id(struct rtnl_link *, uint32_t);
+extern int	rtnl_link_vxlan_get_id(struct rtnl_link *, uint32_t *);
 
 extern int	rtnl_link_vxlan_set_group(struct rtnl_link *, struct nl_addr *);
 extern int	rtnl_link_vxlan_get_group(struct rtnl_link *, struct nl_addr **);
 
-extern int		rtnl_link_vxlan_set_link(struct rtnl_link *, uint32_t);
-extern int		rtnl_link_vxlan_get_link(struct rtnl_link *, uint32_t *);
+extern int	rtnl_link_vxlan_set_link(struct rtnl_link *, uint32_t);
+extern int	rtnl_link_vxlan_get_link(struct rtnl_link *, uint32_t *);
 
 extern int	rtnl_link_vxlan_set_local(struct rtnl_link *, struct nl_addr *);
 extern int	rtnl_link_vxlan_get_local(struct rtnl_link *, struct nl_addr **);
@@ -79,6 +90,33 @@
 extern int	rtnl_link_vxlan_enable_l3miss(struct rtnl_link *);
 extern int	rtnl_link_vxlan_disable_l3miss(struct rtnl_link *);
 
+extern int	rtnl_link_vxlan_set_port(struct rtnl_link *, uint32_t);
+extern int	rtnl_link_vxlan_get_port(struct rtnl_link *, uint32_t *);
+
+extern int	rtnl_link_vxlan_set_udp_csum(struct rtnl_link *, uint8_t);
+extern int	rtnl_link_vxlan_get_udp_csum(struct rtnl_link *);
+
+extern int	rtnl_link_vxlan_set_udp_zero_csum6_tx(struct rtnl_link *, uint8_t);
+extern int	rtnl_link_vxlan_get_udp_zero_csum6_tx(struct rtnl_link *);
+
+extern int	rtnl_link_vxlan_set_udp_zero_csum6_rx(struct rtnl_link *, uint8_t);
+extern int	rtnl_link_vxlan_get_udp_zero_csum6_rx(struct rtnl_link *);
+
+extern int	rtnl_link_vxlan_set_remcsum_tx(struct rtnl_link *, uint8_t);
+extern int	rtnl_link_vxlan_get_remcsum_tx(struct rtnl_link *);
+
+extern int	rtnl_link_vxlan_set_remcsum_rx(struct rtnl_link *, uint8_t);
+extern int	rtnl_link_vxlan_get_remcsum_rx(struct rtnl_link *);
+
+extern int	rtnl_link_vxlan_set_flags(struct rtnl_link *, uint32_t flags, int enable);
+extern int	rtnl_link_vxlan_get_flags(struct rtnl_link *, uint32_t *out_flags);
+
+extern int	rtnl_link_vxlan_set_collect_metadata(struct rtnl_link *, uint8_t);
+extern int	rtnl_link_vxlan_get_collect_metadata(struct rtnl_link *);
+
+extern int	rtnl_link_vxlan_set_label(struct rtnl_link *, uint32_t);
+extern int	rtnl_link_vxlan_get_label(struct rtnl_link *, uint32_t *);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/netlink/route/link/xfrmi.h b/include/netlink/route/link/xfrmi.h
new file mode 100644
index 0000000..6e4cda7
--- /dev/null
+++ b/include/netlink/route/link/xfrmi.h
@@ -0,0 +1,37 @@
+/*
+ * netlink/route/link/xfrmi.h		XFRMI interface
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2019 Eyal Birger <eyal.birger@gmail.com>
+ *
+ * Based on netlink/route/link/ipvti.h
+ */
+
+#ifndef NETLINK_LINK_XFRMI_H_
+#define NETLINK_LINK_XFRMI_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+	extern struct rtnl_link *rtnl_link_xfrmi_alloc(void);
+
+	extern int rtnl_link_is_xfrmi(struct rtnl_link *link);
+
+	extern int rtnl_link_xfrmi_set_link(struct rtnl_link *link,  uint32_t index);
+	extern int rtnl_link_xfrmi_get_link(struct rtnl_link *link, uint32_t *out_link);
+
+	extern int rtnl_link_xfrmi_set_if_id(struct rtnl_link *link, uint32_t if_id);
+	extern int rtnl_link_xfrmi_get_if_id(struct rtnl_link *link, uint32_t *out_if_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/neighbour.h b/include/netlink/route/neighbour.h
index 1d1179b..0f17b66 100644
--- a/include/netlink/route/neighbour.h
+++ b/include/netlink/route/neighbour.h
@@ -26,8 +26,13 @@
 extern void	rtnl_neigh_put(struct rtnl_neigh *);
 
 extern int	rtnl_neigh_alloc_cache(struct nl_sock *, struct nl_cache **);
+extern int	rtnl_neigh_alloc_cache_flags(struct nl_sock *,
+					     struct nl_cache **,
+					     unsigned int);
 extern struct rtnl_neigh *rtnl_neigh_get(struct nl_cache *, int,
 					       struct nl_addr *);
+extern struct rtnl_neigh *rtnl_neigh_get_by_vlan(struct nl_cache *, int,
+						 struct nl_addr *, int);
 
 extern int      rtnl_neigh_parse(struct nlmsghdr *, struct rtnl_neigh **);
 
@@ -74,6 +79,12 @@
 extern void			rtnl_neigh_set_family(struct rtnl_neigh *, int);
 extern int			rtnl_neigh_get_family(struct rtnl_neigh *);
 
+extern void			rtnl_neigh_set_vlan(struct rtnl_neigh *, int);
+extern int			rtnl_neigh_get_vlan(struct rtnl_neigh *);
+
+extern void			rtnl_neigh_set_master(struct rtnl_neigh *, int);
+extern int			rtnl_neigh_get_master(struct rtnl_neigh *);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/netlink/route/neightbl.h b/include/netlink/route/neightbl.h
index 412c3e9..6c6c9a5 100644
--- a/include/netlink/route/neightbl.h
+++ b/include/netlink/route/neightbl.h
@@ -41,6 +41,7 @@
 extern void rtnl_neightbl_set_gc_tresh1(struct rtnl_neightbl *, int);
 extern void rtnl_neightbl_set_gc_tresh2(struct rtnl_neightbl *, int);
 extern void rtnl_neightbl_set_gc_tresh3(struct rtnl_neightbl *, int);
+extern void rtnl_neightbl_set_gc_interval(struct rtnl_neightbl *, uint64_t);
 extern void rtnl_neightbl_set_name(struct rtnl_neightbl *, const char *);
 extern void rtnl_neightbl_set_dev(struct rtnl_neightbl *, int);
 extern void rtnl_neightbl_set_queue_len(struct rtnl_neightbl *, int);
diff --git a/include/netlink/route/netconf.h b/include/netlink/route/netconf.h
new file mode 100644
index 0000000..1993438
--- /dev/null
+++ b/include/netlink/route/netconf.h
@@ -0,0 +1,44 @@
+/*
+ * netlink/route/netconf.h		rtnetlink netconf layer
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2017 David Ahern <dsa@cumulusnetworks.com>
+ */
+
+#ifndef NETCONF_H_
+#define NETCONF_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rtnl_netconf;
+
+int rtnl_netconf_alloc_cache(struct nl_sock *sk, struct nl_cache **result);
+
+struct rtnl_netconf *rtnl_netconf_get_by_idx(struct nl_cache *cache, int family,
+					     int ifindex);
+struct rtnl_netconf *rtnl_netconf_get_all(struct nl_cache *cache,
+					  int family);
+struct rtnl_netconf *rtnl_netconf_get_default(struct nl_cache *cache,
+					      int family);
+void rtnl_netconf_put(struct rtnl_netconf *nc);
+
+int rtnl_netconf_get_family(struct rtnl_netconf *nc, int *val);
+int rtnl_netconf_get_ifindex(struct rtnl_netconf *nc, int *val);
+int rtnl_netconf_get_forwarding(struct rtnl_netconf *nc, int *val);
+int rtnl_netconf_get_mc_forwarding(struct rtnl_netconf *nc, int *val);
+int rtnl_netconf_get_rp_filter(struct rtnl_netconf *nc, int *val);
+int rtnl_netconf_get_proxy_neigh(struct rtnl_netconf *nc, int *val);
+int rtnl_netconf_get_ignore_routes_linkdown(struct rtnl_netconf *nc, int *val);
+int rtnl_netconf_get_input(struct rtnl_netconf *nc, int *val);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/nexthop.h b/include/netlink/route/nexthop.h
index 2aa44dc..5b422dd 100644
--- a/include/netlink/route/nexthop.h
+++ b/include/netlink/route/nexthop.h
@@ -55,9 +55,21 @@
 						 uint32_t);
 extern uint32_t		rtnl_route_nh_get_realms(struct rtnl_nexthop *);
 
+extern int		rtnl_route_nh_set_newdst(struct rtnl_nexthop *,
+						 struct nl_addr *);
+extern struct nl_addr *	rtnl_route_nh_get_newdst(struct rtnl_nexthop *);
+extern int		rtnl_route_nh_set_via(struct rtnl_nexthop *,
+						 struct nl_addr *);
+extern struct nl_addr *	rtnl_route_nh_get_via(struct rtnl_nexthop *);
 extern char *		rtnl_route_nh_flags2str(int, char *, size_t);
 extern int		rtnl_route_nh_str2flags(const char *);
 
+/*
+ * nexthop encapsulations
+ */
+extern int		rtnl_route_nh_encap_mpls(struct rtnl_nexthop *nh,
+						 struct nl_addr *addr,
+						 uint8_t ttl);
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/netlink/route/qdisc/hfsc.h b/include/netlink/route/qdisc/hfsc.h
new file mode 100644
index 0000000..4c33809
--- /dev/null
+++ b/include/netlink/route/qdisc/hfsc.h
@@ -0,0 +1,39 @@
+/*
+ * netlink/route/sch/hfsc.h	HFSC Qdisc
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2014 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+#ifndef NETLINK_HFSC_H_
+#define NETLINK_HFSC_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/tc.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/class.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct tc_service_curve;
+
+extern uint32_t	rtnl_qdisc_hfsc_get_defcls(const struct rtnl_qdisc *);
+extern int	rtnl_qdisc_hfsc_set_defcls(struct rtnl_qdisc *, uint32_t);
+
+extern int rtnl_class_hfsc_get_rsc(const struct rtnl_class *cls, struct tc_service_curve *tsc);
+extern int rtnl_class_hfsc_set_rsc(struct rtnl_class *cls, const struct tc_service_curve *tsc);
+extern int rtnl_class_hfsc_get_fsc(const struct rtnl_class *cls, struct tc_service_curve *tsc);
+extern int rtnl_class_hfsc_set_fsc(struct rtnl_class *cls, const struct tc_service_curve *tsc);
+extern int rtnl_class_hfsc_get_usc(const struct rtnl_class *cls, struct tc_service_curve *tsc);
+extern int rtnl_class_hfsc_set_usc(struct rtnl_class *cls, const struct tc_service_curve *tsc);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/qdisc/htb.h b/include/netlink/route/qdisc/htb.h
index c909f84..5d7ca45 100644
--- a/include/netlink/route/qdisc/htb.h
+++ b/include/netlink/route/qdisc/htb.h
@@ -30,16 +30,24 @@
 
 extern uint32_t	rtnl_htb_get_prio(struct rtnl_class *);
 extern int	rtnl_htb_set_prio(struct rtnl_class *, uint32_t);
-extern uint32_t	rtnl_htb_get_rate(struct rtnl_class *);
-extern int	rtnl_htb_set_rate(struct rtnl_class *, uint32_t);
-extern uint32_t	rtnl_htb_get_ceil(struct rtnl_class *);
-extern int	rtnl_htb_set_ceil(struct rtnl_class *, uint32_t);
+
+extern uint32_t  rtnl_htb_get_rate(struct rtnl_class *);
+extern int       rtnl_htb_set_rate(struct rtnl_class *, uint32_t);
+extern uint32_t  rtnl_htb_get_ceil(struct rtnl_class *);
+extern int       rtnl_htb_set_ceil(struct rtnl_class *, uint32_t);
+
+extern int       rtnl_htb_get_rate64(struct rtnl_class *, uint64_t *);
+extern int       rtnl_htb_set_rate64(struct rtnl_class *, uint64_t);
+extern int       rtnl_htb_get_ceil64(struct rtnl_class *, uint64_t *);
+extern int       rtnl_htb_set_ceil64(struct rtnl_class *, uint64_t);
+
 extern uint32_t	rtnl_htb_get_rbuffer(struct rtnl_class *);
 extern int	rtnl_htb_set_rbuffer(struct rtnl_class *, uint32_t);
 extern uint32_t	rtnl_htb_get_cbuffer(struct rtnl_class *);
 extern int	rtnl_htb_set_cbuffer(struct rtnl_class *, uint32_t);
 extern uint32_t	rtnl_htb_get_quantum(struct rtnl_class *);
 extern int	rtnl_htb_set_quantum(struct rtnl_class *, uint32_t);
+extern int	rtnl_htb_set_level(struct rtnl_class *, int);
 extern int	rtnl_htb_get_level(struct rtnl_class *);
 
 #ifdef __cplusplus
diff --git a/include/netlink/route/qdisc/mqprio.h b/include/netlink/route/qdisc/mqprio.h
new file mode 100644
index 0000000..1a38aeb
--- /dev/null
+++ b/include/netlink/route/qdisc/mqprio.h
@@ -0,0 +1,48 @@
+/*
+ * lib/route/qdisc/mqprio.c             MQPRIO Qdisc/Class
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2018 Volodymyr Bendiuga <volodymyr.bendiuga@westermo.se>
+ */
+
+#ifndef NETLINK_MQPRIO_H_
+#define NETLINK_MQPRIO_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/qdisc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int rtnl_qdisc_mqprio_set_num_tc(struct rtnl_qdisc *qdisc, int num_tc);
+extern int rtnl_qdisc_mqprio_get_num_tc(struct rtnl_qdisc *qdisc);
+extern int rtnl_qdisc_mqprio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[],
+                                         int len);
+extern uint8_t *rtnl_qdisc_mqprio_get_priomap(struct rtnl_qdisc *qdisc);
+extern int rtnl_qdisc_mqprio_hw_offload(struct rtnl_qdisc *qdisc, int offload);
+extern int rtnl_qdisc_mqprio_get_hw_offload(struct rtnl_qdisc *qdisc);
+extern int rtnl_qdisc_mqprio_set_queue(struct rtnl_qdisc *qdisc, uint16_t count[],
+                                       uint16_t offset[], int len);
+extern int rtnl_qdisc_mqprio_get_queue(struct rtnl_qdisc *qdisc, uint16_t *count,
+                                       uint16_t *offset);
+extern int rtnl_qdisc_mqprio_set_mode(struct rtnl_qdisc *qdisc, uint16_t mode);
+extern int rtnl_qdisc_mqprio_get_mode(struct rtnl_qdisc *qdisc);
+extern int rtnl_qdisc_mqprio_set_shaper(struct rtnl_qdisc *qdisc, uint16_t shaper);
+extern int rtnl_qdisc_mqprio_get_shaper(struct rtnl_qdisc *qdisc);
+extern int rtnl_qdisc_mqprio_set_min_rate(struct rtnl_qdisc *qdisc, uint64_t min[],
+                                          int len);
+extern int rtnl_qdisc_mqprio_get_min_rate(struct rtnl_qdisc *qdisc, uint64_t *min);
+extern int rtnl_qdisc_mqprio_set_max_rate(struct rtnl_qdisc *qdisc, uint64_t max[],
+                                          int len);
+extern int rtnl_qdisc_mqprio_get_max_rate(struct rtnl_qdisc *qdisc, uint64_t *max);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NETLINK_MQPRIO_H_ */
diff --git a/include/netlink/route/qdisc/netem.h b/include/netlink/route/qdisc/netem.h
index 4b071bf..47c9dd8 100644
--- a/include/netlink/route/qdisc/netem.h
+++ b/include/netlink/route/qdisc/netem.h
@@ -66,6 +66,7 @@
 /* Delay Distribution */
 #define MAXDIST 65536
 extern int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *, const char *);
+extern int rtnl_netem_set_delay_distribution_data(struct rtnl_qdisc *, const int16_t *, size_t len);
 extern int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *);
 extern int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *, int16_t **);
 
diff --git a/include/netlink/route/qdisc/red.h b/include/netlink/route/qdisc/red.h
index a4e8642..accb8d9 100644
--- a/include/netlink/route/qdisc/red.h
+++ b/include/netlink/route/qdisc/red.h
@@ -14,4 +14,7 @@
 
 #include <netlink/netlink.h>
 
+extern  void rtnl_red_set_limit(struct rtnl_qdisc *qdisc, int limit);
+extern int rtnl_red_get_limit(struct rtnl_qdisc *qdisc);
+
 #endif
diff --git a/include/netlink/route/route.h b/include/netlink/route/route.h
index 477250d..824dae3 100644
--- a/include/netlink/route/route.h
+++ b/include/netlink/route/route.h
@@ -94,6 +94,9 @@
 extern void	rtnl_route_set_iif(struct rtnl_route *, int);
 extern int	rtnl_route_get_iif(struct rtnl_route *);
 extern int	rtnl_route_get_src_len(struct rtnl_route *);
+extern void	rtnl_route_set_ttl_propagate(struct rtnl_route *route,
+					     uint8_t ttl_prop);
+extern int	rtnl_route_get_ttl_propagate(struct rtnl_route *route);
 
 extern void	rtnl_route_add_nexthop(struct rtnl_route *,
 				       struct rtnl_nexthop *);
diff --git a/include/netlink/route/rule.h b/include/netlink/route/rule.h
index 760b782..d0c335f 100644
--- a/include/netlink/route/rule.h
+++ b/include/netlink/route/rule.h
@@ -67,6 +67,24 @@
 extern uint32_t		rtnl_rule_get_realms(struct rtnl_rule *);
 extern void		rtnl_rule_set_goto(struct rtnl_rule *, uint32_t);
 extern uint32_t		rtnl_rule_get_goto(struct rtnl_rule *);
+extern void		rtnl_rule_set_l3mdev(struct rtnl_rule *, int);
+extern int		rtnl_rule_get_l3mdev(struct rtnl_rule *);
+extern int		rtnl_rule_set_protocol(struct rtnl_rule *, uint8_t);
+extern int		rtnl_rule_get_protocol(struct rtnl_rule *, uint8_t *);
+extern int		rtnl_rule_set_ipproto(struct rtnl_rule *, uint8_t);
+extern int		rtnl_rule_get_ipproto(struct rtnl_rule *, uint8_t *);
+extern int		rtnl_rule_set_sport(struct rtnl_rule *, uint16_t start);
+extern int		rtnl_rule_set_sport_range(struct rtnl_rule *,
+						  uint16_t start,
+						  uint16_t end);
+extern int		rtnl_rule_get_sport(struct rtnl_rule *, uint16_t *start,
+					    uint16_t *end);
+extern int		rtnl_rule_set_dport(struct rtnl_rule *, uint16_t start);
+extern int		rtnl_rule_set_dport_range(struct rtnl_rule *,
+						  uint16_t start,
+						  uint16_t end);
+extern int		rtnl_rule_get_dport(struct rtnl_rule *, uint16_t *start,
+					    uint16_t *end);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/route/tc.h b/include/netlink/route/tc.h
index 870c1f2..51d670a 100644
--- a/include/netlink/route/tc.h
+++ b/include/netlink/route/tc.h
@@ -97,6 +97,8 @@
 extern int		rtnl_tc_set_kind(struct rtnl_tc *, const char *);
 extern char *		rtnl_tc_get_kind(struct rtnl_tc *);
 extern uint64_t		rtnl_tc_get_stat(struct rtnl_tc *, enum rtnl_tc_stat);
+extern char *		rtnl_tc_stat2str(enum rtnl_tc_stat, char *, size_t);
+extern int		rtnl_tc_str2stat(const char *);
 
 extern int		rtnl_tc_calc_txtime(int, int);
 extern int		rtnl_tc_calc_bufsize(int, int);
@@ -107,6 +109,8 @@
 extern int		rtnl_tc_str2handle(const char *, uint32_t *);
 extern int		rtnl_classid_generate(const char *, uint32_t *,
 					      uint32_t);
+extern void		rtnl_tc_set_chain(struct rtnl_tc *, uint32_t);
+extern int              rtnl_tc_get_chain(struct rtnl_tc *, uint32_t *);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/socket.h b/include/netlink/socket.h
index 1007eba..9a68cad 100644
--- a/include/netlink/socket.h
+++ b/include/netlink/socket.h
@@ -60,6 +60,7 @@
 extern void		nl_socket_enable_auto_ack(struct nl_sock *);
 
 extern int		nl_socket_get_fd(const struct nl_sock *);
+extern int              nl_socket_set_fd(struct nl_sock *sk, int protocol, int fd);
 extern int		nl_socket_set_nonblocking(const struct nl_sock *);
 extern void		nl_socket_enable_msg_peek(struct nl_sock *);
 extern void		nl_socket_disable_msg_peek(struct nl_sock *);
diff --git a/include/netlink/utils.h b/include/netlink/utils.h
index 6b4b787..b05ce66 100644
--- a/include/netlink/utils.h
+++ b/include/netlink/utils.h
@@ -113,8 +113,202 @@
 	NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE = 4,
 #define NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE
 
-	__NL_CAPABILITY_MAX
-#define NL_CAPABILITY_MAX                               (__NL_CAPABILITY_MAX - 1)
+	/**
+	 * Indicate that rtnl_link_get_kernel() fails with -NLE_OPNOTSUPP in case
+	 * of older kernals not supporting lookup by ifname. This changes behavior
+	 * from returning -NLE_INVAL to return -NLE_OPNOTSUPP.
+	 */
+	NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP = 5,
+#define NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP
+
+	/**
+	 * Also consider the a_cacheinfo field (ADDR_ATTR_CACHEINFO) that contains the
+	 * address timestamps and expiry when comparing struct rtnl_addr objects with
+	 * nl_object_diff().
+	 */
+	NL_CAPABILITY_ROUTE_ADDR_COMPARE_CACHEINFO = 6,
+#define NL_CAPABILITY_ROUTE_ADDR_COMPARE_CACHEINFO NL_CAPABILITY_ROUTE_ADDR_COMPARE_CACHEINFO
+
+	/**
+	 * The library version is libnl3 3.2.26 or newer. This capability should never be backported.
+	 */
+	NL_CAPABILITY_VERSION_3_2_26 = 7,
+#define NL_CAPABILITY_VERSION_3_2_26 NL_CAPABILITY_VERSION_3_2_26
+
+	/**
+	 * nl_recv() fails with NLE_MSG_TRUNC if a message got truncated
+	 * with NL_MSG_PEEK disabled. Previously, the failed message was wrongly
+	 * discarded and the next message received.
+	 */
+	NL_CAPABILITY_NL_RECV_FAIL_TRUNC_NO_PEEK = 8,
+#define NL_CAPABILITY_NL_RECV_FAIL_TRUNC_NO_PEEK NL_CAPABILITY_NL_RECV_FAIL_TRUNC_NO_PEEK
+
+	/**
+	 * rtnl_link_build_change_request() and rtnl_link_change() would set ifi.ifi_flags but leave
+	 * ifi.ifi_change at zero. This was later fixed to set ifi.ifi_change to the flags that are actually
+	 * set in changes.
+	 */
+	NL_CAPABILITY_LINK_BUILD_CHANGE_REQUEST_SET_CHANGE = 9,
+#define NL_CAPABILITY_LINK_BUILD_CHANGE_REQUEST_SET_CHANGE NL_CAPABILITY_LINK_BUILD_CHANGE_REQUEST_SET_CHANGE
+
+	/**
+	 * Between 3.2.14 (64fcb47a36ec12d7e7f00605f6a8952ce985dd08) and 3.2.22 (8571f58f23763d8db7365d02c9b27832ad3d7005),
+	 * rtnl_neigh_get() behaved differently and only returned objects with family AF_UNSPEC.
+	 * This capability indicates, that the function was fixed. The absense of the capability,
+	 * doesn't indicate however which behavior the function will have. So beware. */
+	NL_CAPABILITY_RTNL_NEIGH_GET_FILTER_AF_UNSPEC_FIX = 10,
+#define NL_CAPABILITY_RTNL_NEIGH_GET_FILTER_AF_UNSPEC_FIX NL_CAPABILITY_RTNL_NEIGH_GET_FILTER_AF_UNSPEC_FIX
+
+	/**
+	 * The library version is libnl3 3.2.27 or newer. This capability should never be backported.
+	 */
+	NL_CAPABILITY_VERSION_3_2_27 = 11,
+#define NL_CAPABILITY_VERSION_3_2_27 NL_CAPABILITY_VERSION_3_2_27
+
+	/**
+	 * Properly serialize vlan protocol IFLA_VLAN_PROTOCOL.
+	 */
+	NL_CAPABILITY_RTNL_LINK_VLAN_PROTOCOL_SERIALZE = 12,
+#define NL_CAPABILITY_RTNL_LINK_VLAN_PROTOCOL_SERIALZE NL_CAPABILITY_RTNL_LINK_VLAN_PROTOCOL_SERIALZE
+
+	/**
+	 * Properly read gre REMOTE port.
+	 */
+	NL_CAPABILITY_RTNL_LINK_PARSE_GRE_REMOTE = 13,
+#define NL_CAPABILITY_RTNL_LINK_PARSE_GRE_REMOTE NL_CAPABILITY_RTNL_LINK_PARSE_GRE_REMOTE
+
+	/**
+	 * Don't skip over vlan ingress-map entries with "to" field zero when serializing
+	 * a netlink message. Previously such entires would be ignored which inhibits the
+	 * user from clearing ingress map entries.
+	 */
+	NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR = 14,
+#define NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR
+
+	/**
+	 * Consider vxlan link info for nl_object_diff().
+	 */
+	NL_CAPABILITY_RTNL_LINK_VXLAN_IO_COMPARE = 15,
+#define NL_CAPABILITY_RTNL_LINK_VXLAN_IO_COMPARE NL_CAPABILITY_RTNL_LINK_VXLAN_IO_COMPARE
+
+	/**
+	 * Support 64 bit attributes for nl_object_diff().
+	 */
+	NL_CAPABILITY_NL_OBJECT_DIFF64 = 16,
+#define NL_CAPABILITY_NL_OBJECT_DIFF64 NL_CAPABILITY_NL_OBJECT_DIFF64
+
+	/**
+	 * Support omitting @key argument to xfrmnl_sa_get_*_params() to check
+	 * for required buffer size for key.
+	 */
+	NL_CAPABILITY_XFRM_SA_KEY_SIZE = 17,
+#define NL_CAPABILITY_XFRM_SA_KEY_SIZE NL_CAPABILITY_XFRM_SA_KEY_SIZE
+
+	/**
+	 * Properly handle nl_object_identity() for AF_INET and AF_INET6 addresses
+	 * and properly handle the peer/IFA_ADDRESS for IPv4 addresses.
+	 */
+	NL_CAPABILITY_RTNL_ADDR_PEER_FIX = 18,
+#define NL_CAPABILITY_RTNL_ADDR_PEER_FIX NL_CAPABILITY_RTNL_ADDR_PEER_FIX
+
+	/**
+	 * The library version is libnl3 3.2.28 or newer. This capability should never be backported.
+	 */
+	NL_CAPABILITY_VERSION_3_2_28 = 19,
+#define NL_CAPABILITY_VERSION_3_2_28 NL_CAPABILITY_VERSION_3_2_28
+
+	/**
+	 * After NL_CAPABILITY_RTNL_ADDR_PEER_FIX, a follow up regression to lookup
+	 * IPv4 addresses in the cache was fixed (PR#105).
+	 */
+	NL_CAPABILITY_RTNL_ADDR_PEER_ID_FIX = 20,
+#define NL_CAPABILITY_RTNL_ADDR_PEER_ID_FIX NL_CAPABILITY_RTNL_ADDR_PEER_ID_FIX
+
+	/**
+	 * nl_addr_fill_sockaddr() properly checks that the provided address to
+	 * avoid read-out-of-bounds for invalid addresses.
+	 */
+	NL_CAPABILITY_NL_ADDR_FILL_SOCKADDR = 21,
+#define NL_CAPABILITY_NL_ADDR_FILL_SOCKADDR NL_CAPABILITY_NL_ADDR_FILL_SOCKADDR
+
+	/**
+	 * Support omitting @ctx_str argument to xfrmnl_sa_get_sec_ctx() to check
+	 * for required buffer size for context string.
+	 */
+	NL_CAPABILITY_XFRM_SEC_CTX_LEN = 22,
+#define NL_CAPABILITY_XFRM_SEC_CTX_LEN NL_CAPABILITY_XFRM_SEC_CTX_LEN
+
+	/**
+	 * rtnl_link_build_add_request() would set ifi.ifi_flags but leave ifi.ifi_change at zero.
+	 * This was later fixed to set ifi.ifi_change to the flags that are actually
+	 * set
+	 */
+	NL_CAPABILITY_LINK_BUILD_ADD_REQUEST_SET_CHANGE = 23,
+#define NL_CAPABILITY_LINK_BUILD_ADD_REQUEST_SET_CHANGE NL_CAPABILITY_LINK_BUILD_ADD_REQUEST_SET_CHANGE
+
+	/* Older versions of libnl3 would not use MSG_PEEK for nl_recvmsgs() unless calling
+	 * nl_socket_enable_msg_peek(). Instead, the user had to specify the buffer size via
+	 * nl_socket_set_msg_buf_size(), which in turn would default to 4*getpagesize().
+	 *
+	 * The default value might not be large enough, so users who were not aware of the
+	 * problem easily ended up using a too small receive buffer. Usually, one wants to
+	 * avoid MSG_PEEK for recvmsg() because it requires an additional syscall.
+	 *
+	 * Now, as indicated by this capability, nl_recvmsgs() would use MSG_PEEK by default. The
+	 * user still can explicitly disable MSG_PEEK by calling nl_socket_disable_msg_peek() or
+	 * by setting the nl_socket_set_msg_buf_size() to a non-zero value.
+	 */
+	NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT = 24,
+#define NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT
+
+	/**
+	 * The library version is libnl3 3.2.29 or newer. This capability should never be backported.
+	 */
+	NL_CAPABILITY_VERSION_3_2_29 = 25,
+#define NL_CAPABILITY_VERSION_3_2_29 NL_CAPABILITY_VERSION_3_2_29
+
+	/**
+	 * Support omitting @ctx_str argument to xfrmnl_sp_get_sec_ctx() to check
+	 * for required buffer size for context string.
+	 */
+	NL_CAPABILITY_XFRM_SP_SEC_CTX_LEN = 26,
+#define NL_CAPABILITY_XFRM_SP_SEC_CTX_LEN NL_CAPABILITY_XFRM_SP_SEC_CTX_LEN
+
+	/**
+	 * The library version is libnl3 3.3.0 or newer. This capability should never be backported.
+	 */
+	NL_CAPABILITY_VERSION_3_3_0 = 27,
+#define NL_CAPABILITY_VERSION_3_3_0 NL_CAPABILITY_VERSION_3_3_0
+
+	/**
+	 * The library version is libnl3 3.4.0 or newer. This capability should never be backported.
+	 */
+	NL_CAPABILITY_VERSION_3_4_0 = 28,
+#define NL_CAPABILITY_VERSION_3_4_0 NL_CAPABILITY_VERSION_3_4_0
+
+	/**
+	 * Fixed memory corruption in rtnl_link_vlan_set_egress_map(). Previously, if you tried
+	 * to add more then 4 mappings, a buffer overflow occured. Also fixed nl_object_clone()
+	 * for VLAN links.
+	 */
+	NL_CAPABILITY_ROUTE_FIX_VLAN_SET_EGRESS_MAP = 29,
+#define NL_CAPABILITY_ROUTE_FIX_VLAN_SET_EGRESS_MAP NL_CAPABILITY_ROUTE_FIX_VLAN_SET_EGRESS_MAP
+
+	/**
+	 * The library version is libnl3 3.5.0 or newer. This capability should never be backported.
+	 */
+	NL_CAPABILITY_VERSION_3_5_0 = 30,
+#define NL_CAPABILITY_VERSION_3_5_0 NL_CAPABILITY_VERSION_3_5_0
+
+	__NL_CAPABILITY_MAX,
+	NL_CAPABILITY_MAX = (__NL_CAPABILITY_MAX - 1),
+#define NL_CAPABILITY_MAX NL_CAPABILITY_MAX
+
+	/**
+	 * The range 0x7000 to 0x7FFF is reserved for private capabilities. Upstream libnl3 will
+	 * not register capabilities in this range. However, instead of adding private capabilities,
+	 * better register their number with upstream libnl3. */
+#define NL_CAPABILITY_IS_USER_RESERVED(cap)    ( ((cap) & ~0x0FFF) == 0x7000 )
 };
 int nl_has_capability (int capability);
 
diff --git a/include/netlink/version.h b/include/netlink/version.h
index a809442..093fd91 100644
--- a/include/netlink/version.h
+++ b/include/netlink/version.h
@@ -14,18 +14,18 @@
 
 /* Compile Time Versioning Information */
 
-#define LIBNL_STRING "libnl 3.2.25"
-#define LIBNL_VERSION "3.2.25"
+#define LIBNL_STRING "libnl 3.5.0"
+#define LIBNL_VERSION "3.5.0"
 
 #define LIBNL_VER_MAJ		3
-#define LIBNL_VER_MIN		2
-#define LIBNL_VER_MIC		25
+#define LIBNL_VER_MIN		5
+#define LIBNL_VER_MIC		0
 #define LIBNL_VER(maj,min)	((maj) << 8 | (min))
 #define LIBNL_VER_NUM		LIBNL_VER(LIBNL_VER_MAJ, LIBNL_VER_MIN)
 
-#define LIBNL_CURRENT		220
+#define LIBNL_CURRENT		226
 #define LIBNL_REVISION		0
-#define LIBNL_AGE		20
+#define LIBNL_AGE		26
 
 /* Run-time version information */
 
diff --git a/include/netlink/xfrm/ae.h b/include/netlink/xfrm/ae.h
new file mode 100644
index 0000000..95112dd
--- /dev/null
+++ b/include/netlink/xfrm/ae.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *    Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *    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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  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 NETLINK_XFRM_AE_H_
+#define NETLINK_XFRM_AE_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/addr.h>
+#include <linux/xfrm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct xfrmnl_ae;
+
+extern struct xfrmnl_ae*xfrmnl_ae_alloc(void);
+extern void             xfrmnl_ae_put(struct xfrmnl_ae *);
+
+extern int              xfrmnl_ae_get_kernel(struct nl_sock*, struct nl_addr*, unsigned int, unsigned int,
+                                             unsigned int, unsigned int, struct xfrmnl_ae**);
+extern int              xfrmnl_ae_set(struct nl_sock*, struct xfrmnl_ae*, int);
+
+extern int              xfrmnl_ae_parse(struct nlmsghdr*, struct xfrmnl_ae **);
+extern int              xfrmnl_ae_build_get_request(struct nl_addr*, unsigned int, unsigned int,
+                                                    unsigned int, unsigned int, struct nl_msg **);
+
+extern struct nl_addr*  xfrmnl_ae_get_daddr (struct xfrmnl_ae*);
+extern int              xfrmnl_ae_set_daddr (struct xfrmnl_ae*, struct nl_addr*);
+
+extern int              xfrmnl_ae_get_spi (struct xfrmnl_ae*);
+extern int              xfrmnl_ae_set_spi (struct xfrmnl_ae*, unsigned int);
+
+extern int              xfrmnl_ae_get_family (struct xfrmnl_ae*);
+extern int              xfrmnl_ae_set_family (struct xfrmnl_ae*, unsigned int);
+
+extern int              xfrmnl_ae_get_proto (struct xfrmnl_ae*);
+extern int              xfrmnl_ae_set_proto (struct xfrmnl_ae*, unsigned int);
+
+extern struct nl_addr*  xfrmnl_ae_get_saddr (struct xfrmnl_ae*);
+extern int              xfrmnl_ae_set_saddr (struct xfrmnl_ae*, struct nl_addr*);
+
+extern int              xfrmnl_ae_get_flags (struct xfrmnl_ae*);
+extern int              xfrmnl_ae_set_flags (struct xfrmnl_ae*, unsigned int);
+
+extern int              xfrmnl_ae_get_reqid (struct xfrmnl_ae*);
+extern int              xfrmnl_ae_set_reqid (struct xfrmnl_ae*, unsigned int);
+
+extern int              xfrmnl_ae_get_mark (struct xfrmnl_ae*, unsigned int*, unsigned int*);
+extern int              xfrmnl_ae_set_mark (struct xfrmnl_ae*, unsigned int, unsigned int);
+
+extern int              xfrmnl_ae_get_curlifetime (struct xfrmnl_ae*, unsigned long long int*,
+                                                   unsigned long long int*, unsigned long long int*,
+                                                   unsigned long long int*);
+extern int              xfrmnl_ae_set_curlifetime (struct xfrmnl_ae*, unsigned long long int,
+                                                   unsigned long long int, unsigned long long int,
+                                                   unsigned long long int);
+
+extern int              xfrmnl_ae_get_replay_maxage (struct xfrmnl_ae*);
+extern int              xfrmnl_ae_set_replay_maxage (struct xfrmnl_ae*, unsigned int);
+
+extern int              xfrmnl_ae_get_replay_maxdiff (struct xfrmnl_ae*);
+extern int              xfrmnl_ae_set_replay_maxdiff (struct xfrmnl_ae*, unsigned int);
+
+extern int              xfrmnl_ae_get_replay_state (struct xfrmnl_ae*, unsigned int*, unsigned int*, unsigned int*);
+extern int              xfrmnl_ae_set_replay_state (struct xfrmnl_ae*, unsigned int, unsigned int, unsigned int);
+
+extern int              xfrmnl_ae_get_replay_state_esn (struct xfrmnl_ae*, unsigned int*, unsigned int*, unsigned int*,
+                                                        unsigned int*, unsigned int*, unsigned int*, unsigned int*);
+extern int              xfrmnl_ae_set_replay_state_esn (struct xfrmnl_ae*, unsigned int, unsigned int, unsigned int,
+                                                        unsigned int, unsigned int, unsigned int, unsigned int*);
+
+extern char*            xfrmnl_ae_flags2str(int, char *, size_t);
+extern int              xfrmnl_ae_str2flag(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/xfrm/lifetime.h b/include/netlink/xfrm/lifetime.h
new file mode 100644
index 0000000..a5d5955
--- /dev/null
+++ b/include/netlink/xfrm/lifetime.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *    Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *    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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  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 NETLINK_XFRM_LTIME_H_
+#define NETLINK_XFRM_LTIME_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/addr.h>
+#include <linux/xfrm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct xfrmnl_ltime_cfg;
+
+/* Creation */
+extern struct xfrmnl_ltime_cfg* xfrmnl_ltime_cfg_alloc(void);
+extern struct xfrmnl_ltime_cfg* xfrmnl_ltime_cfg_clone(struct xfrmnl_ltime_cfg*);
+
+/* Usage Management */
+extern struct xfrmnl_ltime_cfg* xfrmnl_ltime_cfg_get(struct xfrmnl_ltime_cfg*);
+extern void                     xfrmnl_ltime_cfg_put(struct xfrmnl_ltime_cfg*);
+extern int                      xfrmnl_ltime_cfg_shared(struct xfrmnl_ltime_cfg*);
+extern int                      xfrmnl_ltime_cfg_cmp(struct xfrmnl_ltime_cfg*, struct xfrmnl_ltime_cfg*);
+
+/* Access Functions */
+extern unsigned long long       xfrmnl_ltime_cfg_get_soft_bytelimit (struct xfrmnl_ltime_cfg*);
+extern int                      xfrmnl_ltime_cfg_set_soft_bytelimit (struct xfrmnl_ltime_cfg*, unsigned long long);
+
+extern unsigned long long       xfrmnl_ltime_cfg_get_hard_bytelimit (struct xfrmnl_ltime_cfg*);
+extern int                      xfrmnl_ltime_cfg_set_hard_bytelimit (struct xfrmnl_ltime_cfg*, unsigned long long);
+
+extern unsigned long long       xfrmnl_ltime_cfg_get_soft_packetlimit (struct xfrmnl_ltime_cfg*);
+extern int                      xfrmnl_ltime_cfg_set_soft_packetlimit (struct xfrmnl_ltime_cfg*, unsigned long long);
+
+extern unsigned long long       xfrmnl_ltime_cfg_get_hard_packetlimit (struct xfrmnl_ltime_cfg*);
+extern int                      xfrmnl_ltime_cfg_set_hard_packetlimit (struct xfrmnl_ltime_cfg*, unsigned long long);
+
+extern unsigned long long       xfrmnl_ltime_cfg_get_soft_addexpires (struct xfrmnl_ltime_cfg*);
+extern int                      xfrmnl_ltime_cfg_set_soft_addexpires (struct xfrmnl_ltime_cfg*, unsigned long long);
+
+extern unsigned long long       xfrmnl_ltime_cfg_get_hard_addexpires (struct xfrmnl_ltime_cfg*);
+extern int                      xfrmnl_ltime_cfg_set_hard_addexpires (struct xfrmnl_ltime_cfg*, unsigned long long);
+
+extern unsigned long long       xfrmnl_ltime_cfg_get_soft_useexpires (struct xfrmnl_ltime_cfg*);
+extern int                      xfrmnl_ltime_cfg_set_soft_useexpires (struct xfrmnl_ltime_cfg*, unsigned long long);
+
+extern unsigned long long       xfrmnl_ltime_cfg_get_hard_useexpires (struct xfrmnl_ltime_cfg*);
+extern int                      xfrmnl_ltime_cfg_set_hard_useexpires (struct xfrmnl_ltime_cfg*, unsigned long long);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/xfrm/sa.h b/include/netlink/xfrm/sa.h
new file mode 100644
index 0000000..7362c36
--- /dev/null
+++ b/include/netlink/xfrm/sa.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *    Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *    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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  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 NETLINK_XFRM_SA_H_
+#define NETLINK_XFRM_SA_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/addr.h>
+#include <linux/xfrm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct xfrmnl_sa;
+
+extern struct xfrmnl_sa*        xfrmnl_sa_alloc(void);
+extern void                     xfrmnl_sa_put(struct xfrmnl_sa *);
+
+extern int                      xfrmnl_sa_alloc_cache(struct nl_sock *, struct nl_cache **);
+extern struct xfrmnl_sa*        xfrmnl_sa_get(struct nl_cache*, struct nl_addr*, unsigned int, unsigned int);
+
+extern int                      xfrmnl_sa_parse(struct nlmsghdr *n, struct xfrmnl_sa **result);
+
+extern int                      xfrmnl_sa_build_get_request(struct nl_addr*, unsigned int, unsigned int,
+                                                            unsigned int, unsigned int, struct nl_msg **);
+extern int                      xfrmnl_sa_get_kernel(struct nl_sock*, struct nl_addr*, unsigned int,
+                                                     unsigned int, unsigned int, unsigned int, struct xfrmnl_sa**);
+
+extern int                      xfrmnl_sa_build_add_request(struct xfrmnl_sa*, int, struct nl_msg **);
+extern int                      xfrmnl_sa_add(struct nl_sock*, struct xfrmnl_sa*, int);
+
+extern int                      xfrmnl_sa_build_update_request(struct xfrmnl_sa*, int, struct nl_msg **);
+extern int                      xfrmnl_sa_update(struct nl_sock*, struct xfrmnl_sa*, int);
+
+extern int                      xfrmnl_sa_build_delete_request(struct xfrmnl_sa*, int, struct nl_msg **);
+extern int                      xfrmnl_sa_delete(struct nl_sock*, struct xfrmnl_sa*, int);
+
+extern struct xfrmnl_sel*       xfrmnl_sa_get_sel (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_sel (struct xfrmnl_sa*, struct xfrmnl_sel*);
+
+extern struct nl_addr*          xfrmnl_sa_get_daddr (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_daddr (struct xfrmnl_sa*, struct nl_addr*);
+
+extern int                      xfrmnl_sa_get_spi (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_spi (struct xfrmnl_sa*, unsigned int);
+
+extern int                      xfrmnl_sa_get_proto (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_proto (struct xfrmnl_sa*, unsigned int);
+
+extern struct nl_addr*          xfrmnl_sa_get_saddr (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_saddr (struct xfrmnl_sa*, struct nl_addr*);
+
+extern struct xfrmnl_ltime_cfg* xfrmnl_sa_get_lifetime_cfg (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_lifetime_cfg (struct xfrmnl_sa*, struct xfrmnl_ltime_cfg*);
+
+extern int                      xfrmnl_sa_get_curlifetime (struct xfrmnl_sa*, unsigned long long int*,
+                                                           unsigned long long int*, unsigned long long int*,
+                                                           unsigned long long int*);
+
+extern int                      xfrmnl_sa_get_stats (struct xfrmnl_sa*, unsigned long long int*,
+                                                     unsigned long long int*, unsigned long long int*);
+
+extern int                      xfrmnl_sa_get_seq (struct xfrmnl_sa*);
+
+extern int                      xfrmnl_sa_get_reqid (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_reqid (struct xfrmnl_sa*, unsigned int);
+
+extern int                      xfrmnl_sa_get_family (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_family (struct xfrmnl_sa*, unsigned int);
+
+extern int                      xfrmnl_sa_get_mode (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_mode (struct xfrmnl_sa*, unsigned int);
+
+extern int                      xfrmnl_sa_get_replay_window (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_replay_window (struct xfrmnl_sa*, unsigned int);
+
+extern int                      xfrmnl_sa_get_flags (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_flags (struct xfrmnl_sa*, unsigned int);
+
+extern int                      xfrmnl_sa_get_aead_params (struct xfrmnl_sa*, char*, unsigned int*,
+                                                           unsigned int*, char*);
+extern int                      xfrmnl_sa_set_aead_params (struct xfrmnl_sa*, const char*, unsigned int,
+                                                           unsigned int, const char*);
+
+extern int                      xfrmnl_sa_get_auth_params (struct xfrmnl_sa*, char*, unsigned int*,
+                                                           unsigned int*, char*);
+extern int                      xfrmnl_sa_set_auth_params (struct xfrmnl_sa*, const char*, unsigned int,
+                                                           unsigned int, const char*);
+
+extern int                      xfrmnl_sa_get_crypto_params (struct xfrmnl_sa*, char*, unsigned int*, char*);
+extern int                      xfrmnl_sa_set_crypto_params (struct xfrmnl_sa*, const char*, unsigned int,
+                                                             const char*);
+
+extern int                      xfrmnl_sa_get_comp_params (struct xfrmnl_sa*, char*, unsigned int*, char*);
+extern int                      xfrmnl_sa_set_comp_params (struct xfrmnl_sa*, const char*, unsigned int,
+                                                           const char*);
+
+extern int                      xfrmnl_sa_get_encap_tmpl (struct xfrmnl_sa*, unsigned int*, unsigned int*,
+                                                          unsigned int*, struct nl_addr**);
+extern int                      xfrmnl_sa_set_encap_tmpl (struct xfrmnl_sa*, unsigned int, unsigned int,
+                                                          unsigned int, struct nl_addr*);
+
+extern int                      xfrmnl_sa_get_tfcpad (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_tfcpad (struct xfrmnl_sa*, unsigned int);
+
+extern struct nl_addr*          xfrmnl_sa_get_coaddr (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_coaddr (struct xfrmnl_sa*, struct nl_addr*);
+
+extern int                      xfrmnl_sa_get_mark (struct xfrmnl_sa*, unsigned int*, unsigned int*);
+extern int                      xfrmnl_sa_set_mark (struct xfrmnl_sa*, unsigned int, unsigned int);
+
+extern int                      xfrmnl_sa_get_sec_ctx (struct xfrmnl_sa*, unsigned int*, unsigned int*,
+                                                       unsigned int*, unsigned int*, char*);
+extern int                      xfrmnl_sa_set_sec_ctx (struct xfrmnl_sa*, unsigned int, unsigned int,
+                                                       unsigned int, unsigned int, const char*);
+
+extern int                      xfrmnl_sa_get_replay_maxage (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_replay_maxage (struct xfrmnl_sa*, unsigned int);
+
+extern int                      xfrmnl_sa_get_replay_maxdiff (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_set_replay_maxdiff (struct xfrmnl_sa*, unsigned int);
+
+extern int                      xfrmnl_sa_get_replay_state (struct xfrmnl_sa*, unsigned int*,
+                                                            unsigned int*, unsigned int*);
+extern int                      xfrmnl_sa_set_replay_state (struct xfrmnl_sa*, unsigned int,
+                                                            unsigned int, unsigned int);
+
+extern int                      xfrmnl_sa_get_replay_state_esn (struct xfrmnl_sa*, unsigned int*, unsigned int*,
+                                                                unsigned int*, unsigned int*, unsigned int*,
+                                                                unsigned int*, unsigned int*);
+extern int                      xfrmnl_sa_set_replay_state_esn (struct xfrmnl_sa*, unsigned int, unsigned int,
+                                                                unsigned int, unsigned int, unsigned int,
+                                                                unsigned int, unsigned int*);
+
+extern int                      xfrmnl_sa_is_expiry_reached (struct xfrmnl_sa*);
+extern int                      xfrmnl_sa_is_hardexpiry_reached (struct xfrmnl_sa*);
+
+extern char*                    xfrmnl_sa_flags2str(int, char *, size_t);
+extern int                      xfrmnl_sa_str2flag(const char *);
+
+extern char*                    xfrmnl_sa_mode2str(int, char *, size_t);
+extern int                      xfrmnl_sa_str2mode(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/xfrm/selector.h b/include/netlink/xfrm/selector.h
new file mode 100644
index 0000000..2ee6842
--- /dev/null
+++ b/include/netlink/xfrm/selector.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *    Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *    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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  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 NETLINK_XFRM_SEL_H_
+#define NETLINK_XFRM_SEL_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/addr.h>
+#include <linux/xfrm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct xfrmnl_sel;
+
+/* Creation */
+extern struct xfrmnl_sel*       xfrmnl_sel_alloc(void);
+extern struct xfrmnl_sel*       xfrmnl_sel_clone(struct xfrmnl_sel*);
+
+/* Usage Management */
+extern struct xfrmnl_sel*       xfrmnl_sel_get(struct xfrmnl_sel*);
+extern void                     xfrmnl_sel_put(struct xfrmnl_sel*);
+extern int                      xfrmnl_sel_shared(struct xfrmnl_sel*);
+extern int                      xfrmnl_sel_cmp(struct xfrmnl_sel*, struct xfrmnl_sel*);
+extern void                     xfrmnl_sel_dump(struct xfrmnl_sel*, struct nl_dump_params *);
+
+/* Access Functions */
+extern struct nl_addr*          xfrmnl_sel_get_daddr (struct xfrmnl_sel*);
+extern int                      xfrmnl_sel_set_daddr (struct xfrmnl_sel*, struct nl_addr*);
+
+extern struct nl_addr*          xfrmnl_sel_get_saddr (struct xfrmnl_sel*);
+extern int                      xfrmnl_sel_set_saddr (struct xfrmnl_sel*, struct nl_addr*);
+
+extern int                      xfrmnl_sel_get_dport (struct xfrmnl_sel*);
+extern int                      xfrmnl_sel_set_dport (struct xfrmnl_sel*, unsigned int);
+
+extern int                      xfrmnl_sel_get_dportmask (struct xfrmnl_sel*);
+extern int                      xfrmnl_sel_set_dportmask (struct xfrmnl_sel*, unsigned int);
+
+extern int                      xfrmnl_sel_get_sport (struct xfrmnl_sel*);
+extern int                      xfrmnl_sel_set_sport (struct xfrmnl_sel*, unsigned int);
+
+extern int                      xfrmnl_sel_get_sportmask (struct xfrmnl_sel*);
+extern int                      xfrmnl_sel_set_sportmask (struct xfrmnl_sel*, unsigned int);
+
+extern int                      xfrmnl_sel_get_family (struct xfrmnl_sel*);
+extern int                      xfrmnl_sel_set_family (struct xfrmnl_sel*, unsigned int);
+
+extern int                      xfrmnl_sel_get_prefixlen_d (struct xfrmnl_sel*);
+extern int                      xfrmnl_sel_set_prefixlen_d (struct xfrmnl_sel*, unsigned int);
+
+extern int                      xfrmnl_sel_get_prefixlen_s (struct xfrmnl_sel*);
+extern int                      xfrmnl_sel_set_prefixlen_s (struct xfrmnl_sel*, unsigned int);
+
+extern int                      xfrmnl_sel_get_proto (struct xfrmnl_sel*);
+extern int                      xfrmnl_sel_set_proto (struct xfrmnl_sel*, unsigned int);
+
+extern int                      xfrmnl_sel_get_ifindex (struct xfrmnl_sel*);
+extern int                      xfrmnl_sel_set_ifindex (struct xfrmnl_sel*, unsigned int);
+
+extern int                      xfrmnl_sel_get_userid (struct xfrmnl_sel*);
+extern int                      xfrmnl_sel_set_userid (struct xfrmnl_sel*, unsigned int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/xfrm/sp.h b/include/netlink/xfrm/sp.h
new file mode 100644
index 0000000..84cbfb2
--- /dev/null
+++ b/include/netlink/xfrm/sp.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *    Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *    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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  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 NETLINK_XFRM_SP_H_
+#define NETLINK_XFRM_SP_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/addr.h>
+#include <netlink/xfrm/template.h>
+#include <netlink/xfrm/lifetime.h>
+#include <linux/xfrm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct xfrmnl_sp;
+
+extern struct xfrmnl_sp*        xfrmnl_sp_alloc(void);
+extern void                     xfrmnl_sp_put(struct xfrmnl_sp *);
+
+extern int                      xfrmnl_sp_alloc_cache(struct nl_sock *, struct nl_cache **);
+extern struct xfrmnl_sp*        xfrmnl_sp_get(struct nl_cache*, unsigned int, unsigned int);
+
+extern int                      xfrmnl_sp_parse(struct nlmsghdr *n, struct xfrmnl_sp **result);
+
+extern int                      xfrmnl_sp_build_get_request(unsigned int, unsigned int, unsigned int,
+                                                            unsigned int, struct nl_msg **);
+extern int                      xfrmnl_sp_get_kernel(struct nl_sock*, unsigned int, unsigned int,
+                                                     unsigned int, unsigned int, struct xfrmnl_sp**);
+
+extern int                      xfrmnl_sp_add(struct nl_sock*, struct xfrmnl_sp*, int);
+extern int                      xfrmnl_sp_build_add_request(struct xfrmnl_sp*, int, struct nl_msg **);
+
+extern int                      xfrmnl_sp_update(struct nl_sock*, struct xfrmnl_sp*, int);
+extern int                      xfrmnl_sp_build_update_request(struct xfrmnl_sp*, int, struct nl_msg **);
+
+extern int                      xfrmnl_sp_delete(struct nl_sock*, struct xfrmnl_sp*, int);
+extern int                      xfrmnl_sp_build_delete_request(struct xfrmnl_sp*, int, struct nl_msg **);
+
+extern struct xfrmnl_sel*       xfrmnl_sp_get_sel (struct xfrmnl_sp*);
+extern int                      xfrmnl_sp_set_sel (struct xfrmnl_sp*, struct xfrmnl_sel*);
+
+extern struct xfrmnl_ltime_cfg* xfrmnl_sp_get_lifetime_cfg (struct xfrmnl_sp*);
+extern int                      xfrmnl_sp_set_lifetime_cfg (struct xfrmnl_sp*, struct xfrmnl_ltime_cfg*);
+
+extern int                      xfrmnl_sp_get_curlifetime (struct xfrmnl_sp*, unsigned long long int*,
+                                                           unsigned long long int*, unsigned long long int*,
+                                                           unsigned long long int*);
+
+extern int                      xfrmnl_sp_get_priority (struct xfrmnl_sp*);
+extern int                      xfrmnl_sp_set_priority (struct xfrmnl_sp*, unsigned int);
+
+extern int                      xfrmnl_sp_get_index (struct xfrmnl_sp*);
+extern int                      xfrmnl_sp_set_index (struct xfrmnl_sp*, unsigned int);
+
+extern int                      xfrmnl_sp_get_dir (struct xfrmnl_sp*);
+extern int                      xfrmnl_sp_set_dir (struct xfrmnl_sp*, unsigned int);
+
+extern int                      xfrmnl_sp_get_action (struct xfrmnl_sp*);
+extern int                      xfrmnl_sp_set_action (struct xfrmnl_sp*, unsigned int);
+
+extern int                      xfrmnl_sp_get_flags (struct xfrmnl_sp*);
+extern int                      xfrmnl_sp_set_flags (struct xfrmnl_sp*, unsigned int);
+
+extern int                      xfrmnl_sp_get_share (struct xfrmnl_sp*);
+extern int                      xfrmnl_sp_set_share (struct xfrmnl_sp*, unsigned int);
+
+extern int                      xfrmnl_sp_get_sec_ctx (struct xfrmnl_sp*, unsigned int*, unsigned int*,
+                                                       unsigned int*, unsigned int*, unsigned int*, char*);
+extern int                      xfrmnl_sp_set_sec_ctx (struct xfrmnl_sp*, unsigned int, unsigned int,
+                                                       unsigned int, unsigned int, unsigned int, char*);
+
+extern int                      xfrmnl_sp_get_userpolicy_type (struct xfrmnl_sp*);
+extern int                      xfrmnl_sp_set_userpolicy_type (struct xfrmnl_sp*, unsigned int);
+
+extern void                     xfrmnl_sp_add_usertemplate(struct xfrmnl_sp*, struct xfrmnl_user_tmpl*);
+extern void                     xfrmnl_sp_remove_usertemplate(struct xfrmnl_sp*, struct xfrmnl_user_tmpl*);
+extern struct nl_list_head*     xfrmnl_sp_get_usertemplates(struct xfrmnl_sp*);
+extern int                      xfrmnl_sp_get_nusertemplates(struct xfrmnl_sp*);
+extern void                     xfrmnl_sp_foreach_usertemplate(struct xfrmnl_sp*,
+                                                               void (*cb)(struct xfrmnl_user_tmpl*, void *),
+                                                               void *arg);
+extern struct xfrmnl_user_tmpl* xfrmnl_sp_usertemplate_n(struct xfrmnl_sp*, int);
+
+extern int                      xfrmnl_sp_get_mark (struct xfrmnl_sp*, unsigned int*, unsigned int*);
+extern int                      xfrmnl_sp_set_mark (struct xfrmnl_sp*, unsigned int, unsigned int);
+
+extern char*                    xfrmnl_sp_action2str(int, char *, size_t);
+extern int                      xfrmnl_sp_str2action(const char *);
+
+extern char*                    xfrmnl_sp_flags2str(int, char *, size_t);
+extern int                      xfrmnl_sp_str2flag(const char *);
+
+extern char*                    xfrmnl_sp_type2str(int, char *, size_t);
+extern int                      xfrmnl_sp_str2type(const char *);
+
+extern char*                    xfrmnl_sp_dir2str(int, char *, size_t);
+extern int                      xfrmnl_sp_str2dir(const char *);
+
+extern char*                    xfrmnl_sp_share2str(int, char *, size_t);
+extern int                      xfrmnl_sp_str2share(const char *);
+
+extern int                      xfrmnl_sp_index2dir (unsigned int);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/xfrm/template.h b/include/netlink/xfrm/template.h
new file mode 100644
index 0000000..da51e7d
--- /dev/null
+++ b/include/netlink/xfrm/template.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *    Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *    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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  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 NETLINK_XFRM_TEMPL_H_
+#define NETLINK_XFRM_TEMPL_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/addr.h>
+#include <linux/xfrm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct xfrmnl_user_tmpl;
+
+/* Creation */
+extern struct xfrmnl_user_tmpl* xfrmnl_user_tmpl_alloc(void);
+extern struct xfrmnl_user_tmpl* xfrmnl_user_tmpl_clone(struct xfrmnl_user_tmpl*);
+extern void                     xfrmnl_user_tmpl_free(struct xfrmnl_user_tmpl* utmpl);
+
+/* Utility functions */
+extern int                      xfrmnl_user_tmpl_cmp(struct xfrmnl_user_tmpl*, struct xfrmnl_user_tmpl*);
+extern void                     xfrmnl_user_tmpl_dump(struct xfrmnl_user_tmpl*, struct nl_dump_params*);
+
+/* Access Functions */
+extern struct nl_addr*          xfrmnl_user_tmpl_get_daddr (struct xfrmnl_user_tmpl*);
+extern int                      xfrmnl_user_tmpl_set_daddr (struct xfrmnl_user_tmpl*, struct nl_addr*);
+
+extern int                      xfrmnl_user_tmpl_get_spi (struct xfrmnl_user_tmpl*);
+extern int                      xfrmnl_user_tmpl_set_spi (struct xfrmnl_user_tmpl*, unsigned int);
+
+extern int                      xfrmnl_user_tmpl_get_proto (struct xfrmnl_user_tmpl*);
+extern int                      xfrmnl_user_tmpl_set_proto (struct xfrmnl_user_tmpl*, unsigned int);
+
+extern int                      xfrmnl_user_tmpl_get_family (struct xfrmnl_user_tmpl*);
+extern int                      xfrmnl_user_tmpl_set_family (struct xfrmnl_user_tmpl*, unsigned int);
+
+extern struct nl_addr*          xfrmnl_user_tmpl_get_saddr (struct xfrmnl_user_tmpl*);
+extern int                      xfrmnl_user_tmpl_set_saddr (struct xfrmnl_user_tmpl*, struct nl_addr*);
+
+extern int                      xfrmnl_user_tmpl_get_reqid (struct xfrmnl_user_tmpl*);
+extern int                      xfrmnl_user_tmpl_set_reqid (struct xfrmnl_user_tmpl*, unsigned int);
+
+extern int                      xfrmnl_user_tmpl_get_mode (struct xfrmnl_user_tmpl*);
+extern int                      xfrmnl_user_tmpl_set_mode (struct xfrmnl_user_tmpl*, unsigned int);
+
+extern int                      xfrmnl_user_tmpl_get_share (struct xfrmnl_user_tmpl*);
+extern int                      xfrmnl_user_tmpl_set_share (struct xfrmnl_user_tmpl*, unsigned int);
+
+extern int                      xfrmnl_user_tmpl_get_optional (struct xfrmnl_user_tmpl*);
+extern int                      xfrmnl_user_tmpl_set_optional (struct xfrmnl_user_tmpl*, unsigned int);
+
+extern int                      xfrmnl_user_tmpl_get_aalgos (struct xfrmnl_user_tmpl*);
+extern int                      xfrmnl_user_tmpl_set_aalgos (struct xfrmnl_user_tmpl*, unsigned int);
+
+extern int                      xfrmnl_user_tmpl_get_ealgos (struct xfrmnl_user_tmpl*);
+extern int                      xfrmnl_user_tmpl_set_ealgos (struct xfrmnl_user_tmpl*, unsigned int);
+
+extern int                      xfrmnl_user_tmpl_get_calgos (struct xfrmnl_user_tmpl*);
+extern int                      xfrmnl_user_tmpl_set_calgos (struct xfrmnl_user_tmpl*, unsigned int);
+
+extern char*                    xfrmnl_user_tmpl_mode2str(int, char *, size_t);
+extern int                      xfrmnl_user_tmpl_str2mode(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/Makefile.am b/lib/Makefile.am
deleted file mode 100644
index 1ae37a9..0000000
--- a/lib/Makefile.am
+++ /dev/null
@@ -1,134 +0,0 @@
-# -*- Makefile -*-
-
-AM_CPPFLAGS  =			 	\
-	-Wall \
-	-I${top_srcdir}/include \
-	-I${top_builddir}/include \
-	-I${builddir}/route \
-	-I${builddir}/route/cls \
-	-D_GNU_SOURCE \
-	-DSYSCONFDIR=\"$(sysconfdir)/libnl\"
-
-AM_LDFLAGS = \
-	-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
-	-Wl,--version-script=$(top_builddir)/libnl.sym
-
-lib_LTLIBRARIES = \
-	libnl-3.la libnl-genl-3.la libnl-route-3.la libnl-nf-3.la libnl-idiag-3.la
-
-libnl_3_la_SOURCES = \
-	addr.c attr.c cache.c cache_mngr.c cache_mngt.c data.c \
-	error.c handlers.c msg.c nl.c object.c socket.c utils.c \
-	version.c hash.c hashtable.c
-
-libnl_idiag_3_la_LIBADD = libnl-3.la
-libnl_idiag_3_la_SOURCES = \
-	idiag/idiag_meminfo_obj.c idiag/idiag_vegasinfo_obj.c \
-	idiag/idiag_msg_obj.c idiag/idiag_req_obj.c idiag/idiag.c
-
-libnl_genl_3_la_LIBADD  = libnl-3.la
-libnl_genl_3_la_SOURCES = \
-	genl/ctrl.c genl/family.c genl/genl.c genl/mngt.c
-
-libnl_nf_3_la_LIBADD  = libnl-route-3.la
-libnl_nf_3_la_SOURCES = \
-	netfilter/ct.c netfilter/ct_obj.c netfilter/log.c \
-	netfilter/log_msg.c netfilter/log_msg_obj.c netfilter/log_obj.c \
-	netfilter/netfilter.c netfilter/nfnl.c netfilter/queue.c \
-	netfilter/queue_msg.c netfilter/queue_msg_obj.c netfilter/queue_obj.c \
-	netfilter/exp.c netfilter/exp_obj.c
-
-CLEANFILES = \
-	route/pktloc_grammar.c route/pktloc_grammar.h \
-	route/pktloc_syntax.c route/pktloc_syntax.h \
-	route/cls/ematch_grammar.c route/cls/ematch_grammar.h \
-	route/cls/ematch_syntax.c route/cls/ematch_syntax.h
-
-# Hack to avoid using ylwrap. It does not function correctly in combination
-# with --header-file=
-route/pktloc_grammar.c: route/pktloc_grammar.l
-	$(AM_V_GEN) $(MKDIR_P) route; $(FLEX) --header-file=route/pktloc_grammar.h $(LFLAGS) -o $@ $^
-
-route/pktloc_syntax.c: route/pktloc_syntax.y
-	$(AM_V_GEN) $(MKDIR_P) route; $(YACC) -d $(YFLAGS) -o $@ $^
-
-route/cls/ematch_grammar.c: route/cls/ematch_grammar.l
-	$(AM_V_GEN) $(MKDIR_P) route/cls; $(FLEX) --header-file=route/cls/ematch_grammar.h $(LFLAGS) -o $@ $^
-
-route/cls/ematch_syntax.c: route/cls/ematch_syntax.y
-	$(AM_V_GEN) $(MKDIR_P) route/cls; $(YACC) -d $(YFLAGS) -o $@ $^
-
-libnl_route_3_la_LIBADD  = libnl-3.la
-libnl_route_3_la_SOURCES = \
-	route/addr.c route/class.c route/cls.c route/act.c route/link.c \
-	route/neigh.c route/neightbl.c route/nexthop.c route/qdisc.c \
-	route/route.c route/route_obj.c route/route_utils.c route/rtnl.c \
-	route/rule.c route/tc.c route/classid.c \
-	\
-	route/cls/fw.c route/cls/police.c route/cls/u32.c route/cls/basic.c \
-	route/cls/cgroup.c \
-	\
-	route/act/mirred.c \
-	\
-	route/cls/ematch.c \
-	route/cls/ematch/container.c route/cls/ematch/cmp.c \
-	route/cls/ematch/nbyte.c route/cls/ematch/text.c \
-	route/cls/ematch/meta.c \
-	\
-	route/link/api.c route/link/vlan.c route/link/dummy.c \
-	route/link/bridge.c route/link/inet6.c route/link/inet.c \
-	route/link/bonding.c route/link/can.c route/link/macvlan.c \
-	route/link/vxlan.c route/link/veth.c route/link/ipip.c \
-	route/link/ipgre.c route/link/sit.c route/link/ipvti.c \
-	route/link/ip6tnl.c \
-	\
-	route/qdisc/blackhole.c route/qdisc/cbq.c route/qdisc/dsmark.c \
-	route/qdisc/fifo.c route/qdisc/htb.c route/qdisc/netem.c \
-	route/qdisc/prio.c route/qdisc/red.c route/qdisc/sfq.c \
-	route/qdisc/tbf.c route/qdisc/plug.c route/qdisc/ingress.c \
-	route/qdisc/fq_codel.c \
-	\
-	fib_lookup/lookup.c fib_lookup/request.c \
-	\
-	route/pktloc.c
-
-nodist_libnl_route_3_la_SOURCES = \
-	route/pktloc_syntax.c route/pktloc_syntax.h \
-	route/pktloc_grammar.c route/pktloc_grammar.h \
-	route/cls/ematch_syntax.c route/cls/ematch_syntax.h \
-	route/cls/ematch_grammar.c route/cls/ematch_grammar.h
-
-BUILT_SOURCES = \
-	route/cls/ematch_grammar.c \
-	route/cls/ematch_syntax.c \
-	route/pktloc_grammar.c \
-	route/pktloc_syntax.c
-
-EXTRA_DIST = \
-	route/pktloc_grammar.l \
-	route/pktloc_syntax.y \
-	route/cls/ematch_grammar.l \
-	route/cls/ematch_syntax.y
-
-if ENABLE_CLI
-nobase_pkglib_LTLIBRARIES = \
-	cli/qdisc/htb.la \
-	cli/qdisc/blackhole.la \
-	cli/qdisc/pfifo.la \
-	cli/qdisc/plug.la \
-	cli/qdisc/bfifo.la \
-	cli/qdisc/ingress.la \
-	cli/qdisc/fq_codel.la \
-	cli/cls/basic.la \
-	cli/cls/cgroup.la
-
-cli_qdisc_htb_la_LDFLAGS = -module -avoid-version
-cli_qdisc_blackhole_la_LDFLAGS = -module -avoid-version
-cli_qdisc_pfifo_la_LDFLAGS = -module -avoid-version
-cli_qdisc_plug_la_LDFLAGS = -module -avoid-version
-cli_qdisc_bfifo_la_LDFLAGS = -module -avoid-version
-cli_qdisc_ingress_la_LDFLAGS = -module -avoid-version
-cli_qdisc_fq_codel_la_LDFLAGS = -module -avoid-version
-cli_cls_basic_la_LDFLAGS = -module -avoid-version
-cli_cls_cgroup_la_LDFLAGS = -module -avoid-version
-endif
diff --git a/lib/addr.c b/lib/addr.c
index 54d2b1d..06f3138 100644
--- a/lib/addr.c
+++ b/lib/addr.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/addr.c		Network Address
  *
@@ -31,6 +32,7 @@
 #include <netlink/netlink.h>
 #include <netlink/utils.h>
 #include <netlink/addr.h>
+#include <netlink-private/route/mpls.h>
 #include <linux/socket.h>
 
 /* All this DECnet stuff is stolen from iproute2, thanks to whoever wrote
@@ -65,7 +67,7 @@
 	return 0;
 }
 
-static const char *dnet_ntop(char *addrbuf, size_t addrlen, char *str,
+static const char *dnet_ntop(const char *addrbuf, size_t addrlen, char *str,
 			     size_t len)
 {
 	uint16_t addr = dn_ntohs(*(uint16_t *)addrbuf);
@@ -213,7 +215,7 @@
  *
  * @return Allocated address object or NULL upon failure.
  */
-struct nl_addr *nl_addr_build(int family, void *buf, size_t size)
+struct nl_addr *nl_addr_build(int family, const void *buf, size_t size)
 {
 	struct nl_addr *addr;
 
@@ -223,7 +225,13 @@
 
 	addr->a_family = family;
 	addr->a_len = size;
-	addr->a_prefixlen = size*8;
+	switch(family) {
+	case AF_MPLS:
+		addr->a_prefixlen = 20;  /* MPLS address is a 20-bit label */
+		break;
+	default:
+		addr->a_prefixlen = size*8;
+	}
 
 	if (size)
 		memcpy(addr->a_addr, buf, size);
@@ -252,7 +260,7 @@
  *
  * @return Allocated address object or NULL upon failure.
  */
-struct nl_addr *nl_addr_alloc_attr(struct nlattr *nla, int family)
+struct nl_addr *nl_addr_alloc_attr(const struct nlattr *nla, int family)
 {
 	return nl_addr_build(family, nla_data(nla), nla_len(nla));
 }
@@ -290,8 +298,8 @@
  */
 int nl_addr_parse(const char *addrstr, int hint, struct nl_addr **result)
 {
-	int err, copy = 0, len = 0, family = AF_UNSPEC;
-	char *str, *prefix, buf[32];
+	int err, copy = 0, len = 0, family = AF_UNSPEC, plen = 0;
+	char *str, *prefix = NULL, buf[256];
 	struct nl_addr *addr = NULL; /* gcc ain't that smart */
 
 	str = strdup(addrstr);
@@ -300,9 +308,11 @@
 		goto errout;
 	}
 
-	prefix = strchr(str, '/');
-	if (prefix)
-		*prefix = '\0';
+	if (hint != AF_MPLS) {
+		prefix = strchr(str, '/');
+		if (prefix)
+			*prefix = '\0';
+	}
 
 	if (!strcasecmp(str, "none")) {
 		family = hint;
@@ -399,6 +409,17 @@
 		}
 	}
 
+	if (hint == AF_MPLS) {
+		len = mpls_pton(AF_MPLS, str, buf, sizeof(buf));
+		if (len <= 0) {
+			err = -NLE_INVAL;
+			goto errout;
+		}
+		family = AF_MPLS;
+		plen = 20;
+		goto prefix;
+	}
+
 	if (hint == AF_UNSPEC && strchr(str, ':')) {
 		size_t i = 0;
 		char *s = str, *p;
@@ -445,9 +466,11 @@
 			goto errout;
 		}
 		nl_addr_set_prefixlen(addr, pl);
-	} else
-		nl_addr_set_prefixlen(addr, len * 8);
-
+	} else {
+		if (!plen)
+			plen = len * 8;
+		nl_addr_set_prefixlen(addr, plen);
+	}
 	*result = addr;
 	err = 0;
 errout:
@@ -468,7 +491,7 @@
  *
  * @return Allocated abstract address or NULL upon failure.
  */
-struct nl_addr *nl_addr_clone(struct nl_addr *addr)
+struct nl_addr *nl_addr_clone(const struct nl_addr *addr)
 {
 	struct nl_addr *new;
 
@@ -531,7 +554,7 @@
  *
  * @return Non-zero if the abstract address is shared, otherwise 0.
  */
-int nl_addr_shared(struct nl_addr *addr)
+int nl_addr_shared(const struct nl_addr *addr)
 {
 	return addr->a_refcnt > 1;
 }
@@ -560,10 +583,18 @@
  * @return Integer less than, equal to or greather than zero if the two
  *         addresses match.
  */
-int nl_addr_cmp(struct nl_addr *a, struct nl_addr *b)
+int nl_addr_cmp(const struct nl_addr *a, const struct nl_addr *b)
 {
-	int d = a->a_family - b->a_family;
+	int d;
 
+	if (a == b)
+		return 0;
+	if (!a)
+		return -1;
+	if (!b)
+		return 1;
+
+	d = a->a_family - b->a_family;
 	if (d == 0) {
 		d = a->a_len - b->a_len;
 
@@ -591,7 +622,7 @@
  * @return Integer less than, equal to or greather than zero if the two
  *         addresses match.
  */
-int nl_addr_cmp_prefix(struct nl_addr *a, struct nl_addr *b)
+int nl_addr_cmp_prefix(const struct nl_addr *a, const struct nl_addr *b)
 {
 	int d = a->a_family - b->a_family;
 
@@ -617,7 +648,7 @@
  *
  * @return 1 if the binary address consists of all zeros, 0 otherwise.
  */
-int nl_addr_iszero(struct nl_addr *addr)
+int nl_addr_iszero(const struct nl_addr *addr)
 {
 	unsigned int i;
 
@@ -636,10 +667,10 @@
  * @return 1 if the address is parseable assuming the specified address family,
  *         otherwise 0 is returned.
  */
-int nl_addr_valid(char *addr, int family)
+int nl_addr_valid(const char *addr, int family)
 {
 	int ret;
-	char buf[32];
+	char buf[256]; /* MPLS has N-labels at 4-bytes / label */
 
 	switch (family) {
 	case AF_INET:
@@ -649,6 +680,12 @@
 			return 0;
 		break;
 
+	case AF_MPLS:
+		ret = mpls_pton(family, addr, buf, sizeof(buf));
+		if (ret <= 0)
+			return 0;
+		break;
+
 	case AF_DECnet:
 		ret = dnet_pton(addr, buf);
 		if (ret <= 0)
@@ -670,7 +707,7 @@
  *
  * @return Numeric address family or AF_UNSPEC
  */
-int nl_addr_guess_family(struct nl_addr *addr)
+int nl_addr_guess_family(const struct nl_addr *addr)
 {
 	switch (addr->a_len) {
 		case 4:
@@ -697,7 +734,7 @@
  *
  * @return 0 on success or a negative error code
  */
-int nl_addr_fill_sockaddr(struct nl_addr *addr, struct sockaddr *sa,
+int nl_addr_fill_sockaddr(const struct nl_addr *addr, struct sockaddr *sa,
 			  socklen_t *salen)
 {
 	switch (addr->a_family) {
@@ -707,8 +744,14 @@
 		if (*salen < sizeof(*sai))
 			return -NLE_INVAL;
 
+		if (addr->a_len == 4)
+			memcpy(&sai->sin_addr, addr->a_addr, 4);
+		else if (addr->a_len != 0)
+			return -NLE_INVAL;
+		else
+			memset(&sai->sin_addr, 0, 4);
+
 		sai->sin_family = addr->a_family;
-		memcpy(&sai->sin_addr, addr->a_addr, 4);
 		*salen = sizeof(*sai);
 	}
 		break;
@@ -719,8 +762,14 @@
 		if (*salen < sizeof(*sa6))
 			return -NLE_INVAL;
 
+		if (addr->a_len == 16)
+			memcpy(&sa6->sin6_addr, addr->a_addr, 16);
+		else if (addr->a_len != 0)
+			return -NLE_INVAL;
+		else
+			memset(&sa6->sin6_addr, 0, 16);
+
 		sa6->sin6_family = addr->a_family;
-		memcpy(&sa6->sin6_addr, addr->a_addr, 16);
 		*salen = sizeof(*sa6);
 	}
 		break;
@@ -753,7 +802,7 @@
  *
  * @return 0 on success or a negative error code.
  */
-int nl_addr_info(struct nl_addr *addr, struct addrinfo **result)
+int nl_addr_info(const struct nl_addr *addr, struct addrinfo **result)
 {
 	int err;
 	char buf[INET6_ADDRSTRLEN+5];
@@ -797,7 +846,7 @@
  *
  * @return 0 on success or a negative error code.
  */
-int nl_addr_resolve(struct nl_addr *addr, char *host, size_t hostlen)
+int nl_addr_resolve(const struct nl_addr *addr, char *host, size_t hostlen)
 {
 	int err;
 	struct sockaddr_in6 buf;
@@ -842,7 +891,7 @@
  *
  * @return The numeric address family or `AF_UNSPEC`
  */
-int nl_addr_get_family(struct nl_addr *addr)
+int nl_addr_get_family(const struct nl_addr *addr)
 {
 	return addr->a_family;
 }
@@ -867,7 +916,7 @@
  *
  * @return 0 on success or a negative error code.
  */
-int nl_addr_set_binary_addr(struct nl_addr *addr, void *buf, size_t len)
+int nl_addr_set_binary_addr(struct nl_addr *addr, const void *buf, size_t len)
 {
 	if (len > addr->a_maxsize)
 		return -NLE_RANGE;
@@ -890,9 +939,9 @@
  *
  * @return Pointer to binary address of length nl_addr_get_len()
  */
-void *nl_addr_get_binary_addr(struct nl_addr *addr)
+void *nl_addr_get_binary_addr(const struct nl_addr *addr)
 {
-	return addr->a_addr;
+	return (void*)addr->a_addr;
 }
 
 /**
@@ -902,7 +951,7 @@
  * @see nl_addr_get_binary_addr()
  * @see nl_addr_set_binary_addr()
  */
-unsigned int nl_addr_get_len(struct nl_addr *addr)
+unsigned int nl_addr_get_len(const struct nl_addr *addr)
 {
 	return addr->a_len;
 }
@@ -925,7 +974,7 @@
  *
  * @see nl_addr_set_prefixlen()
  */
-unsigned int nl_addr_get_prefixlen(struct nl_addr *addr)
+unsigned int nl_addr_get_prefixlen(const struct nl_addr *addr)
 {
 	return addr->a_prefixlen;
 }
@@ -948,7 +997,7 @@
  *
  * @return Address represented in ASCII stored in destination buffer.
  */
-char *nl_addr2str(struct nl_addr *addr, char *buf, size_t size)
+char *nl_addr2str(const struct nl_addr *addr, char *buf, size_t size)
 {
 	unsigned int i;
 	char tmp[16];
@@ -970,6 +1019,10 @@
 			inet_ntop(AF_INET6, addr->a_addr, buf, size);
 			break;
 
+		case AF_MPLS:
+			mpls_ntop(AF_MPLS, addr->a_addr, buf, size);
+			break;
+
 		case AF_DECnet:
 			dnet_ntop(addr->a_addr, addr->a_len, buf, size);
 			break;
@@ -987,7 +1040,8 @@
 	}
 
 prefix:
-	if (addr->a_prefixlen != (8 * addr->a_len)) {
+	if (addr->a_family != AF_MPLS &&
+	    addr->a_prefixlen != (8 * addr->a_len)) {
 		snprintf(tmp, sizeof(tmp), "/%u", addr->a_prefixlen);
 		strncat(buf, tmp, size - strlen(buf) - 1);
 	}
@@ -1003,66 +1057,70 @@
  */
 
 static const struct trans_tbl afs[] = {
-	__ADD(AF_UNSPEC,unspec)
-	__ADD(AF_UNIX,unix)
-	__ADD(AF_INET,inet)
-	__ADD(AF_AX25,ax25)
-	__ADD(AF_IPX,ipx)
-	__ADD(AF_APPLETALK,appletalk)
-	__ADD(AF_NETROM,netrom)
-	__ADD(AF_BRIDGE,bridge)
-	__ADD(AF_ATMPVC,atmpvc)
-	__ADD(AF_X25,x25)
-	__ADD(AF_INET6,inet6)
-	__ADD(AF_ROSE,rose)
-	__ADD(AF_DECnet,decnet)
-	__ADD(AF_NETBEUI,netbeui)
-	__ADD(AF_SECURITY,security)
-	__ADD(AF_KEY,key)
-	__ADD(AF_NETLINK,netlink)
-	__ADD(AF_PACKET,packet)
-	__ADD(AF_ASH,ash)
-	__ADD(AF_ECONET,econet)
-	__ADD(AF_ATMSVC,atmsvc)
+	__ADD(AF_UNSPEC,unspec),
+	__ADD(AF_UNIX,unix),
+	__ADD(AF_INET,inet),
+	__ADD(AF_AX25,ax25),
+	__ADD(AF_IPX,ipx),
+	__ADD(AF_APPLETALK,appletalk),
+	__ADD(AF_NETROM,netrom),
+	__ADD(AF_BRIDGE,bridge),
+	__ADD(AF_ATMPVC,atmpvc),
+	__ADD(AF_X25,x25),
+	__ADD(AF_INET6,inet6),
+	__ADD(AF_ROSE,rose),
+	__ADD(AF_DECnet,decnet),
+	__ADD(AF_NETBEUI,netbeui),
+	__ADD(AF_SECURITY,security),
+	__ADD(AF_KEY,key),
+	__ADD(AF_NETLINK,netlink),
+	__ADD(AF_PACKET,packet),
+	__ADD(AF_ASH,ash),
+	__ADD(AF_ECONET,econet),
+	__ADD(AF_ATMSVC,atmsvc),
 #ifdef AF_RDS
-	__ADD(AF_RDS,rds)
+	__ADD(AF_RDS,rds),
 #endif
-	__ADD(AF_SNA,sna)
-	__ADD(AF_IRDA,irda)
-	__ADD(AF_PPPOX,pppox)
-	__ADD(AF_WANPIPE,wanpipe)
-	__ADD(AF_LLC,llc)
+	__ADD(AF_SNA,sna),
+	__ADD(AF_IRDA,irda),
+	__ADD(AF_PPPOX,pppox),
+	__ADD(AF_WANPIPE,wanpipe),
+	__ADD(AF_LLC,llc),
 #ifdef AF_CAN
-	__ADD(AF_CAN,can)
+	__ADD(AF_CAN,can),
 #endif
 #ifdef AF_TIPC
-	__ADD(AF_TIPC,tipc)
+	__ADD(AF_TIPC,tipc),
 #endif
-	__ADD(AF_BLUETOOTH,bluetooth)
+	__ADD(AF_BLUETOOTH,bluetooth),
 #ifdef AF_IUCV
-	__ADD(AF_IUCV,iucv)
+	__ADD(AF_IUCV,iucv),
 #endif
 #ifdef AF_RXRPC
-	__ADD(AF_RXRPC,rxrpc)
+	__ADD(AF_RXRPC,rxrpc),
 #endif
 #ifdef AF_ISDN
-	__ADD(AF_ISDN,isdn)
+	__ADD(AF_ISDN,isdn),
 #endif
 #ifdef AF_PHONET
-	__ADD(AF_PHONET,phonet)
+	__ADD(AF_PHONET,phonet),
 #endif
 #ifdef AF_IEEE802154
-	__ADD(AF_IEEE802154,ieee802154)
+	__ADD(AF_IEEE802154,ieee802154),
 #endif
 #ifdef AF_CAIF
-	__ADD(AF_CAIF,caif)
+	__ADD(AF_CAIF,caif),
 #endif
 #ifdef AF_ALG
-	__ADD(AF_ALG,alg)
+	__ADD(AF_ALG,alg),
 #endif
 #ifdef AF_NFC
-	__ADD(AF_NFC,nfc)
+	__ADD(AF_NFC,nfc),
 #endif
+#ifdef AF_VSOCK
+	__ADD(AF_VSOCK,vsock),
+#endif
+	__ADD(AF_MPLS,mpls),
 };
 
 char *nl_af2str(int family, char *buf, size_t size)
diff --git a/lib/attr.c b/lib/attr.c
index d1f0268..a4f5852 100644
--- a/lib/attr.c
+++ b/lib/attr.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/attr.c		Netlink Attributes
  *
@@ -147,7 +148,7 @@
  */
 int nla_ok(const struct nlattr *nla, int remaining)
 {
-	return remaining >= sizeof(*nla) &&
+	return remaining >= (int) sizeof(*nla) &&
 	       nla->nla_len >= sizeof(*nla) &&
 	       nla->nla_len <= remaining;
 }
@@ -185,10 +186,10 @@
 	[NLA_FLAG]	= 0,
 };
 
-static int validate_nla(struct nlattr *nla, int maxtype,
-			struct nla_policy *policy)
+static int validate_nla(const struct nlattr *nla, int maxtype,
+			const struct nla_policy *policy)
 {
-	struct nla_policy *pt;
+	const struct nla_policy *pt;
 	unsigned int minlen = 0;
 	int type = nla_type(nla);
 
@@ -212,7 +213,7 @@
 		return -NLE_RANGE;
 
 	if (pt->type == NLA_STRING) {
-		char *data = nla_data(nla);
+		const char *data = nla_data(nla);
 		if (data[nla_len(nla) - 1] != '\0')
 			return -NLE_INVAL;
 	}
@@ -240,7 +241,7 @@
  * @return 0 on success or a negative error code.
  */
 int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
-	      struct nla_policy *policy)
+	      const struct nla_policy *policy)
 {
 	struct nlattr *nla;
 	int rem, err;
@@ -291,10 +292,10 @@
  *
  * @return 0 on success or a negative error code.
  */
-int nla_validate(struct nlattr *head, int len, int maxtype,
-		 struct nla_policy *policy)
+int nla_validate(const struct nlattr *head, int len, int maxtype,
+		 const struct nla_policy *policy)
 {
-	struct nlattr *nla;
+	const struct nlattr *nla;
 	int rem, err;
 
 	nla_for_each_attr(nla, head, len, rem) {
@@ -320,14 +321,14 @@
  *
  * @return Pointer to attribute found or NULL.
  */
-struct nlattr *nla_find(struct nlattr *head, int len, int attrtype)
+struct nlattr *nla_find(const struct nlattr *head, int len, int attrtype)
 {
-	struct nlattr *nla;
+	const struct nlattr *nla;
 	int rem;
 
 	nla_for_each_attr(nla, head, len, rem)
 		if (nla_type(nla) == attrtype)
-			return nla;
+			return (struct nlattr*)nla;
 
 	return NULL;
 }
@@ -350,7 +351,7 @@
  *
  * @return The number of bytes copied to dest.
  */
-int nla_memcpy(void *dest, struct nlattr *src, int count)
+int nla_memcpy(void *dest, const struct nlattr *src, int count)
 {
 	int minlen;
 
@@ -378,7 +379,7 @@
 size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize)
 {
 	size_t srclen = nla_len(nla);
-	char *src = nla_data(nla);
+	const char *src = nla_data(nla);
 
 	if (srclen > 0 && src[srclen - 1] == '\0')
 		srclen--;
@@ -457,7 +458,10 @@
 {
 	struct nlattr *nla;
 	int tlen;
-	
+
+	if (attrlen < 0)
+		return NULL;
+
 	tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen);
 
 	if (tlen > msg->nm_size)
@@ -474,7 +478,7 @@
 	NL_DBG(2, "msg %p: attr <%p> %d: Reserved %d (%d) bytes at offset +%td "
 		  "nlmsg_len=%d\n", msg, nla, nla->nla_type,
 		  nla_total_size(attrlen), attrlen,
-		  (void *) nla - nlmsg_data(msg->nm_nlh),
+		  (char *) nla - (char *) nlmsg_data(msg->nm_nlh),
 		  msg->nm_nlh->nlmsg_len);
 
 	return nla;
@@ -499,14 +503,18 @@
 	struct nlattr *nla;
 
 	nla = nla_reserve(msg, attrtype, datalen);
-	if (!nla)
+	if (!nla) {
+		if (datalen < 0)
+			return -NLE_INVAL;
+
 		return -NLE_NOMEM;
+	}
 
 	if (datalen > 0) {
 		memcpy(nla_data(nla), data, datalen);
 		NL_DBG(2, "msg %p: attr <%p> %d: Wrote %d bytes at offset +%td\n",
 		       msg, nla, nla->nla_type, datalen,
-		       (void *) nla - nlmsg_data(msg->nm_nlh));
+		       (char *) nla - (char *) nlmsg_data(msg->nm_nlh));
 	}
 
 	return 0;
@@ -524,7 +532,7 @@
  * @see nla_put
  * @return 0 on success or a negative error code.
  */
-int nla_put_data(struct nl_msg *msg, int attrtype, struct nl_data *data)
+int nla_put_data(struct nl_msg *msg, int attrtype, const struct nl_data *data)
 {
 	return nla_put(msg, attrtype, nl_data_get_size(data),
 		       nl_data_get(data));
@@ -552,6 +560,31 @@
  */
 
 /**
+ * Add 8 bit signed integer attribute to netlink message.
+ * @arg msg             Netlink message.
+ * @arg attrtype        Attribute type.
+ * @arg value           Numeric value to store as payload.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+int nla_put_s8(struct nl_msg *msg, int attrtype, int8_t value)
+{
+	return nla_put(msg, attrtype, sizeof(int8_t), &value);
+}
+
+/**
+ * Return value of 8 bit signed integer attribute.
+ * @arg nla             8 bit integer attribute
+ *
+ * @return Payload as 8 bit integer.
+ */
+int8_t nla_get_s8(const struct nlattr *nla)
+{
+	return *(const int8_t *) nla_data(nla);
+}
+
+/**
  * Add 8 bit integer attribute to netlink message.
  * @arg msg		Netlink message.
  * @arg attrtype	Attribute type.
@@ -571,9 +604,34 @@
  *
  * @return Payload as 8 bit integer.
  */
-uint8_t nla_get_u8(struct nlattr *nla)
+uint8_t nla_get_u8(const struct nlattr *nla)
 {
-	return *(uint8_t *) nla_data(nla);
+	return *(const uint8_t *) nla_data(nla);
+}
+
+/**
+ * Add 16 bit signed integer attribute to netlink message.
+ * @arg msg             Netlink message.
+ * @arg attrtype        Attribute type.
+ * @arg value           Numeric value to store as payload.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+int nla_put_s16(struct nl_msg *msg, int attrtype, int16_t value)
+{
+	return nla_put(msg, attrtype, sizeof(int16_t), &value);
+}
+
+/**
+ * Return payload of 16 bit signed integer attribute.
+ * @arg nla             16 bit integer attribute
+ *
+ * @return Payload as 16 bit integer.
+ */
+int16_t nla_get_s16(const struct nlattr *nla)
+{
+	return *(const int16_t *) nla_data(nla);
 }
 
 /**
@@ -596,9 +654,34 @@
  *
  * @return Payload as 16 bit integer.
  */
-uint16_t nla_get_u16(struct nlattr *nla)
+uint16_t nla_get_u16(const struct nlattr *nla)
 {
-	return *(uint16_t *) nla_data(nla);
+	return *(const uint16_t *) nla_data(nla);
+}
+
+/**
+ * Add 32 bit signed integer attribute to netlink message.
+ * @arg msg             Netlink message.
+ * @arg attrtype        Attribute type.
+ * @arg value           Numeric value to store as payload.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+int nla_put_s32(struct nl_msg *msg, int attrtype, int32_t value)
+{
+	return nla_put(msg, attrtype, sizeof(int32_t), &value);
+}
+
+/**
+ * Return payload of 32 bit signed integer attribute.
+ * @arg nla             32 bit integer attribute.
+ *
+ * @return Payload as 32 bit integer.
+ */
+int32_t nla_get_s32(const struct nlattr *nla)
+{
+	return *(const int32_t *) nla_data(nla);
 }
 
 /**
@@ -621,9 +704,39 @@
  *
  * @return Payload as 32 bit integer.
  */
-uint32_t nla_get_u32(struct nlattr *nla)
+uint32_t nla_get_u32(const struct nlattr *nla)
 {
-	return *(uint32_t *) nla_data(nla);
+	return *(const uint32_t *) nla_data(nla);
+}
+
+/**
+ * Add 64 bit signed integer attribute to netlink message.
+ * @arg msg             Netlink message.
+ * @arg attrtype        Attribute type.
+ * @arg value           Numeric value to store as payload.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+int nla_put_s64(struct nl_msg *msg, int attrtype, int64_t value)
+{
+	return nla_put(msg, attrtype, sizeof(int64_t), &value);
+}
+
+/**
+ * Return payload of s64 attribute
+ * @arg nla             s64 netlink attribute
+ *
+ * @return Payload as 64 bit integer.
+ */
+int64_t nla_get_s64(const struct nlattr *nla)
+{
+	int64_t tmp = 0;
+
+	if (nla && nla_len(nla) >= sizeof(tmp))
+		memcpy(&tmp, nla_data(nla), sizeof(tmp));
+
+	return tmp;
 }
 
 /**
@@ -646,7 +759,7 @@
  *
  * @return Payload as 64 bit integer.
  */
-uint64_t nla_get_u64(struct nlattr *nla)
+uint64_t nla_get_u64(const struct nlattr *nla)
 {
 	uint64_t tmp = 0;
 
@@ -682,12 +795,12 @@
  *
  * @return Pointer to attribute payload.
  */
-char *nla_get_string(struct nlattr *nla)
+char *nla_get_string(const struct nlattr *nla)
 {
 	return (char *) nla_data(nla);
 }
 
-char *nla_strdup(struct nlattr *nla)
+char *nla_strdup(const struct nlattr *nla)
 {
 	return strdup(nla_get_string(nla));
 }
@@ -717,7 +830,7 @@
  *
  * @return True if flag is set, otherwise false.
  */
-int nla_get_flag(struct nlattr *nla)
+int nla_get_flag(const struct nlattr *nla)
 {
 	return !!nla;
 }
@@ -745,7 +858,7 @@
  *
  * @return the number of milliseconds.
  */
-unsigned long nla_get_msecs(struct nlattr *nla)
+unsigned long nla_get_msecs(const struct nlattr *nla)
 {
 	return nla_get_u64(nla);
 }
@@ -769,7 +882,8 @@
  * @see nla_put
  * @return 0 on success or a negative error code.
  */
-int nla_put_nested(struct nl_msg *msg, int attrtype, struct nl_msg *nested)
+int nla_put_nested(struct nl_msg *msg, int attrtype,
+		   const struct nl_msg *nested)
 {
 	NL_DBG(2, "msg %p: attr <> %d: adding msg %p as nested attribute\n",
 		msg, attrtype, nested);
@@ -790,7 +904,7 @@
 {
 	struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh);
 
-	if (nla_put(msg, attrtype, 0, NULL) < 0)
+	if (nla_put(msg, NLA_F_NESTED | attrtype, 0, NULL) < 0)
 		return NULL;
 
 	NL_DBG(2, "msg %p: attr <%p> %d: starting nesting\n",
@@ -799,29 +913,22 @@
 	return start;
 }
 
-/**
- * Finalize nesting of attributes.
- * @arg msg		Netlink message.
- * @arg start		Container attribute as returned from nla_nest_start().
- *
- * Corrects the container attribute header to include the appeneded attributes.
- *
- * @return 0
- */
-int nla_nest_end(struct nl_msg *msg, struct nlattr *start)
+static int _nest_end(struct nl_msg *msg, struct nlattr *start, int keep_empty)
 {
 	size_t pad, len;
 
-	len = (void *) nlmsg_tail(msg->nm_nlh) - (void *) start;
+	len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) start;
 
-	if (len == NLA_HDRLEN) {
+	if (   len > USHRT_MAX
+	    || (!keep_empty && len == NLA_HDRLEN)) {
 		/*
-		 * Kernel can't handle empty nested attributes, trim the
+		 * Max nlattr size exceeded or empty nested attribute, trim the
 		 * attribute header again
 		 */
 		nla_nest_cancel(msg, start);
 
-		return 0;
+		/* Return error only if nlattr size was exceeded */
+		return (len == NLA_HDRLEN) ? 0 : -NLE_ATTRSIZE;
 	}
 
 	start->nla_len = len;
@@ -848,6 +955,35 @@
 }
 
 /**
+ * Finalize nesting of attributes.
+ * @arg msg		Netlink message.
+ * @arg start		Container attribute as returned from nla_nest_start().
+ *
+ * Corrects the container attribute header to include the appeneded attributes.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nla_nest_end(struct nl_msg *msg, struct nlattr *start)
+{
+	return _nest_end (msg, start, 0);
+}
+
+/**
+ * Finalize nesting of attributes without stripping off empty attributes.
+ * @arg msg		Netlink message.
+ * @arg start		Container attribute as returned from nla_nest_start().
+ *
+ * Corrects the container attribute header to include the appeneded attributes.
+ * Keep empty attribute if NO actual attribute payload exists.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nla_nest_end_keep_empty(struct nl_msg *msg, struct nlattr *start)
+{
+	return _nest_end (msg, start, 1);
+}
+
+/**
  * Cancel the addition of a nested attribute
  * @arg msg		Netlink message
  * @arg attr		Nested netlink attribute
@@ -856,11 +992,11 @@
  * by resetting the message to the size before the call to nla_nest_start()
  * and by overwriting any potentially touched message segments with 0.
  */
-void nla_nest_cancel(struct nl_msg *msg, struct nlattr *attr)
+void nla_nest_cancel(struct nl_msg *msg, const struct nlattr *attr)
 {
 	ssize_t len;
 
-	len = (void *) nlmsg_tail(msg->nm_nlh) - (void *) attr;
+	len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) attr;
 	if (len < 0)
 		BUG();
 	else if (len > 0) {
@@ -883,7 +1019,7 @@
  * @return 0 on success or a negative error code.
  */
 int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla,
-		     struct nla_policy *policy)
+		     const struct nla_policy *policy)
 {
 	return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy);
 }
@@ -894,7 +1030,7 @@
  *
  * @return True if attribute has NLA_F_NESTED flag set, oterhwise False.
  */
-int nla_is_nested(struct nlattr *attr)
+int nla_is_nested(const struct nlattr *attr)
 {
 	return !!(attr->nla_type & NLA_F_NESTED);
 }
diff --git a/lib/cache.c b/lib/cache.c
index b4f9649..4bb10d1 100644
--- a/lib/cache.c
+++ b/lib/cache.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/cache.c		Caching Module
  *
@@ -235,7 +236,7 @@
 {
 	struct nl_cache *cache;
 	int err;
-	
+
 	if (!(cache = nl_cache_alloc(ops)))
 		return -NLE_NOMEM;
 
@@ -421,7 +422,7 @@
 
 void nl_cache_put(struct nl_cache *cache)
 {
-	return nl_cache_free(cache);
+	nl_cache_free(cache);
 }
 
 /** @} */
@@ -527,7 +528,7 @@
 
 	NL_DBG(3, "Moving object %p from cache %p to cache %p\n",
 	       obj, obj->ce_cache, cache);
-	
+
 	/* Acquire reference, if already in a cache this will be
 	 * reverted during removal */
 	nl_object_get(obj);
@@ -589,7 +590,7 @@
  */
 void nl_cache_set_arg1(struct nl_cache *cache, int arg)
 {
-        cache->c_iarg1 = arg;
+	cache->c_iarg1 = arg;
 }
 
 /**
@@ -602,7 +603,7 @@
  */
 void nl_cache_set_arg2(struct nl_cache *cache, int arg)
 {
-        cache->c_iarg2 = arg;
+	cache->c_iarg2 = arg;
 }
 
 /**
@@ -637,7 +638,9 @@
  *
  * @see nl_cache_pickup(), nl_cache_resync()
  *
- * @return 0 on success or a negative error code.
+ * @return 0 on success or a negative error code. Some implementations
+ * of co_request_update() return a positive number on success that is
+ * the number of bytes sent. Treat any non-negative number as success too.
  */
 static int nl_cache_request_full_dump(struct nl_sock *sk,
 				      struct nl_cache *cache)
@@ -708,7 +711,7 @@
 	return err;
 }
 
-static int pickup_cb(struct nl_object *c, struct nl_parser_param *p)
+static int pickup_checkdup_cb(struct nl_object *c, struct nl_parser_param *p)
 {
 	struct nl_cache *cache = (struct nl_cache *)p->pp_arg;
 	struct nl_object *old;
@@ -727,6 +730,42 @@
 	return nl_cache_add(cache, c);
 }
 
+static int pickup_cb(struct nl_object *c, struct nl_parser_param *p)
+{
+	struct nl_cache *cache = p->pp_arg;
+
+	return nl_cache_add(cache, c);
+}
+
+static int __nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache,
+			     int checkdup)
+{
+	struct nl_parser_param p;
+
+	p.pp_cb = checkdup ? pickup_checkdup_cb : pickup_cb;
+	p.pp_arg = cache;
+
+	if (sk->s_proto != cache->c_ops->co_protocol)
+		return -NLE_PROTO_MISMATCH;
+
+	return __cache_pickup(sk, cache, &p);
+}
+
+/**
+ * Pickup a netlink dump response and put it into a cache.
+ * @arg sk		Netlink socket.
+ * @arg cache		Cache to put items into.
+ *
+ * Waits for netlink messages to arrive, parses them and puts them into
+ * the specified cache.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nl_cache_pickup_checkdup(struct nl_sock *sk, struct nl_cache *cache)
+{
+	return __nl_cache_pickup(sk, cache, 1);
+}
+
 /**
  * Pickup a netlink dump response and put it into a cache.
  * @arg sk		Netlink socket.
@@ -742,42 +781,49 @@
  */
 int nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache)
 {
-	struct nl_parser_param p = {
-		.pp_cb = pickup_cb,
-		.pp_arg = cache,
-	};
-
-	if (sk->s_proto != cache->c_ops->co_protocol)
-		return -NLE_PROTO_MISMATCH;
-
-	return __cache_pickup(sk, cache, &p);
+	return __nl_cache_pickup(sk, cache, 0);
 }
 
 static int cache_include(struct nl_cache *cache, struct nl_object *obj,
-			 struct nl_msgtype *type, change_func_t cb, void *data)
+			 struct nl_msgtype *type, change_func_t cb,
+			 change_func_v2_t cb_v2, void *data)
 {
 	struct nl_object *old;
+	struct nl_object *clone = NULL;
+	uint64_t diff = 0;
 
 	switch (type->mt_act) {
 	case NL_ACT_NEW:
 	case NL_ACT_DEL:
 		old = nl_cache_search(cache, obj);
 		if (old) {
+			if (cb_v2 && old->ce_ops->oo_update) {
+				clone = nl_object_clone(old);
+				diff = nl_object_diff64(old, obj);
+			}
 			/*
 			 * Some objects types might support merging the new
 			 * object with the old existing cache object.
 			 * Handle them first.
 			 */
 			if (nl_object_update(old, obj) == 0) {
-				if (cb)
+				if (cb_v2) {
+					cb_v2(cache, clone, obj, diff,
+					      NL_ACT_CHANGE, data);
+					nl_object_put(clone);
+				} else if (cb)
 					cb(cache, old, NL_ACT_CHANGE, data);
 				nl_object_put(old);
 				return 0;
 			}
+			nl_object_put(clone);
 
 			nl_cache_remove(old);
 			if (type->mt_act == NL_ACT_DEL) {
-				if (cb)
+				if (cb_v2)
+					cb_v2(cache, old, NULL, 0, NL_ACT_DEL,
+					      data);
+				else if (cb)
 					cb(cache, old, NL_ACT_DEL, data);
 				nl_object_put(old);
 			}
@@ -785,10 +831,20 @@
 
 		if (type->mt_act == NL_ACT_NEW) {
 			nl_cache_move(cache, obj);
-			if (old == NULL && cb)
-				cb(cache, obj, NL_ACT_NEW, data);
-			else if (old) {
-				if (nl_object_diff(old, obj) && cb)
+			if (old == NULL) {
+				if (cb_v2) {
+					cb_v2(cache, NULL, obj, 0, NL_ACT_NEW,
+					      data);
+				} else if (cb)
+					cb(cache, obj, NL_ACT_NEW, data);
+			} else if (old) {
+				diff = 0;
+				if (cb || cb_v2)
+					diff = nl_object_diff64(old, obj);
+				if (diff && cb_v2) {
+					cb_v2(cache, old, obj, diff, NL_ACT_CHANGE,
+					      data);
+				} else if (diff && cb)
 					cb(cache, obj, NL_ACT_CHANGE, data);
 
 				nl_object_put(old);
@@ -815,7 +871,27 @@
 	for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
 		if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype)
 			return cache_include(cache, obj, &ops->co_msgtypes[i],
-					     change_cb, data);
+					     change_cb, NULL, data);
+
+	NL_DBG(3, "Object %p does not seem to belong to cache %p <%s>\n",
+	       obj, cache, nl_cache_name(cache));
+
+	return -NLE_MSGTYPE_NOSUPPORT;
+}
+
+int nl_cache_include_v2(struct nl_cache *cache, struct nl_object *obj,
+			change_func_v2_t change_cb, void *data)
+{
+	struct nl_cache_ops *ops = cache->c_ops;
+	int i;
+
+	if (ops->co_obj_ops != obj->ce_ops)
+		return -NLE_OBJ_MISMATCH;
+
+	for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
+		if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype)
+			return cache_include(cache, obj, &ops->co_msgtypes[i],
+						NULL, change_cb, data);
 
 	NL_DBG(3, "Object %p does not seem to belong to cache %p <%s>\n",
 	       obj, cache, nl_cache_name(cache));
@@ -827,7 +903,12 @@
 {
 	struct nl_cache_assoc *ca = p->pp_arg;
 
-	return nl_cache_include(ca->ca_cache, c, ca->ca_change, ca->ca_change_data);
+	if (ca->ca_change_v2)
+		return nl_cache_include_v2(ca->ca_cache, c, ca->ca_change_v2,
+					   ca->ca_change_data);
+	else
+		return nl_cache_include(ca->ca_cache, c, ca->ca_change,
+					ca->ca_change_data);
 }
 
 int nl_cache_resync(struct nl_sock *sk, struct nl_cache *cache,
@@ -1008,8 +1089,8 @@
 
 	obj = nl_hash_table_lookup(cache->hashtable, needle);
 	if (obj) {
-	    nl_object_get(obj);
-	    return obj;
+		nl_object_get(obj);
+		return obj;
 	}
 
 	return NULL;
diff --git a/lib/cache_mngr.c b/lib/cache_mngr.c
index 9b25e9b..380834f 100644
--- a/lib/cache_mngr.c
+++ b/lib/cache_mngr.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/cache_mngr.c	Cache Manager
  *
@@ -33,6 +34,7 @@
  */
 
 #include <netlink-private/netlink.h>
+#include <netlink-private/utils.h>
 #include <netlink/netlink.h>
 #include <netlink/cache.h>
 #include <netlink/utils.h>
@@ -59,9 +61,15 @@
 
 	if (ops->co_include_event)
 		return ops->co_include_event(ca->ca_cache, obj, ca->ca_change,
+					     ca->ca_change_v2,
 					     ca->ca_change_data);
-	else
-		return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data);
+	else {
+		if (ca->ca_change_v2)
+			return nl_cache_include_v2(ca->ca_cache, obj, ca->ca_change_v2, ca->ca_change_data);
+		else
+			return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data);
+	}
+
 }
 
 static int event_input(struct nl_msg *msg, void *arg)
@@ -194,6 +202,70 @@
 }
 
 /**
+ * Set change_func_v2 for cache manager
+ * @arg mngr		Cache manager.
+ * @arg cache		Cache associated with the callback
+ * @arg cb		Function to be called upon changes.
+ * @arg data		Argument passed on to change callback
+ *
+ * Adds callback change_func_v2 to a registered cache. This callback provides
+ * in like the standard change_func the added or remove netlink object. In case
+ * of a change the old and the new object is provided as well as the according
+ * diff. If this callback is registered this has a higher priority then the
+ * change_func registered during cache registration. Hence only one callback is
+ * executed.
+ *
+ * The first netlink object in the callback is refering to the old object and
+ * the second to the new. This means on NL_ACT_CHANGE the first is the previous
+ * object in the cache and the second the updated version. On NL_ACT_DEL the
+ * first is the deleted object the second is NULL. On NL_ACT_NEW the first is
+ * NULL and the second the new netlink object.
+ *
+ * The user is responsible for calling nl_cache_mngr_poll() or monitor
+ * the socket and call nl_cache_mngr_data_ready() to allow the library
+ * to process netlink notification events.
+ *
+ * @see nl_cache_mngr_poll()
+ * @see nl_cache_mngr_data_ready()
+ *
+ * @return 0 on success or a negative error code.
+ * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and
+ * 			       cache type
+ * @return -NLE_OPNOTSUPP Cache type does not support updates
+ * @return -NLE_RANGE Cache of this type is not registered
+ */
+static int nl_cache_mngr_set_change_func_v2(struct nl_cache_mngr *mngr,
+					    struct nl_cache *cache,
+					    change_func_v2_t cb, void *data)
+{
+	struct nl_cache_ops *ops;
+	int i;
+
+	ops = cache->c_ops;
+	if (!ops)
+		return -NLE_INVAL;
+
+	if (ops->co_protocol != mngr->cm_protocol)
+		return -NLE_PROTO_MISMATCH;
+
+	if (ops->co_groups == NULL)
+		return -NLE_OPNOTSUPP;
+
+	for (i = 0; i < mngr->cm_nassocs; i++)
+		if (mngr->cm_assocs[i].ca_cache == cache)
+			break;
+
+	if (i >= mngr->cm_nassocs) {
+		return -NLE_RANGE;
+	}
+
+	mngr->cm_assocs[i].ca_change_v2 = cb;
+	mngr->cm_assocs[i].ca_change_data = data;
+
+	return 0;
+}
+
+/**
  * Add cache to cache manager
  * @arg mngr		Cache manager.
  * @arg cache		Cache to be added to cache manager
@@ -240,25 +312,26 @@
 		    mngr->cm_assocs[i].ca_cache->c_ops == ops)
 			return -NLE_EXIST;
 
-retry:
 	for (i = 0; i < mngr->cm_nassocs; i++)
 		if (!mngr->cm_assocs[i].ca_cache)
 			break;
 
 	if (i >= mngr->cm_nassocs) {
-		mngr->cm_nassocs += NASSOC_EXPAND;
-		mngr->cm_assocs = realloc(mngr->cm_assocs,
-					  mngr->cm_nassocs *
-					  sizeof(struct nl_cache_assoc));
-		if (mngr->cm_assocs == NULL)
+		struct nl_cache_assoc *cm_assocs;
+		int cm_nassocs = mngr->cm_nassocs + NASSOC_EXPAND;
+
+		cm_assocs = realloc(mngr->cm_assocs,
+				    cm_nassocs * sizeof(struct nl_cache_assoc));
+		if (cm_assocs == NULL)
 			return -NLE_NOMEM;
 
-		memset(mngr->cm_assocs + (mngr->cm_nassocs - NASSOC_EXPAND), 0,
+		memset(cm_assocs + mngr->cm_nassocs, 0,
 		       NASSOC_EXPAND * sizeof(struct nl_cache_assoc));
+		mngr->cm_assocs = cm_assocs;
+		mngr->cm_nassocs = cm_nassocs;
 
 		NL_DBG(1, "Increased capacity of cache manager %p " \
 			  "to %d\n", mngr, mngr->cm_nassocs);
-		goto retry;
 	}
 
 	for (grp = ops->co_groups; grp->ag_group; grp++) {
@@ -293,6 +366,41 @@
 /**
  * Add cache to cache manager
  * @arg mngr		Cache manager.
+ * @arg cache		Cache to be added to cache manager
+ * @arg cb		V2 function to be called upon changes.
+ * @arg data		Argument passed on to change callback
+ *
+ * Adds cache to the manager. The operation will trigger a full
+ * dump request from the kernel to initially fill the contents
+ * of the cache. The manager will subscribe to the notification group
+ * of the cache and keep track of any further changes.
+ *
+ * The user is responsible for calling nl_cache_mngr_poll() or monitor
+ * the socket and call nl_cache_mngr_data_ready() to allow the library
+ * to process netlink notification events.
+ *
+ * @see nl_cache_mngr_poll()
+ * @see nl_cache_mngr_data_ready()
+ *
+ * @return 0 on success or a negative error code.
+ * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and
+ * 			       cache type
+ * @return -NLE_OPNOTSUPP Cache type does not support updates
+ * @return -NLE_EXIST Cache of this type already being managed
+ */
+int nl_cache_mngr_add_cache_v2(struct nl_cache_mngr *mngr, struct nl_cache *cache,
+		      change_func_v2_t cb, void *data) {
+	int err;
+	err = nl_cache_mngr_add_cache(mngr, cache, NULL, NULL);
+	if (err < 0)
+		return err;
+
+	return nl_cache_mngr_set_change_func_v2(mngr, cache, cb, data);
+}
+
+/**
+ * Add cache to cache manager
+ * @arg mngr		Cache manager.
  * @arg name		Name of cache to keep track of
  * @arg cb		Function to be called upon changes.
  * @arg data		Argument passed on to change callback
@@ -308,6 +416,9 @@
  * the socket and call nl_cache_mngr_data_ready() to allow the library
  * to process netlink notification events.
  *
+ * @note Versions up to 3.4.0 actually required the result argument, preventing
+ * 	 NULL to be passed.
+ *
  * @see nl_cache_mngr_poll()
  * @see nl_cache_mngr_data_ready()
  *
@@ -338,7 +449,8 @@
 	if (err < 0)
 		goto errout_free_cache;
 
-	*result = cache;
+	if (result)
+		*result = cache;
 	return 0;
 
 errout_free_cache:
@@ -391,8 +503,11 @@
 	NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd);
 	ret = poll(&fds, 1, timeout);
 	NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret);
-	if (ret < 0)
+	if (ret < 0) {
+		NL_DBG(4, "nl_cache_mngr_poll(%p): poll() failed with %d (%s)\n",
+			mngr, errno, nl_strerror_l(errno));
 		return -nl_syserr2nlerr(errno);
+	}
 
 	/* No events, return */
 	if (ret == 0)
diff --git a/lib/cache_mngt.c b/lib/cache_mngt.c
index 4d3d6ff..4178e43 100644
--- a/lib/cache_mngt.c
+++ b/lib/cache_mngt.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/cache_mngt.c	Cache Management
  *
@@ -38,7 +39,7 @@
  * @{
  */
 
-struct nl_cache_ops *__nl_cache_ops_lookup(const char *name)
+static struct nl_cache_ops *__nl_cache_ops_lookup(const char *name)
 {
 	struct nl_cache_ops *ops;
 
@@ -254,6 +255,9 @@
 	if (!ops->co_name || !ops->co_obj_ops)
 		return -NLE_INVAL;
 
+	/* oo_keygen() also needs oo_compare() */
+	BUG_ON (ops->co_obj_ops->oo_keygen && !ops->co_obj_ops->oo_compare);
+
 	nl_write_lock(&cache_ops_lock);
 	if (__nl_cache_ops_lookup(ops->co_name)) {
 		nl_write_unlock(&cache_ops_lock);
diff --git a/lib/cli/cls/basic.c b/lib/cli/cls/basic.c
index 1939988..1a6b188 100644
--- a/lib/cli/cls/basic.c
+++ b/lib/cli/cls/basic.c
@@ -49,7 +49,7 @@
 			{ "ematch", 1, 0, 'e' },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "ht:e:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -72,7 +72,7 @@
 			rtnl_basic_set_ematch(cls, tree);
 			break;
 		}
- 	}
+	}
 }
 
 static struct nl_cli_tc_module basic_module =
diff --git a/lib/cli/cls/cgroup.c b/lib/cli/cls/cgroup.c
index fae6208..9e1443c 100644
--- a/lib/cli/cls/cgroup.c
+++ b/lib/cli/cls/cgroup.c
@@ -39,7 +39,7 @@
 			{ "ematch", 1, 0, 'e' },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "he:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -54,7 +54,7 @@
 			rtnl_cgroup_set_ematch(cls, tree);
 			break;
 		}
- 	}
+	}
 }
 
 static struct nl_cli_tc_module cgroup_module =
diff --git a/lib/cli/qdisc/bfifo.c b/lib/cli/qdisc/bfifo.c
index 1ee4777..6b0206f 100644
--- a/lib/cli/qdisc/bfifo.c
+++ b/lib/cli/qdisc/bfifo.c
@@ -42,7 +42,7 @@
 			{ "limit", 1, 0, ARG_LIMIT },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "h", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -62,7 +62,7 @@
 			rtnl_qdisc_fifo_set_limit(qdisc, limit);
 			break;
 		}
- 	}
+	}
 }
 
 static struct nl_cli_tc_module bfifo_module =
diff --git a/lib/cli/qdisc/blackhole.c b/lib/cli/qdisc/blackhole.c
index af9dc6d..372855f 100644
--- a/lib/cli/qdisc/blackhole.c
+++ b/lib/cli/qdisc/blackhole.c
@@ -33,7 +33,7 @@
 			{ "help", 0, 0, 'h' },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "h", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -43,7 +43,7 @@
 			print_usage();
 			return;
 		}
- 	}
+	}
 }
 
 static struct nl_cli_tc_module blackhole_module =
diff --git a/lib/cli/qdisc/fq_codel.c b/lib/cli/qdisc/fq_codel.c
index 1602bcb..a592f90 100644
--- a/lib/cli/qdisc/fq_codel.c
+++ b/lib/cli/qdisc/fq_codel.c
@@ -55,7 +55,7 @@
 			{ "target", 1, 0, ARG_TARGET},
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "h", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -91,7 +91,7 @@
 			break;
 
 		}
- 	}
+	}
 }
 
 static struct nl_cli_tc_module fq_codel_module =
diff --git a/lib/cli/qdisc/hfsc.c b/lib/cli/qdisc/hfsc.c
new file mode 100644
index 0000000..619befc
--- /dev/null
+++ b/lib/cli/qdisc/hfsc.c
@@ -0,0 +1,252 @@
+/*
+ * lib/cli/qdisc/hfsc.c     	HFSC module for CLI lib
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2014 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/route/qdisc/hfsc.h>
+#include <linux/pkt_sched.h>
+
+static void print_qdisc_usage(void)
+{
+	printf(
+"Usage: nl-qdisc-add [...] hfsc [OPTIONS]...\n"
+"\n"
+"OPTIONS\n"
+"     --help                Show this help text.\n"
+"     --default=ID          Default class for unclassified traffic.\n"
+"\n"
+"EXAMPLE"
+"    # Create hfsc root qdisc 1: and direct unclassified traffic to class 1:10\n"
+"    nl-qdisc-add --dev=eth1 --parent=root --handle=1: hfsc --default=10\n");
+}
+
+static void hfsc_parse_qdisc_argv(struct rtnl_tc *tc, int argc, char **argv)
+{
+	struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_DEFAULT = 257,
+		};
+		static struct option long_opts[] = {
+			{ "help", 0, 0, 'h' },
+			{ "default", 1, 0, ARG_DEFAULT },
+			{ 0, 0, 0, 0 }
+		};
+
+		c = getopt_long(argc, argv, "hv", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_qdisc_usage();
+			return;
+
+		case ARG_DEFAULT:
+			rtnl_qdisc_hfsc_set_defcls(qdisc, nl_cli_parse_u32(optarg));
+			break;
+		}
+	}
+}
+
+static void print_class_usage(void)
+{
+	printf(
+"Usage: nl-class-add [...] hfsc [OPTIONS]...\n"
+"\n"
+"OPTIONS\n"
+"     --help                Show this help text.\n"
+"     --ls=SC               Link-sharing service curve\n"
+"     --rt=SC               Real-time service curve\n"
+"     --sc=SC               Specifiy both of the above\n"
+"     --ul=SC               Upper limit\n"
+"     where SC := [ [ m1 bits ] d usec ] m2 bits\n"
+"\n"
+"EXAMPLE"
+"    # Attach class 1:1 to hfsc qdisc 1: and use rt and ls curve\n"
+"    nl-class-add --dev=eth1 --parent=1: --classid=1:1 hfsc --sc=m1:250,d:8,m2:100\n");
+}
+
+static int
+hfsc_get_sc(char *optarg, struct tc_service_curve *sc)
+{
+	unsigned int m1 = 0, d = 0, m2 = 0;
+	char *tmp = strdup(optarg);
+	char *p, *endptr;
+	char *pp = tmp;
+
+	if (!tmp)
+		return -ENOMEM;
+
+	p = strstr(pp, "m1:");
+	if (p) {
+		char *q;
+		p += 3;
+		if (*p == 0)
+			goto err;
+		q = strchr(p, ',');
+		if (!q)
+			goto err;
+		*q = 0;
+		m1 = strtoul(p, &endptr, 10);
+		if (endptr == p)
+			goto err;
+		pp = q + 1;
+	}
+
+	p = strstr(pp, "d:");
+	if (p) {
+		char *q;
+		p += 2;
+		if (*p == 0)
+			goto err;
+		q = strchr(p, ',');
+		if (!q)
+			goto err;
+		*q = 0;
+		d = strtoul(p, &endptr, 10);
+		if (endptr == p)
+			goto err;
+		pp = q + 1;
+	}
+
+	p = strstr(pp, "m2:");
+	if (p) {
+		p += 3;
+		if (*p == 0)
+			goto err;
+		m2 = strtoul(p, &endptr, 10);
+		if (endptr == p)
+			goto err;
+	} else
+		goto err;
+
+	free(tmp);
+	sc->m1 = m1;
+	sc->d  = d;
+	sc->m2 = m2;
+	return 0;
+
+err:
+	free(tmp);
+	return -EINVAL;
+}
+
+static void hfsc_parse_class_argv(struct rtnl_tc *tc, int argc, char **argv)
+{
+	struct rtnl_class *class = (struct rtnl_class *) tc;
+	int arg_ok = 0, ret = -EINVAL;
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_RT = 257,
+			ARG_LS = 258,
+			ARG_SC,
+			ARG_UL,
+		};
+		static struct option long_opts[] = {
+			{ "help", 0, 0, 'h' },
+			{ "rt", 1, 0, ARG_RT },
+			{ "ls", 1, 0, ARG_LS },
+			{ "sc", 1, 0, ARG_SC },
+			{ "ul", 1, 0, ARG_UL },
+			{ 0, 0, 0, 0 }
+		};
+		struct tc_service_curve tsc;
+
+		c = getopt_long(argc, argv, "h", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_class_usage();
+			return;
+
+		case ARG_RT:
+			ret = hfsc_get_sc(optarg, &tsc);
+			if (ret < 0) {
+				nl_cli_fatal(ret, "Unable to parse sc "
+					"\"%s\": Invalid format.", optarg);
+			}
+
+			rtnl_class_hfsc_set_rsc(class, &tsc);
+			arg_ok++;
+			break;
+
+		case ARG_LS:
+			ret = hfsc_get_sc(optarg, &tsc);
+			if (ret < 0) {
+				nl_cli_fatal(ret, "Unable to parse sc "
+					"\"%s\": Invalid format.", optarg);
+			}
+
+			rtnl_class_hfsc_set_fsc(class, &tsc);
+			arg_ok++;
+			break;
+
+		case ARG_SC:
+			ret = hfsc_get_sc(optarg, &tsc);
+			if (ret < 0) {
+				nl_cli_fatal(ret, "Unable to parse sc "
+					"\"%s\": Invalid format.", optarg);
+			}
+
+			rtnl_class_hfsc_set_rsc(class, &tsc);
+			rtnl_class_hfsc_set_fsc(class, &tsc);
+			arg_ok++;
+			break;
+
+		case ARG_UL:
+			ret = hfsc_get_sc(optarg, &tsc);
+			if (ret < 0) {
+				nl_cli_fatal(ret, "Unable to parse sc "
+					"\"%s\": Invalid format.", optarg);
+			}
+
+			rtnl_class_hfsc_set_usc(class, &tsc);
+			arg_ok++;
+			break;
+		}
+	}
+
+	if (!arg_ok)
+		nl_cli_fatal(ret, "Invalid arguments");
+}
+
+static struct nl_cli_tc_module hfsc_qdisc_module =
+{
+	.tm_name		= "hfsc",
+	.tm_type		= RTNL_TC_TYPE_QDISC,
+	.tm_parse_argv		= hfsc_parse_qdisc_argv,
+};
+
+static struct nl_cli_tc_module hfsc_class_module =
+{
+	.tm_name		= "hfsc",
+	.tm_type		= RTNL_TC_TYPE_CLASS,
+	.tm_parse_argv		= hfsc_parse_class_argv,
+};
+
+static void __init hfsc_init(void)
+{
+	nl_cli_tc_register(&hfsc_qdisc_module);
+	nl_cli_tc_register(&hfsc_class_module);
+}
+
+static void __exit hfsc_exit(void)
+{
+	nl_cli_tc_unregister(&hfsc_class_module);
+	nl_cli_tc_unregister(&hfsc_qdisc_module);
+}
diff --git a/lib/cli/qdisc/htb.c b/lib/cli/qdisc/htb.c
index 1751595..628e6cc 100644
--- a/lib/cli/qdisc/htb.c
+++ b/lib/cli/qdisc/htb.c
@@ -44,7 +44,7 @@
 			{ "default", 1, 0, ARG_DEFAULT },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "hv", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -62,7 +62,7 @@
 			rtnl_htb_set_defcls(qdisc, nl_cli_parse_u32(optarg));
 			break;
 		}
- 	}
+	}
 }
 
 static void print_class_usage(void)
@@ -109,7 +109,7 @@
 			{ "cburst", 1, 0, ARG_CBURST },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "h", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -173,7 +173,7 @@
 			rtnl_htb_set_cbuffer(class, rate);
 			break;
 		}
- 	}
+	}
 }
 
 static struct nl_cli_tc_module htb_qdisc_module =
diff --git a/lib/cli/qdisc/pfifo.c b/lib/cli/qdisc/pfifo.c
index 02c4d22..7aac7df 100644
--- a/lib/cli/qdisc/pfifo.c
+++ b/lib/cli/qdisc/pfifo.c
@@ -42,7 +42,7 @@
 			{ "limit", 1, 0, ARG_LIMIT },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "h", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -56,7 +56,7 @@
 			rtnl_qdisc_fifo_set_limit(qdisc, nl_cli_parse_u32(optarg));
 			break;
 		}
- 	}
+	}
 }
 
 static struct nl_cli_tc_module pfifo_module =
diff --git a/lib/cli/qdisc/plug.c b/lib/cli/qdisc/plug.c
index 2b8d5d6..227082d 100644
--- a/lib/cli/qdisc/plug.c
+++ b/lib/cli/qdisc/plug.c
@@ -66,7 +66,7 @@
 			{ "release-indefinite", 0, 0, ARG_RELEASE_INDEFINITE },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "h", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -85,14 +85,14 @@
 			break;
 
 		case ARG_RELEASE_ONE:
-		        rtnl_qdisc_plug_release_one(qdisc);
+			rtnl_qdisc_plug_release_one(qdisc);
 			break;
 
 		case ARG_RELEASE_INDEFINITE:
 			rtnl_qdisc_plug_release_indefinite(qdisc);
 			break;
 		}
- 	}
+	}
 }
 
 static struct nl_cli_tc_module plug_module =
diff --git a/lib/data.c b/lib/data.c
index 1a3a3fb..fea3060 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/data.c		Abstract Data
  *
@@ -47,7 +48,7 @@
  * 
  * @return Newly allocated data handle or NULL
  */
-struct nl_data *nl_data_alloc(void *buf, size_t size)
+struct nl_data *nl_data_alloc(const void *buf, size_t size)
 {
 	struct nl_data *data;
 
@@ -81,7 +82,7 @@
  * @see nla_data_alloc
  * @return Newly allocated data handle or NULL
  */
-struct nl_data *nl_data_alloc_attr(struct nlattr *nla)
+struct nl_data *nl_data_alloc_attr(const struct nlattr *nla)
 {
 	return nl_data_alloc(nla_data(nla), nla_len(nla));
 }
@@ -92,7 +93,7 @@
  *
  * @return Cloned object or NULL
  */
-struct nl_data *nl_data_clone(struct nl_data *src)
+struct nl_data *nl_data_clone(const struct nl_data *src)
 {
 	return nl_data_alloc(src->d_data, src->d_size);
 }
@@ -108,18 +109,19 @@
  * 
  * @return 0 on success or a negative error code
  */
-int nl_data_append(struct nl_data *data, void *buf, size_t size)
+int nl_data_append(struct nl_data *data, const void *buf, size_t size)
 {
 	if (size > 0) {
-		data->d_data = realloc(data->d_data, data->d_size + size);
-		if (!data->d_data)
+		char *d_data = realloc(data->d_data, data->d_size + size);
+		if (!d_data)
 			return -NLE_NOMEM;
 
 		if (buf)
-			memcpy(data->d_data + data->d_size, buf, size);
+			memcpy(d_data + data->d_size, buf, size);
 		else
-			memset(data->d_data + data->d_size, 0, size);
+			memset(d_data + data->d_size, 0, size);
 
+		data->d_data = d_data;
 		data->d_size += size;
 	}
 
@@ -150,9 +152,11 @@
  * @arg data		Abstract data object.
  * @return Data buffer or NULL if empty.
  */
-void *nl_data_get(struct nl_data *data)
+void *nl_data_get(const struct nl_data *data)
 {
-	return data->d_size > 0 ? data->d_data : NULL;
+	if (data->d_size > 0)
+		return (void*)data->d_data;
+	return NULL;
 }
 
 /**
@@ -160,7 +164,7 @@
  * @arg data		Abstract data object.
  * @return Size of data buffer.
  */
-size_t nl_data_get_size(struct nl_data *data)
+size_t nl_data_get_size(const struct nl_data *data)
 {
 	return data->d_size;
 }
@@ -180,10 +184,10 @@
  *         a is found, respectively, to be less than, to match, or
  *         be greater than b.
  */
-int nl_data_cmp(struct nl_data *a, struct nl_data *b)
+int nl_data_cmp(const struct nl_data *a, const struct nl_data *b)
 {
-	void *a_ = nl_data_get(a);
-	void *b_ = nl_data_get(b);
+	const void *a_ = nl_data_get(a);
+	const void *b_ = nl_data_get(b);
 
 	if (a_ && b_)
 		return memcmp(a_, b_, nl_data_get_size(a));
diff --git a/lib/error.c b/lib/error.c
index f30b9a5..1106cb0 100644
--- a/lib/error.c
+++ b/lib/error.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/error.c		Error Handling
  *
@@ -47,6 +48,7 @@
 [NLE_NODEV]		= "No such device",
 [NLE_IMMUTABLE]		= "Immutable attribute",
 [NLE_DUMP_INTR]		= "Dump inconsistency detected, interrupted",
+[NLE_ATTRSIZE]		= "Attribute max length exceeded",
 };
 
 /**
diff --git a/lib/fib_lookup/lookup.c b/lib/fib_lookup/lookup.c
index 3d07317..fbd6291 100644
--- a/lib/fib_lookup/lookup.c
+++ b/lib/fib_lookup/lookup.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/fib_lookup/lookup.c	FIB Lookup
  *
@@ -17,6 +18,7 @@
  */
 
 #include <netlink-private/netlink.h>
+#include <netlink-private/utils.h>
 #include <netlink/netlink.h>
 #include <netlink/attr.h>
 #include <netlink/utils.h>
@@ -133,7 +135,7 @@
 		     nl_rtntype2str(res->fr_type, buf, sizeof(buf)));
 	nl_dump(p, "scope %s error %s (%d)\n",
 		rtnl_scope2str(res->fr_scope, buf, sizeof(buf)),
-		strerror_r(-res->fr_error, buf, sizeof(buf)), res->fr_error);
+		nl_strerror_l(-res->fr_error), res->fr_error);
 }
 
 static void result_dump_details(struct nl_object *obj, struct nl_dump_params *p)
@@ -141,8 +143,8 @@
 	result_dump_line(obj, p);
 }
 
-static int result_compare(struct nl_object *_a, struct nl_object *_b,
-			uint32_t attrs, int flags)
+static uint64_t result_compare(struct nl_object *_a, struct nl_object *_b,
+			uint64_t attrs, int flags)
 {
 	return 0;
 }
@@ -270,7 +272,7 @@
 	if (err < 0)
 		return err;
 
-	return nl_cache_pickup(sk, cache);
+	return nl_cache_pickup_checkdup(sk, cache);
 }
 
 /** @} */
diff --git a/lib/fib_lookup/request.c b/lib/fib_lookup/request.c
index 1b021b6..7749a07 100644
--- a/lib/fib_lookup/request.c
+++ b/lib/fib_lookup/request.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/fib_lookup/request.c	FIB Lookup Request
  *
@@ -53,12 +54,12 @@
 	return 0;
 }
 
-static int request_compare(struct nl_object *_a, struct nl_object *_b,
-			   uint32_t attrs, int flags)
+static uint64_t request_compare(struct nl_object *_a, struct nl_object *_b,
+			   uint64_t attrs, int flags)
 {
 	struct flnl_request *a = (struct flnl_request *) _a;
 	struct flnl_request *b = (struct flnl_request *) _b;
-	int diff = 0;
+	uint64_t diff = 0;
 
 #define REQ_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, REQUEST_ATTR_##ATTR, a, b, EXPR)
 
diff --git a/lib/genl/ctrl.c b/lib/genl/ctrl.c
index ce07f1d..2d5d6f2 100644
--- a/lib/genl/ctrl.c
+++ b/lib/genl/ctrl.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/genl/ctrl.c		Generic Netlink Controller
  *
diff --git a/lib/genl/family.c b/lib/genl/family.c
index 897c809..ed8b08b 100644
--- a/lib/genl/family.c
+++ b/lib/genl/family.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/genl/family.c		Generic Netlink Family
  *
@@ -24,6 +25,8 @@
 #include <netlink/genl/family.h>
 #include <netlink/utils.h>
 
+#include "netlink-private/utils.h"
+
 /** @cond SKIP */
 #define FAMILY_ATTR_ID		0x01
 #define FAMILY_ATTR_NAME	0x02
@@ -96,10 +99,10 @@
 }
 
 static const struct trans_tbl ops_flags[] = {
-	__ADD(GENL_ADMIN_PERM, admin_perm)
-	__ADD(GENL_CMD_CAP_DO, has_doit)
-	__ADD(GENL_CMD_CAP_DUMP, has_dump)
-	__ADD(GENL_CMD_CAP_HASPOL, has_policy)
+	__ADD(GENL_ADMIN_PERM, admin_perm),
+	__ADD(GENL_CMD_CAP_DO, has_doit),
+	__ADD(GENL_CMD_CAP_DUMP, has_dump),
+	__ADD(GENL_CMD_CAP_HASPOL, has_policy),
 };
 
 static char *ops_flags2str(int flags, char *buf, size_t len)
@@ -147,12 +150,12 @@
 	family_dump_details(obj, p);
 }
 
-static int family_compare(struct nl_object *_a, struct nl_object *_b,
-			  uint32_t attrs, int flags)
+static uint64_t family_compare(struct nl_object *_a, struct nl_object *_b,
+			  uint64_t attrs, int flags)
 {
 	struct genl_family *a = (struct genl_family *) _a;
 	struct genl_family *b = (struct genl_family *) _b;
-	int diff = 0;
+	uint64_t diff = 0;
 
 #define FAM_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, FAMILY_ATTR_##ATTR, a, b, EXPR)
 
@@ -215,7 +218,7 @@
 	if (family->ce_mask & FAMILY_ATTR_ID)
 		return family->gf_id;
 	else
-		return GENL_ID_GENERATE;
+		return 0;
 }
 
 /**
@@ -330,7 +333,7 @@
 	if (family->ce_mask & FAMILY_ATTR_MAXATTR)
 		return family->gf_maxattr;
 	else
-		return family->gf_maxattr;
+		return 0;
 }
 
 void genl_family_set_maxattr(struct genl_family *family, uint32_t maxattr)
@@ -364,16 +367,20 @@
 }
 
 int genl_family_add_grp(struct genl_family *family, uint32_t id,
-	       		const char *name)
+                        const char *name)
 {
-	struct genl_family_grp *grp;  
+	struct genl_family_grp *grp;
+
+	if (   !name
+	    || strlen (name) >= GENL_NAMSIZ)
+		return -NLE_INVAL;
 
 	grp = calloc(1, sizeof(*grp));
 	if (grp == NULL)
 		return -NLE_NOMEM;
 
 	grp->id = id;
-	strncpy(grp->name, name, GENL_NAMSIZ - 1);
+	_nl_strncpy(grp->name, name, GENL_NAMSIZ);
 
 	nl_list_add_tail(&grp->list, &family->gf_mc_grps);
 
diff --git a/lib/genl/genl.c b/lib/genl/genl.c
index 0c9b11e..b4f37b5 100644
--- a/lib/genl/genl.c
+++ b/lib/genl/genl.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/genl/genl.c		Generic Netlink
  *
@@ -79,7 +80,8 @@
  *
  * @see nl_send_simple()
  *
- * @return 0 on success or a negative error code.
+ * @return 0 on success or a negative error code. Due to a bug, this function
+ * returns the number of bytes sent. Treat any non-negative number as success.
  */
 int genl_send_simple(struct nl_sock *sk, int family, int cmd,
 		     int version, int flags)
@@ -149,7 +151,7 @@
  * @return 0 on success or a negative error code.
  */
 int genlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype,
-		     struct nla_policy *policy)
+		     const struct nla_policy *policy)
 {
 	struct genlmsghdr *ghdr;
 
@@ -177,7 +179,7 @@
  * @code
  * struct nlattr *attrs[MY_TYPE_MAX+1];
  *
- * if ((err = genlsmg_parse(nlmsg_nlh(msg), sizeof(struct my_hdr), attrs,
+ * if ((err = genlmsg_parse(nlmsg_hdr(msg), sizeof(struct my_hdr), attrs,
  *                          MY_TYPE_MAX, attr_policy)) < 0)
  * 	// ERROR
  * @endcode
@@ -189,7 +191,7 @@
  * @return 0 on success or a negative error code.
  */
 int genlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
-		  int maxtype, struct nla_policy *policy)
+		  int maxtype, const struct nla_policy *policy)
 {
 	struct genlmsghdr *ghdr;
 
@@ -258,7 +260,7 @@
  */
 void *genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen)
 {
-	return genlmsg_user_hdr(gnlh) + NLMSG_ALIGN(hdrlen);
+	return (char *) genlmsg_user_hdr(gnlh) + NLMSG_ALIGN(hdrlen);
 }
 
 /**
@@ -362,7 +364,7 @@
 	NL_DBG(2, "msg %p: Added generic netlink header cmd=%d version=%d\n",
 	       msg, cmd, version);
 
-	return nlmsg_data(nlh) + GENL_HDRLEN;
+	return (char *) nlmsg_data(nlh) + GENL_HDRLEN;
 }
 
 /** @} */
diff --git a/lib/genl/mngt.c b/lib/genl/mngt.c
index 3648663..28326cd 100644
--- a/lib/genl/mngt.c
+++ b/lib/genl/mngt.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/genl/mngt.c		Generic Netlink Management
  *
@@ -26,6 +27,8 @@
 #include <netlink/genl/ctrl.h>
 #include <netlink/utils.h>
 
+#include "netlink-private/utils.h"
+
 /** @cond SKIP */
 
 static NL_LIST_HEAD(genl_ops_list);
@@ -45,41 +48,45 @@
 }
 
 static int cmd_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *nlh,
-			  struct genl_ops *ops, struct nl_cache_ops *cache_ops, void *arg)
+                          struct genl_ops *ops, struct nl_cache_ops *cache_ops, void *arg)
 {
+	_nl_auto_free struct nlattr **tb_free = NULL;
 	int err;
 	struct genlmsghdr *ghdr;
 	struct genl_cmd *cmd;
+	struct nlattr **tb;
 
 	ghdr = genlmsg_hdr(nlh);
 
-	if (!(cmd = lookup_cmd(ops, ghdr->cmd))) {
-		err = -NLE_MSGTYPE_NOSUPPORT;
-		goto errout;
-	}
+	if (!(cmd = lookup_cmd(ops, ghdr->cmd)))
+		return -NLE_MSGTYPE_NOSUPPORT;
 
 	if (cmd->c_msg_parser == NULL)
-		err = -NLE_OPNOTSUPP;
-	else {
-		struct nlattr *tb[cmd->c_maxattr + 1];
+		return -NLE_OPNOTSUPP;
+
+	tb = _nl_malloc_maybe_a (300, (((size_t) cmd->c_maxattr) + 1u) * sizeof (struct nlattr *), &tb_free);
+	if (!tb)
+		return -NLE_NOMEM;
+
+	err = nlmsg_parse(nlh,
+	                  GENL_HDRSIZE(ops->o_hdrsize),
+	                  tb,
+	                  cmd->c_maxattr,
+	                  cmd->c_attr_policy);
+	if (err < 0)
+		return err;
+
+	{
 		struct genl_info info = {
-			.who = who,
-			.nlh = nlh,
+			.who     = who,
+			.nlh     = nlh,
 			.genlhdr = ghdr,
 			.userhdr = genlmsg_user_hdr(ghdr),
-			.attrs = tb,
+			.attrs   = tb,
 		};
 
-		err = nlmsg_parse(nlh, GENL_HDRSIZE(ops->o_hdrsize), tb, cmd->c_maxattr,
-				  cmd->c_attr_policy);
-		if (err < 0)
-			goto errout;
-
-		err = cmd->c_msg_parser(cache_ops, cmd, &info, arg);
+		return cmd->c_msg_parser(cache_ops, cmd, &info, arg);
 	}
-errout:
-	return err;
-
 }
 
 static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
@@ -313,7 +320,7 @@
 	int err = 0;
 
 	/* Check if resolved already */
-	if (ops->o_id != GENL_ID_GENERATE)
+	if (ops->o_id != 0)
 		return 0;
 
 	if (!ops->o_name)
diff --git a/lib/handlers.c b/lib/handlers.c
index a6a97bb..85cb3c6 100644
--- a/lib/handlers.c
+++ b/lib/handlers.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/handlers.c	default netlink message handlers
  *
@@ -26,6 +27,7 @@
  */
 
 #include <netlink-private/netlink.h>
+#include <netlink-private/utils.h>
 #include <netlink/netlink.h>
 #include <netlink/utils.h>
 #include <netlink/msg.h>
@@ -79,10 +81,9 @@
 				    struct nlmsgerr *e, void *arg)
 {
 	FILE *ofd = arg ? arg : stderr;
-	char buf[256];
 
 	fprintf(ofd, "-- Error received: %s\n-- Original message: ",
-		strerror_r(-e->error, buf, sizeof(buf)));
+		nl_strerror_l(-e->error));
 	print_header_content(ofd, &e->msg);
 	fprintf(ofd, "\n");
 
@@ -203,7 +204,7 @@
 	int i;
 	struct nl_cb *cb;
 
-	if (kind < 0 || kind > NL_CB_KIND_MAX)
+	if ((unsigned int) kind > NL_CB_KIND_MAX)
 		return NULL;
 
 	cb = calloc(1, sizeof(*cb));
@@ -293,10 +294,10 @@
 int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind,
 	      nl_recvmsg_msg_cb_t func, void *arg)
 {
-	if (type < 0 || type > NL_CB_TYPE_MAX)
+	if ((unsigned int) type > NL_CB_TYPE_MAX)
 		return -NLE_RANGE;
 
-	if (kind < 0 || kind > NL_CB_KIND_MAX)
+	if ((unsigned int) kind > NL_CB_KIND_MAX)
 		return -NLE_RANGE;
 
 	if (kind == NL_CB_CUSTOM) {
@@ -343,7 +344,7 @@
 int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind,
 	      nl_recvmsg_err_cb_t func, void *arg)
 {
-	if (kind < 0 || kind > NL_CB_KIND_MAX)
+	if ((unsigned int) kind > NL_CB_KIND_MAX)
 		return -NLE_RANGE;
 
 	if (kind == NL_CB_CUSTOM) {
diff --git a/lib/hash.c b/lib/hash.c
index 47c938b..17b5c8f 100644
--- a/lib/hash.c
+++ b/lib/hash.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * This code was taken from http://ccodearchive.net/info/hash.html
  * The original file was modified to remove unwanted code
@@ -321,17 +322,17 @@
     /*-------------------------------- last block: affect all 32 bits of (c) */
     switch(length)                   /* all the case statements fall through */
     {
-    case 12: c+=((uint32_t)k[11])<<24;
-    case 11: c+=((uint32_t)k[10])<<16;
-    case 10: c+=((uint32_t)k[9])<<8;
-    case 9 : c+=k[8];
-    case 8 : b+=((uint32_t)k[7])<<24;
-    case 7 : b+=((uint32_t)k[6])<<16;
-    case 6 : b+=((uint32_t)k[5])<<8;
-    case 5 : b+=k[4];
-    case 4 : a+=((uint32_t)k[3])<<24;
-    case 3 : a+=((uint32_t)k[2])<<16;
-    case 2 : a+=((uint32_t)k[1])<<8;
+    case 12: c+=((uint32_t)k[11])<<24;  /* fall through */
+    case 11: c+=((uint32_t)k[10])<<16;  /* fall through */
+    case 10: c+=((uint32_t)k[9])<<8;    /* fall through */
+    case 9 : c+=k[8];                   /* fall through */
+    case 8 : b+=((uint32_t)k[7])<<24;   /* fall through */
+    case 7 : b+=((uint32_t)k[6])<<16;   /* fall through */
+    case 6 : b+=((uint32_t)k[5])<<8;    /* fall through */
+    case 5 : b+=k[4];                   /* fall through */
+    case 4 : a+=((uint32_t)k[3])<<24;   /* fall through */
+    case 3 : a+=((uint32_t)k[2])<<16;   /* fall through */
+    case 2 : a+=((uint32_t)k[1])<<8;    /* fall through */
     case 1 : a+=k[0];
              break;
     case 0 : return c;
@@ -451,18 +452,18 @@
     /*-------------------------------- last block: affect all 32 bits of (c) */
     switch(length)                   /* all the case statements fall through */
     {
-    case 12: c+=k[11];
-    case 11: c+=((uint32_t)k[10])<<8;
-    case 10: c+=((uint32_t)k[9])<<16;
-    case 9 : c+=((uint32_t)k[8])<<24;
-    case 8 : b+=k[7];
-    case 7 : b+=((uint32_t)k[6])<<8;
-    case 6 : b+=((uint32_t)k[5])<<16;
-    case 5 : b+=((uint32_t)k[4])<<24;
-    case 4 : a+=k[3];
-    case 3 : a+=((uint32_t)k[2])<<8;
-    case 2 : a+=((uint32_t)k[1])<<16;
-    case 1 : a+=((uint32_t)k[0])<<24;
+    case 12: c+=k[11];                  /* fall through */
+    case 11: c+=((uint32_t)k[10])<<8;   /* fall through */
+    case 10: c+=((uint32_t)k[9])<<16;   /* fall through */
+    case 9 : c+=((uint32_t)k[8])<<24;   /* fall through */
+    case 8 : b+=k[7];                   /* fall through */
+    case 7 : b+=((uint32_t)k[6])<<8;    /* fall through */
+    case 6 : b+=((uint32_t)k[5])<<16;   /* fall through */
+    case 5 : b+=((uint32_t)k[4])<<24;   /* fall through */
+    case 4 : a+=k[3];                   /* fall through */
+    case 3 : a+=((uint32_t)k[2])<<8;    /* fall through */
+    case 2 : a+=((uint32_t)k[1])<<16;   /* fall through */
+    case 1 : a+=((uint32_t)k[0])<<24;   /* fall through */
              break;
     case 0 : return c;
     }
diff --git a/lib/hashtable.c b/lib/hashtable.c
index 8b15925..db4ed8b 100644
--- a/lib/hashtable.c
+++ b/lib/hashtable.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * netlink/hashtable.c      Netlink hashtable Utilities
  *
@@ -191,7 +192,7 @@
 
 uint32_t nl_hash(void *k, size_t length, uint32_t initval)
 {
-	return(__nl_hash(k, length, initval));
+	return(__nl_hash((char *) k, length, initval));
 }
 
 /** @} */
diff --git a/lib/idiag/idiag.c b/lib/idiag/idiag.c
index 81d73db..23a8413 100644
--- a/lib/idiag/idiag.c
+++ b/lib/idiag/idiag.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  *  lib/idiag/idiag.c    Inet Diag Netlink
  *
@@ -55,9 +56,11 @@
  * @arg flags	Message flags
  * @arg family	Address family
  * @arg states	Socket states to query
- * @arg ext	Inet Diag attribute extensions to query
+ * @arg ext	Inet Diag attribute extensions to query. Note that this only supports
+ *   8 bit arguments. Flags outside uint8_t range are silently ignored.
  *
- * @return Newly allocated netlink message or NULL.
+ * @return 0 on success or a negative error code. Due to a bug, this function
+ * returns the number of bytes sent. Treat any non-negative number as success.
  */
 int idiagnl_send_simple(struct nl_sock *sk, int flags, uint8_t family,
 		uint16_t states, uint16_t ext)
@@ -82,25 +85,22 @@
  */
 
 static const struct trans_tbl idiag_states[] = {
-	__ADD(IDIAG_SS_UNKNOWN, unknown)
-	__ADD(IDIAG_SS_ESTABLISHED, established)
-	__ADD(IDIAG_SS_SYN_SENT, syn_sent)
-	__ADD(IDIAG_SS_SYN_RECV, syn_recv)
-	__ADD(IDIAG_SS_FIN_WAIT1, fin_wait)
-	__ADD(IDIAG_SS_FIN_WAIT2, fin_wait2)
-	__ADD(IDIAG_SS_TIME_WAIT, time_wait)
-	__ADD(IDIAG_SS_CLOSE, close)
-	__ADD(IDIAG_SS_CLOSE_WAIT, close_wait)
-	__ADD(IDIAG_SS_LAST_ACK, last_ack)
-	__ADD(IDIAG_SS_LISTEN, listen)
-	__ADD(IDIAG_SS_CLOSING, closing)
-	__ADD(IDIAG_SS_MAX, max)
-	{ ((1<<IDIAG_SS_MAX)-1), "all" }
+	__ADD(TCP_ESTABLISHED, established),
+	__ADD(TCP_SYN_SENT, syn_sent),
+	__ADD(TCP_SYN_RECV, syn_recv),
+	__ADD(TCP_FIN_WAIT1, fin_wait),
+	__ADD(TCP_FIN_WAIT2, fin_wait2),
+	__ADD(TCP_TIME_WAIT, time_wait),
+	__ADD(TCP_CLOSE, close),
+	__ADD(TCP_CLOSE_WAIT, close_wait),
+	__ADD(TCP_LAST_ACK, last_ack),
+	__ADD(TCP_LISTEN, listen),
+	__ADD(TCP_CLOSING, closing),
 };
 
 /**
  * Convert inet diag socket states to strings.
- * @arg state	  inetdiag socket state (e.g., IDIAG_SS_ESTABLISHED)
+ * @arg state	  inetdiag socket state (e.g., TCP_ESTABLISHED)
  * @arg buf	  output buffer which will hold string result
  * @arg len	  length in bytes of the output buffer
  *
@@ -126,17 +126,17 @@
 }
 
 static const struct trans_tbl idiag_timers[] = {
-	__ADD(IDIAG_TIMER_OFF, off)
-	__ADD(IDIAG_TIMER_ON, on)
-	__ADD(IDIAG_TIMER_KEEPALIVE, keepalive)
-	__ADD(IDIAG_TIMER_TIMEWAIT, timewait)
-	__ADD(IDIAG_TIMER_PERSIST, persist)
-	__ADD(IDIAG_TIMER_UNKNOWN, unknown)
+	__ADD(IDIAGNL_TIMER_OFF, off),
+	__ADD(IDIAGNL_TIMER_ON, on),
+	__ADD(IDIAGNL_TIMER_KEEPALIVE, keepalive),
+	__ADD(IDIAGNL_TIMER_TIMEWAIT, timewait),
+	__ADD(IDIAGNL_TIMER_PERSIST, persist),
+	__ADD(IDIAGNL_TIMER_UNKNOWN, unknown),
 };
 
 /**
  * Convert inet diag timer types to strings.
- * @arg timer	  inetdiag timer (e.g., IDIAG_TIMER_ON)
+ * @arg timer	  inetdiag timer (e.g., IDIAGNL_TIMER_ON)
  * @arg buf	  output buffer which will hold string result
  * @arg len	  length in bytes of the output buffer
  *
@@ -160,34 +160,61 @@
 }
 
 static const struct trans_tbl idiag_attrs[] = {
-	__ADD(IDIAG_ATTR_NONE, none)
-	__ADD(IDIAG_ATTR_MEMINFO, meminfo)
-	__ADD(IDIAG_ATTR_INFO, info)
-	__ADD(IDIAG_ATTR_VEGASINFO, vegasinfo)
-	__ADD(IDIAG_ATTR_CONG, congestion)
-	__ADD(IDIAG_ATTR_TOS, tos)
-	__ADD(IDIAG_ATTR_TCLASS, tclass)
+	__ADD(INET_DIAG_NONE, none),
+	__ADD(INET_DIAG_MEMINFO, meminfo),
+	__ADD(INET_DIAG_INFO, info),
+	__ADD(INET_DIAG_VEGASINFO, vegasinfo),
+	__ADD(INET_DIAG_CONG, congestion),
+	__ADD(INET_DIAG_TOS, tos),
+	__ADD(INET_DIAG_TCLASS, tclass),
+	__ADD(INET_DIAG_SKMEMINFO, skmeminfo),
+	__ADD(INET_DIAG_SHUTDOWN, shutdown),
 };
 
 /**
- * Convert inetdiag extended attributes to strings.
- * @arg attrs	  inetdiag attribute (e.g., IDIAG_ATTR_MEMINFO)
+ * Convert inet diag extension type to a string.
+ * @arg attrs	  inet diag extension type (e.g. INET_DIAG_MEMINFO)
  * @arg buf	  output buffer which will hold string result
  * @arg len	  length in bytes of the output buffer
  *
- * @return string representation of attrs or an empty string.
+ * @return string representation of inet diag extension type or an empty string.
+ * @deprecated: don't use this function. It is not very useful and should
+ * never have been exposed as public API.
  */
 char *idiagnl_attrs2str(int attrs, char *buf, size_t len)
 {
 	return __type2str(attrs, buf, len, idiag_attrs, ARRAY_SIZE(idiag_attrs));
 }
 
+static const struct trans_tbl idiag_exts[] = {
+	__ADD((1 << (INET_DIAG_MEMINFO - 1)), meminfo),
+	__ADD((1 << (INET_DIAG_INFO - 1)), info),
+	__ADD((1 << (INET_DIAG_VEGASINFO - 1)), vegasinfo),
+	__ADD((1 << (INET_DIAG_CONG - 1)), congestion),
+	__ADD((1 << (INET_DIAG_TOS - 1)), tos),
+	__ADD((1 << (INET_DIAG_TCLASS - 1)), tclass),
+	__ADD((1 << (INET_DIAG_SKMEMINFO - 1)), skmeminfo),
+	__ADD((1 << (INET_DIAG_SHUTDOWN - 1)), shutdown),
+};
+
+/**
+ * Convert inet diag extension flags to a string.
+ * @arg attrs	inet diag extension flags (e.g.
+ *   ( (1<<(INET_DIAG_MEMINFO-1)) | (1<<(INET_DIAG_CONG-1)) | (1<<(INET_DIAG_TOS-1)) ) )
+ * @arg buf	Output buffer to hold string representation
+ * @arg len	length in bytes of the output buffer
+ */
+char *idiagnl_exts2str(uint8_t attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, idiag_exts, ARRAY_SIZE(idiag_exts));
+}
+
 static const struct trans_tbl idiagnl_tcpstates[] = {
-	__ADD(TCP_CA_Open, open)
-	__ADD(TCP_CA_Disorder, disorder)
-	__ADD(TCP_CA_CWR, cwr)
-	__ADD(TCP_CA_Recovery, recovery)
-	__ADD(TCP_CA_Loss, loss)
+	__ADD(TCP_CA_Open, open),
+	__ADD(TCP_CA_Disorder, disorder),
+	__ADD(TCP_CA_CWR, cwr),
+	__ADD(TCP_CA_Recovery, recovery),
+	__ADD(TCP_CA_Loss, loss),
 };
 
 /**
@@ -203,10 +230,10 @@
 }
 
 static const struct trans_tbl idiagnl_tcpopt_attrs[] = {
-	__ADD(TCPI_OPT_TIMESTAMPS, timestamps)
-	__ADD(TCPI_OPT_SACK, sACK)
-	__ADD(TCPI_OPT_WSCALE, wscale)
-	__ADD(TCPI_OPT_ECN, ecn)
+	__ADD(TCPI_OPT_TIMESTAMPS, timestamps),
+	__ADD(TCPI_OPT_SACK, sACK),
+	__ADD(TCPI_OPT_WSCALE, wscale),
+	__ADD(TCPI_OPT_ECN, ecn),
 };
 
 /**
@@ -234,40 +261,18 @@
  */
 char * idiagnl_shutdown2str(uint8_t shutdown, char *buf, size_t len)
 {
-  if (shutdown == 0) {
-	  snprintf(buf, len, " ");
-	  return buf;
-  } else if (shutdown == 1) {
-	  snprintf(buf, len, "receive shutdown");
-	  return buf;
-  } else if (shutdown == 2) {
-	  snprintf(buf, len, "send shutdown");
-	  return buf;
-  }
+	if (shutdown == 0) {
+		snprintf(buf, len, " ");
+		return buf;
+	} else if (shutdown == 1) {
+		snprintf(buf, len, "receive shutdown");
+		return buf;
+	} else if (shutdown == 2) {
+		snprintf(buf, len, "send shutdown");
+		return buf;
+	}
 
-  return NULL;
-}
-
-static const struct trans_tbl idiag_exts[] = {
-	__ADD(IDIAG_ATTR_NONE, none)
-	__ADD(IDIAG_ATTR_MEMINFO, meminfo)
-	__ADD(IDIAG_ATTR_INFO, info)
-	__ADD(IDIAG_ATTR_VEGASINFO, vegasinfo)
-	__ADD(IDIAG_ATTR_CONG, congestion)
-	__ADD(IDIAG_ATTR_TOS, tos)
-	__ADD(IDIAG_ATTR_TCLASS, tclass)
-};
-
-/**
- * Convert inet diag extension flags to a string.
- * @arg attrs	inet diag extension flags (e.g., (IDIAG_ATTR_MEMINFO |
- *   IDIAG_ATTR_CONG | IDIAG_ATTR_TOS))
- * @arg buf	Output buffer to hold string representation
- * @arg len	length in bytes of the output buffer
- */
-char *idiagnl_exts2str(uint8_t attrs, char *buf, size_t len)
-{
-	return __flags2str(attrs, buf, len, idiag_exts, ARRAY_SIZE(idiag_exts));
+	return NULL;
 }
 
 /** @} */
diff --git a/lib/idiag/idiag_meminfo_obj.c b/lib/idiag/idiag_meminfo_obj.c
index a60f497..0ada71e 100644
--- a/lib/idiag/idiag_meminfo_obj.c
+++ b/lib/idiag/idiag_meminfo_obj.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/idiag/idiagnl_meminfo_obj.c Inet Diag Meminfo Object
  *
@@ -80,21 +81,25 @@
 }
 /** @} */
 
-static int idiagnl_meminfo_clone(struct nl_object *_dst, struct nl_object *_src)
+/** @cond SKIP */
+static uint64_t idiagnl_meminfo_compare(struct nl_object *_a, struct nl_object *_b,
+                                     uint64_t attrs, int flags)
 {
-	struct idiagnl_meminfo *dst = (struct idiagnl_meminfo *) _dst;
-	struct idiagnl_meminfo *src = (struct idiagnl_meminfo *) _src;
+	struct idiagnl_meminfo *a = (struct idiagnl_meminfo *) _a;
+	struct idiagnl_meminfo *b = (struct idiagnl_meminfo *) _b;
 
-	memcpy(dst, src, sizeof(struct idiagnl_meminfo));
-
-	return 0;
+	/* meminfo is a very simple object. It has no attribe flags (ce_mask),
+	 * hence compare just returns 0 or 1, not a bit mask of attributes. */
+	return a->idiag_rmem != b->idiag_rmem ||
+	       a->idiag_wmem != b->idiag_wmem ||
+	       a->idiag_fmem != b->idiag_fmem ||
+	       a->idiag_tmem != b->idiag_tmem;
 }
 
-/** @cond SKIP */
 struct nl_object_ops idiagnl_meminfo_obj_ops = {
 	.oo_name	= "idiag/idiag_meminfo",
 	.oo_size	= sizeof(struct idiagnl_meminfo),
-	.oo_clone	= idiagnl_meminfo_clone,
+	.oo_compare     = idiagnl_meminfo_compare,
 };
 /** @endcond */
 /** @} */
diff --git a/lib/idiag/idiag_msg_obj.c b/lib/idiag/idiag_msg_obj.c
index 19e6c5b..a1beb2c 100644
--- a/lib/idiag/idiag_msg_obj.c
+++ b/lib/idiag/idiag_msg_obj.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/idiag/idiagnl_msg_obj.c Inet Diag Message Object
  *
@@ -10,11 +11,40 @@
  */
 
 #include <netlink-private/netlink.h>
+#include <netlink/hashtable.h>
 #include <netlink/idiag/msg.h>
 #include <netlink/idiag/meminfo.h>
 #include <netlink/idiag/vegasinfo.h>
 #include <linux/inet_diag.h>
 
+
+/** @cond SKIP */
+#define IDIAGNL_ATTR_FAMILY                     (0x1 << 1)
+#define IDIAGNL_ATTR_STATE                      (0x1 << 2)
+#define IDIAGNL_ATTR_TIMER                      (0x1 << 3)
+#define IDIAGNL_ATTR_RETRANS                    (0x1 << 4)
+#define IDIAGNL_ATTR_SPORT                      (0x1 << 5)
+#define IDIAGNL_ATTR_DPORT                      (0x1 << 6)
+#define IDIAGNL_ATTR_SRC                        (0x1 << 7)
+#define IDIAGNL_ATTR_DST                        (0x1 << 8)
+#define IDIAGNL_ATTR_IFINDEX                    (0x1 << 9)
+#define IDIAGNL_ATTR_EXPIRES                    (0x1 << 10)
+#define IDIAGNL_ATTR_RQUEUE                     (0x1 << 11)
+#define IDIAGNL_ATTR_WQUEUE                     (0x1 << 12)
+#define IDIAGNL_ATTR_UID                        (0x1 << 13)
+#define IDIAGNL_ATTR_INODE                      (0x1 << 14)
+#define IDIAGNL_ATTR_TOS                        (0x1 << 15)
+#define IDIAGNL_ATTR_TCLASS                     (0x1 << 16)
+#define IDIAGNL_ATTR_SHUTDOWN                   (0x1 << 17)
+#define IDIAGNL_ATTR_CONG                       (0x1 << 18)
+#define IDIAGNL_ATTR_MEMINFO                    (0x1 << 19)
+#define IDIAGNL_ATTR_VEGASINFO                  (0x1 << 20)
+#define IDIAGNL_ATTR_TCPINFO                    (0x1 << 21)
+#define IDIAGNL_ATTR_SKMEMINFO                  (0x1 << 22)
+
+#define _INET_DIAG_ALL ((1<<(INET_DIAG_MAX+1))-1)
+/** @endcond */
+
 /**
  * @ingroup idiag
  * @defgroup idiagnl_msg Inet Diag Messages
@@ -60,15 +90,25 @@
 	int family = cache->c_iarg1;
 	int states = cache->c_iarg2;
 
-	return idiagnl_send_simple(sk, 0, family, states, IDIAG_ATTR_ALL);
+	/* idiagnl_send_simple()'s "ext" argument is u16, which is too small for _INET_DIAG_ALL,
+	 * which is more than 16 bits on recent kernels.
+	 *
+	 * Actually, internally idiagnl_send_simple() sets "struct inet_diag_req"'s "idiag_ext"
+	 * field, which is only 8 bits. So, it's even worse.
+	 *
+	 * FIXME: this probably should be fixed (by adding idiagnl_send_simple2() function), but for
+	 *    the moment it means we cannot request more than 0xFF.
+	 */
+
+	return idiagnl_send_simple(sk, 0, family, states, (uint16_t) _INET_DIAG_ALL);
 }
 
 static struct nl_cache_ops idiagnl_msg_ops = {
 	.co_name		= "idiag/idiag",
 	.co_hdrsize		= sizeof(struct inet_diag_msg),
 	.co_msgtypes		= {
-		{ IDIAG_TCPDIAG_GETSOCK, NL_ACT_NEW, "new" },
-		{ IDIAG_DCCPDIAG_GETSOCK, NL_ACT_NEW, "new" },
+		{ TCPDIAG_GETSOCK, NL_ACT_NEW, "new" },
+		{ DCCPDIAG_GETSOCK, NL_ACT_NEW, "new" },
 		END_OF_MSGTYPES_LIST,
 	},
 	.co_protocol		= NETLINK_INET_DIAG,
@@ -139,6 +179,7 @@
 void idiagnl_msg_set_family(struct idiagnl_msg *msg, uint8_t family)
 {
 	msg->idiag_family = family;
+	msg->ce_mask |= IDIAGNL_ATTR_FAMILY;
 }
 
 uint8_t idiagnl_msg_get_state(const struct idiagnl_msg *msg)
@@ -149,6 +190,7 @@
 void idiagnl_msg_set_state(struct idiagnl_msg *msg, uint8_t state)
 {
 	msg->idiag_state = state;
+	msg->ce_mask |= IDIAGNL_ATTR_STATE;
 }
 
 uint8_t idiagnl_msg_get_timer(const struct idiagnl_msg *msg)
@@ -159,6 +201,7 @@
 void idiagnl_msg_set_timer(struct idiagnl_msg *msg, uint8_t timer)
 {
 	msg->idiag_timer = timer;
+	msg->ce_mask |= IDIAGNL_ATTR_TIMER;
 }
 
 uint8_t idiagnl_msg_get_retrans(const struct idiagnl_msg *msg)
@@ -169,6 +212,7 @@
 void idiagnl_msg_set_retrans(struct idiagnl_msg *msg, uint8_t retrans)
 {
 	msg->idiag_retrans = retrans;
+	msg->ce_mask |= IDIAGNL_ATTR_RETRANS;
 }
 
 uint16_t idiagnl_msg_get_sport(struct idiagnl_msg *msg)
@@ -179,6 +223,7 @@
 void idiagnl_msg_set_sport(struct idiagnl_msg *msg, uint16_t port)
 {
 	msg->idiag_sport = port;
+	msg->ce_mask |= IDIAGNL_ATTR_SPORT;
 }
 
 uint16_t idiagnl_msg_get_dport(struct idiagnl_msg *msg)
@@ -189,6 +234,7 @@
 void idiagnl_msg_set_dport(struct idiagnl_msg *msg, uint16_t port)
 {
 	msg->idiag_dport = port;
+	msg->ce_mask |= IDIAGNL_ATTR_DPORT;
 }
 
 struct nl_addr *idiagnl_msg_get_src(const struct idiagnl_msg *msg)
@@ -203,6 +249,7 @@
 
 	nl_addr_get(addr);
 	msg->idiag_src = addr;
+	msg->ce_mask |= IDIAGNL_ATTR_SRC;
 
 	return 0;
 }
@@ -219,6 +266,7 @@
 
 	nl_addr_get(addr);
 	msg->idiag_dst = addr;
+	msg->ce_mask |= IDIAGNL_ATTR_DST;
 
 	return 0;
 }
@@ -231,6 +279,7 @@
 void idiagnl_msg_set_ifindex(struct idiagnl_msg *msg, uint32_t ifindex)
 {
 	msg->idiag_ifindex = ifindex;
+	msg->ce_mask |= IDIAGNL_ATTR_IFINDEX;
 }
 
 uint32_t idiagnl_msg_get_expires(const struct idiagnl_msg *msg)
@@ -241,6 +290,7 @@
 void idiagnl_msg_set_expires(struct idiagnl_msg *msg, uint32_t expires)
 {
 	msg->idiag_expires = expires;
+	msg->ce_mask |= IDIAGNL_ATTR_EXPIRES;
 }
 
 uint32_t idiagnl_msg_get_rqueue(const struct idiagnl_msg *msg)
@@ -251,6 +301,7 @@
 void idiagnl_msg_set_rqueue(struct idiagnl_msg *msg, uint32_t rqueue)
 {
 	msg->idiag_rqueue = rqueue;
+	msg->ce_mask |= IDIAGNL_ATTR_RQUEUE;
 }
 
 uint32_t idiagnl_msg_get_wqueue(const struct idiagnl_msg *msg)
@@ -261,6 +312,7 @@
 void idiagnl_msg_set_wqueue(struct idiagnl_msg *msg, uint32_t wqueue)
 {
 	msg->idiag_wqueue = wqueue;
+	msg->ce_mask |= IDIAGNL_ATTR_WQUEUE;
 }
 
 uint32_t idiagnl_msg_get_uid(const struct idiagnl_msg *msg)
@@ -271,6 +323,7 @@
 void idiagnl_msg_set_uid(struct idiagnl_msg *msg, uint32_t uid)
 {
 	msg->idiag_uid = uid;
+	msg->ce_mask |= IDIAGNL_ATTR_UID;
 }
 
 uint32_t idiagnl_msg_get_inode(const struct idiagnl_msg *msg)
@@ -281,6 +334,7 @@
 void idiagnl_msg_set_inode(struct idiagnl_msg *msg, uint32_t inode)
 {
 	msg->idiag_inode = inode;
+	msg->ce_mask |= IDIAGNL_ATTR_INODE;
 }
 
 uint8_t idiagnl_msg_get_tos(const struct idiagnl_msg *msg)
@@ -291,6 +345,7 @@
 void idiagnl_msg_set_tos(struct idiagnl_msg *msg, uint8_t tos)
 {
 	msg->idiag_tos = tos;
+	msg->ce_mask |= IDIAGNL_ATTR_TOS;
 }
 
 uint8_t idiagnl_msg_get_tclass(const struct idiagnl_msg *msg)
@@ -301,6 +356,7 @@
 void idiagnl_msg_set_tclass(struct idiagnl_msg *msg, uint8_t tclass)
 {
 	msg->idiag_tclass = tclass;
+	msg->ce_mask |= IDIAGNL_ATTR_TCLASS;
 }
 
 uint8_t	idiagnl_msg_get_shutdown(const struct idiagnl_msg *msg)
@@ -311,6 +367,7 @@
 void  idiagnl_msg_set_shutdown(struct idiagnl_msg *msg, uint8_t shutdown)
 {
 	msg->idiag_shutdown = shutdown;
+	msg->ce_mask |= IDIAGNL_ATTR_SHUTDOWN;
 }
 
 char *idiagnl_msg_get_cong(const struct idiagnl_msg *msg)
@@ -320,7 +377,9 @@
 
 void idiagnl_msg_set_cong(struct idiagnl_msg *msg, char *cong)
 {
+	free (msg->idiag_cong);
 	msg->idiag_cong = strdup(cong);
+	msg->ce_mask |= IDIAGNL_ATTR_CONG;
 }
 
 struct idiagnl_meminfo *idiagnl_msg_get_meminfo(const struct idiagnl_msg *msg)
@@ -328,14 +387,14 @@
 	return msg->idiag_meminfo;
 }
 
-void idiagnl_msg_set_meminfo(struct idiagnl_msg *msg, struct idiagnl_meminfo
-		*minfo)
+void idiagnl_msg_set_meminfo(struct idiagnl_msg *msg, struct idiagnl_meminfo *minfo)
 {
 	if (msg->idiag_meminfo)
 		idiagnl_meminfo_put(msg->idiag_meminfo);
 
 	idiagnl_meminfo_get(minfo);
 	msg->idiag_meminfo = minfo;
+	msg->ce_mask |= IDIAGNL_ATTR_MEMINFO;
 }
 
 struct idiagnl_vegasinfo *idiagnl_msg_get_vegasinfo(const struct idiagnl_msg *msg)
@@ -343,14 +402,14 @@
 	return msg->idiag_vegasinfo;
 }
 
-void idiagnl_msg_set_vegasinfo(struct idiagnl_msg *msg, struct idiagnl_vegasinfo
-		*vinfo)
+void idiagnl_msg_set_vegasinfo(struct idiagnl_msg *msg, struct idiagnl_vegasinfo *vinfo)
 {
 	if (msg->idiag_vegasinfo)
 		idiagnl_vegasinfo_put(msg->idiag_vegasinfo);
 
 	idiagnl_vegasinfo_get(vinfo);
 	msg->idiag_vegasinfo = vinfo;
+	msg->ce_mask |= IDIAGNL_ATTR_VEGASINFO;
 }
 
 struct tcp_info idiagnl_msg_get_tcpinfo(const struct idiagnl_msg *msg)
@@ -361,6 +420,7 @@
 void idiagnl_msg_set_tcpinfo(struct idiagnl_msg *msg, struct tcp_info *tinfo)
 {
 	memcpy(&msg->idiag_tcpinfo, tinfo, sizeof(struct tcp_info));
+	msg->ce_mask |= IDIAGNL_ATTR_TCPINFO;
 }
 
 /** @} */
@@ -409,7 +469,7 @@
 
 	nl_dump(p, "tos: 0x%x\n", msg->idiag_tos);
 	nl_dump(p, "traffic class: %d\n", msg->idiag_tclass);
-	nl_dump(p, "congestion algorithm: %s\n", msg->idiag_cong);
+	nl_dump(p, "congestion algorithm: %s\n", msg->idiag_cong ? msg->idiag_cong : "");
 }
 
 static void idiag_msg_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
@@ -526,27 +586,29 @@
 		nl_dump(p, "]\n");
 	}
 
-	nl_dump(p, "skmeminfo:  [\n");
-	nl_dump(p, "\trmem alloc: %d\n",
-			msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_RMEM_ALLOC]);
-	nl_dump(p, "\trcv buf: %s\n",
-			nl_size2str(msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_RCVBUF],
-				buf, sizeof(buf)));
-	nl_dump(p, "\twmem alloc: %d\n",
-			msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_WMEM_ALLOC]);
-	nl_dump(p, "\tsnd buf: %s\n",
-			nl_size2str(msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_SNDBUF],
-				buf, sizeof(buf)));
-	nl_dump(p, "\tfwd alloc: %d\n",
-			msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_FWD_ALLOC]);
-	nl_dump(p, "\twmem queued: %s\n",
-			nl_size2str(msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_WMEM_QUEUED],
-				buf, sizeof(buf)));
-	nl_dump(p, "\topt mem: %d\n",
-			msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_OPTMEM]);
-	nl_dump(p, "\tbacklog: %d\n",
-			msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_BACKLOG]);
-	nl_dump(p, "]\n\n");
+	if (msg->ce_mask & IDIAGNL_ATTR_MEMINFO) {
+		nl_dump(p, "skmeminfo:  [\n");
+		nl_dump(p, "\trmem alloc: %d\n",
+				msg->idiag_skmeminfo[SK_MEMINFO_RMEM_ALLOC]);
+		nl_dump(p, "\trcv buf: %s\n",
+				nl_size2str(msg->idiag_skmeminfo[SK_MEMINFO_RCVBUF],
+					buf, sizeof(buf)));
+		nl_dump(p, "\twmem alloc: %d\n",
+				msg->idiag_skmeminfo[SK_MEMINFO_WMEM_ALLOC]);
+		nl_dump(p, "\tsnd buf: %s\n",
+				nl_size2str(msg->idiag_skmeminfo[SK_MEMINFO_SNDBUF],
+					buf, sizeof(buf)));
+		nl_dump(p, "\tfwd alloc: %d\n",
+				msg->idiag_skmeminfo[SK_MEMINFO_FWD_ALLOC]);
+		nl_dump(p, "\twmem queued: %s\n",
+				nl_size2str(msg->idiag_skmeminfo[SK_MEMINFO_WMEM_QUEUED],
+					buf, sizeof(buf)));
+		nl_dump(p, "\topt mem: %d\n",
+				msg->idiag_skmeminfo[SK_MEMINFO_OPTMEM]);
+		nl_dump(p, "\tbacklog: %d\n",
+				msg->idiag_skmeminfo[SK_MEMINFO_BACKLOG]);
+		nl_dump(p, "]\n\n");
+	}
 }
 
 static void idiagnl_msg_free(struct nl_object *a)
@@ -567,26 +629,60 @@
 	struct idiagnl_msg *dst = (struct idiagnl_msg *) _dst;
 	struct idiagnl_msg *src = (struct idiagnl_msg *) _src;
 
-	if (src->idiag_src)
+	dst->idiag_cong = NULL;
+	dst->idiag_src = NULL;
+	dst->idiag_dst = NULL;
+	dst->idiag_meminfo = NULL;
+	dst->idiag_vegasinfo = NULL;
+	dst->ce_mask &= ~(IDIAGNL_ATTR_CONG |
+	                  IDIAGNL_ATTR_SRC |
+	                  IDIAGNL_ATTR_DST |
+	                  IDIAGNL_ATTR_MEMINFO |
+	                  IDIAGNL_ATTR_VEGASINFO);
+
+	if (src->idiag_cong) {
+		if (!(dst->idiag_cong = strdup(src->idiag_cong)))
+			return -NLE_NOMEM;
+		dst->ce_mask |= IDIAGNL_ATTR_CONG;
+	}
+
+	if (src->idiag_src) {
 		if (!(dst->idiag_src = nl_addr_clone(src->idiag_src)))
 			return -NLE_NOMEM;
+		dst->ce_mask |= IDIAGNL_ATTR_SRC;
+	}
 
-	if (src->idiag_dst)
+	if (src->idiag_dst) {
 		if (!(dst->idiag_dst = nl_addr_clone(src->idiag_dst)))
 			return -NLE_NOMEM;
+		dst->ce_mask |= IDIAGNL_ATTR_DST;
+	}
+
+	if (src->idiag_meminfo) {
+		if (!(dst->idiag_meminfo = (struct idiagnl_meminfo *) nl_object_clone((struct nl_object *) src->idiag_meminfo)))
+			return -NLE_NOMEM;
+		dst->ce_mask |= IDIAGNL_ATTR_MEMINFO;
+	}
+
+	if (src->idiag_vegasinfo) {
+		if (!(dst->idiag_vegasinfo = (struct idiagnl_vegasinfo *) nl_object_clone((struct nl_object *) src->idiag_vegasinfo)))
+			return -NLE_NOMEM;
+		dst->ce_mask |= IDIAGNL_ATTR_VEGASINFO;
+	}
 
 	return 0;
 }
 
-static struct nla_policy ext_policy[IDIAG_ATTR_MAX] = {
-	[IDIAG_ATTR_MEMINFO]    = { .minlen = sizeof(struct inet_diag_meminfo) },
-	[IDIAG_ATTR_INFO]       = { .minlen = sizeof(struct tcp_info)	},
-	[IDIAG_ATTR_VEGASINFO]  = { .minlen = sizeof(struct tcpvegas_info) },
-	[IDIAG_ATTR_CONG]       = { .type = NLA_STRING },
-	[IDIAG_ATTR_TOS]        = { .type = NLA_U8 },
-	[IDIAG_ATTR_TCLASS]     = { .type = NLA_U8 },
-	[IDIAG_ATTR_SKMEMINFO]  = { .minlen = (sizeof(uint32_t) * IDIAG_SK_MEMINFO_VARS)  },
-	[IDIAG_ATTR_SHUTDOWN]   = { .type = NLA_U8 },
+static struct nla_policy ext_policy[INET_DIAG_MAX+1] = {
+	[INET_DIAG_MEMINFO]    = { .minlen = sizeof(struct inet_diag_meminfo) },
+	[INET_DIAG_INFO]       = { .minlen = sizeof(struct tcp_info)	},
+	[INET_DIAG_VEGASINFO]  = { .minlen = sizeof(struct tcpvegas_info) },
+	[INET_DIAG_CONG]       = { .type = NLA_STRING },
+	[INET_DIAG_TOS]        = { .type = NLA_U8 },
+	[INET_DIAG_TCLASS]     = { .type = NLA_U8 },
+	/* Older kernel doesn't have SK_MEMINFO_BACKLOG */
+	[INET_DIAG_SKMEMINFO]  = { .minlen = (sizeof(uint32_t) * (SK_MEMINFO_OPTMEM + 1)) },
+	[INET_DIAG_SHUTDOWN]   = { .type = NLA_U8 },
 };
 
 int idiagnl_msg_parse(struct nlmsghdr *nlh, struct idiagnl_msg **result)
@@ -594,14 +690,14 @@
 	struct idiagnl_msg *msg = NULL;
 	struct inet_diag_msg *raw_msg = NULL;
 	struct nl_addr *src = NULL, *dst = NULL;
-	struct nlattr *tb[IDIAG_ATTR_MAX];
+	struct nlattr *tb[INET_DIAG_MAX+1];
 	int err = 0;
 
 	msg = idiagnl_msg_alloc();
 	if (!msg)
 		goto errout_nomem;
 
-	err = nlmsg_parse(nlh, sizeof(struct inet_diag_msg), tb, IDIAG_ATTR_MAX,
+	err = nlmsg_parse(nlh, sizeof(struct inet_diag_msg), tb, INET_DIAG_MAX,
 			ext_policy);
 	if (err < 0)
 		goto errout;
@@ -620,6 +716,19 @@
 	msg->idiag_dport = raw_msg->id.idiag_dport;
 	msg->idiag_ifindex = raw_msg->id.idiag_if;
 
+	msg->ce_mask = (IDIAGNL_ATTR_FAMILY |
+	                IDIAGNL_ATTR_STATE |
+	                IDIAGNL_ATTR_TIMER |
+	                IDIAGNL_ATTR_RETRANS |
+	                IDIAGNL_ATTR_EXPIRES |
+	                IDIAGNL_ATTR_RQUEUE |
+	                IDIAGNL_ATTR_WQUEUE |
+	                IDIAGNL_ATTR_UID |
+	                IDIAGNL_ATTR_INODE |
+	                IDIAGNL_ATTR_SPORT |
+	                IDIAGNL_ATTR_DPORT |
+	                IDIAGNL_ATTR_IFINDEX);
+
 	dst = nl_addr_build(raw_msg->idiag_family, raw_msg->id.idiag_dst,
 			sizeof(raw_msg->id.idiag_dst));
 	if (!dst)
@@ -642,23 +751,33 @@
 
 	nl_addr_put(src);
 
-	if (tb[IDIAG_ATTR_TOS])
-		msg->idiag_tos = nla_get_u8(tb[IDIAG_ATTR_TOS]);
+	if (tb[INET_DIAG_TOS]) {
+		msg->idiag_tos = nla_get_u8(tb[INET_DIAG_TOS]);
+		msg->ce_mask |= IDIAGNL_ATTR_TOS;
+	}
 
-	if (tb[IDIAG_ATTR_TCLASS])
-		msg->idiag_tclass = nla_get_u8(tb[IDIAG_ATTR_TCLASS]);
+	if (tb[INET_DIAG_TCLASS]) {
+		msg->idiag_tclass = nla_get_u8(tb[INET_DIAG_TCLASS]);
+		msg->ce_mask |= IDIAGNL_ATTR_TCLASS;
+	}
 
-	if (tb[IDIAG_ATTR_SHUTDOWN])
-		msg->idiag_shutdown = nla_get_u8(tb[IDIAG_ATTR_SHUTDOWN]);
+	if (tb[INET_DIAG_SHUTDOWN]) {
+		msg->idiag_shutdown = nla_get_u8(tb[INET_DIAG_SHUTDOWN]);
+		msg->ce_mask |= IDIAGNL_ATTR_SHUTDOWN;
+	}
 
-	if (tb[IDIAG_ATTR_CONG])
-		msg->idiag_cong = nla_strdup(tb[IDIAG_ATTR_CONG]);
+	if (tb[INET_DIAG_CONG]) {
+		msg->idiag_cong = nla_strdup(tb[INET_DIAG_CONG]);
+		msg->ce_mask |= IDIAGNL_ATTR_CONG;
+	}
 
-	if (tb[IDIAG_ATTR_INFO])
-		nla_memcpy(&msg->idiag_tcpinfo, tb[IDIAG_ATTR_INFO],
+	if (tb[INET_DIAG_INFO]) {
+		nla_memcpy(&msg->idiag_tcpinfo, tb[INET_DIAG_INFO],
 				sizeof(msg->idiag_tcpinfo));
+		msg->ce_mask |= IDIAGNL_ATTR_TCPINFO;
+	}
 
-	if (tb[IDIAG_ATTR_MEMINFO]) {
+	if (tb[INET_DIAG_MEMINFO]) {
 		struct idiagnl_meminfo *minfo = idiagnl_meminfo_alloc();
 		struct inet_diag_meminfo *raw_minfo = NULL;
 
@@ -666,7 +785,7 @@
 			goto errout_nomem;
 
 		raw_minfo = (struct inet_diag_meminfo *)
-			nla_data(tb[IDIAG_ATTR_MEMINFO]);
+			nla_data(tb[INET_DIAG_MEMINFO]);
 
 		idiagnl_meminfo_set_rmem(minfo, raw_minfo->idiag_rmem);
 		idiagnl_meminfo_set_wmem(minfo, raw_minfo->idiag_wmem);
@@ -674,9 +793,10 @@
 		idiagnl_meminfo_set_tmem(minfo, raw_minfo->idiag_tmem);
 
 		msg->idiag_meminfo = minfo;
+		msg->ce_mask |= IDIAGNL_ATTR_MEMINFO;
 	}
 
-	if (tb[IDIAG_ATTR_VEGASINFO]) {
+	if (tb[INET_DIAG_VEGASINFO]) {
 		struct idiagnl_vegasinfo *vinfo = idiagnl_vegasinfo_alloc();
 		struct tcpvegas_info *raw_vinfo = NULL;
 
@@ -684,7 +804,7 @@
 			goto errout_nomem;
 
 		raw_vinfo = (struct tcpvegas_info *)
-			nla_data(tb[IDIAG_ATTR_VEGASINFO]);
+			nla_data(tb[INET_DIAG_VEGASINFO]);
 
 		idiagnl_vegasinfo_set_enabled(vinfo, raw_vinfo->tcpv_enabled);
 		idiagnl_vegasinfo_set_rttcnt(vinfo, raw_vinfo->tcpv_rttcnt);
@@ -692,11 +812,14 @@
 		idiagnl_vegasinfo_set_minrtt(vinfo, raw_vinfo->tcpv_minrtt);
 
 		msg->idiag_vegasinfo = vinfo;
+		msg->ce_mask |= IDIAGNL_ATTR_VEGASINFO;
 	}
 
-	if (tb[IDIAG_ATTR_SKMEMINFO])
-		nla_memcpy(&msg->idiag_skmeminfo, tb[IDIAG_ATTR_SKMEMINFO],
+	if (tb[INET_DIAG_SKMEMINFO]) {
+		nla_memcpy(&msg->idiag_skmeminfo, tb[INET_DIAG_SKMEMINFO],
 				sizeof(msg->idiag_skmeminfo));
+		msg->ce_mask |= IDIAGNL_ATTR_SKMEMINFO;
+	}
 
 	*result = msg;
 	return 0;
@@ -710,6 +833,108 @@
 	goto errout;
 }
 
+static const struct trans_tbl idiagnl_attrs[] = {
+	__ADD(IDIAGNL_ATTR_FAMILY, family),
+	__ADD(IDIAGNL_ATTR_STATE, state),
+	__ADD(IDIAGNL_ATTR_TIMER, timer),
+	__ADD(IDIAGNL_ATTR_RETRANS, retrans),
+	__ADD(IDIAGNL_ATTR_SPORT, sport),
+	__ADD(IDIAGNL_ATTR_DPORT, dport),
+	__ADD(IDIAGNL_ATTR_SRC, src),
+	__ADD(IDIAGNL_ATTR_DST, dst),
+	__ADD(IDIAGNL_ATTR_IFINDEX, ifindex),
+	__ADD(IDIAGNL_ATTR_EXPIRES, expires),
+	__ADD(IDIAGNL_ATTR_RQUEUE, rqueue),
+	__ADD(IDIAGNL_ATTR_WQUEUE, wqueue),
+	__ADD(IDIAGNL_ATTR_UID, uid),
+	__ADD(IDIAGNL_ATTR_INODE, inode),
+	__ADD(IDIAGNL_ATTR_TOS, tos),
+	__ADD(IDIAGNL_ATTR_TCLASS, tclass),
+	__ADD(IDIAGNL_ATTR_SHUTDOWN, shutdown),
+	__ADD(IDIAGNL_ATTR_CONG, cong),
+	__ADD(IDIAGNL_ATTR_MEMINFO, meminfo),
+	__ADD(IDIAGNL_ATTR_VEGASINFO, vegasinfo),
+	__ADD(IDIAGNL_ATTR_TCPINFO, tcpinfo),
+	__ADD(IDIAGNL_ATTR_SKMEMINFO, skmeminfo),
+};
+
+static char *_idiagnl_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, idiagnl_attrs,
+	                   ARRAY_SIZE(idiagnl_attrs));
+}
+
+static uint64_t idiagnl_compare(struct nl_object *_a, struct nl_object *_b,
+                                uint64_t attrs, int flags)
+{
+	struct idiagnl_msg *a = (struct idiagnl_msg *) _a;
+	struct idiagnl_msg *b = (struct idiagnl_msg *) _b;
+	uint64_t diff = 0;
+
+#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, IDIAGNL_ATTR_##ATTR, a, b, EXPR)
+	diff |= _DIFF(FAMILY,    a->idiag_family != b->idiag_family);
+	diff |= _DIFF(STATE,     a->idiag_state != b->idiag_state);
+	diff |= _DIFF(TIMER,     a->idiag_timer != b->idiag_timer);
+	diff |= _DIFF(RETRANS,   a->idiag_retrans != b->idiag_retrans);
+	diff |= _DIFF(SPORT,     a->idiag_sport != b->idiag_sport);
+	diff |= _DIFF(DPORT,     a->idiag_dport != b->idiag_dport);
+	diff |= _DIFF(SRC,       nl_addr_cmp (a->idiag_src, b->idiag_src));
+	diff |= _DIFF(DST,       nl_addr_cmp (a->idiag_dst, b->idiag_dst));
+	diff |= _DIFF(IFINDEX,   a->idiag_ifindex != b->idiag_ifindex);
+	diff |= _DIFF(EXPIRES,   a->idiag_expires != b->idiag_expires);
+	diff |= _DIFF(RQUEUE,    a->idiag_rqueue != b->idiag_rqueue);
+	diff |= _DIFF(WQUEUE,    a->idiag_wqueue != b->idiag_wqueue);
+	diff |= _DIFF(UID,       a->idiag_uid != b->idiag_uid);
+	diff |= _DIFF(INODE,     a->idiag_inode != b->idiag_inode);
+	diff |= _DIFF(TOS,       a->idiag_tos != b->idiag_tos);
+	diff |= _DIFF(TCLASS,    a->idiag_tclass != b->idiag_tclass);
+	diff |= _DIFF(SHUTDOWN,  a->idiag_shutdown != b->idiag_shutdown);
+	diff |= _DIFF(CONG,      strcmp(a->idiag_cong, b->idiag_cong));
+	diff |= _DIFF(MEMINFO,   nl_object_diff((struct nl_object *) a->idiag_meminfo, (struct nl_object *) b->idiag_meminfo));
+	diff |= _DIFF(VEGASINFO, nl_object_diff((struct nl_object *) a->idiag_vegasinfo, (struct nl_object *) b->idiag_vegasinfo));
+	diff |= _DIFF(TCPINFO,   memcmp(&a->idiag_tcpinfo, &b->idiag_tcpinfo, sizeof(a->idiag_tcpinfo)));
+	diff |= _DIFF(SKMEMINFO, memcmp(a->idiag_skmeminfo, b->idiag_skmeminfo, sizeof(a->idiag_skmeminfo)));
+#undef _DIFF
+	return diff;
+}
+
+static void idiagnl_keygen(struct nl_object *obj, uint32_t *hashkey,
+        uint32_t table_sz)
+{
+	struct idiagnl_msg *msg = (struct idiagnl_msg *)obj;
+	unsigned int key_sz;
+	struct idiagnl_hash_key {
+		uint8_t	family;
+		uint32_t src_hash;
+		uint32_t dst_hash;
+		uint16_t sport;
+		uint16_t dport;
+	} __attribute__((packed)) key;
+
+	key_sz = sizeof(key);
+	key.family = msg->idiag_family;
+	key.src_hash = 0;
+	key.dst_hash = 0;
+	key.sport = msg->idiag_sport;
+	key.dport = msg->idiag_dport;
+
+	if (msg->idiag_src) {
+		key.src_hash = nl_hash (nl_addr_get_binary_addr(msg->idiag_src),
+		                        nl_addr_get_len(msg->idiag_src), 0);
+	}
+	if (msg->idiag_dst) {
+		key.dst_hash = nl_hash (nl_addr_get_binary_addr(msg->idiag_dst),
+		                        nl_addr_get_len(msg->idiag_dst), 0);
+	}
+
+	*hashkey = nl_hash(&key, key_sz, 0) % table_sz;
+
+	NL_DBG(5, "idiagnl %p key (fam %d src_hash %d dst_hash %d sport %d dport %d) keysz %d, hash 0x%x\n",
+	       msg, key.family, key.src_hash, key.dst_hash, key.sport, key.dport, key_sz, *hashkey);
+
+	return;
+}
+
 /** @cond SKIP */
 struct nl_object_ops idiagnl_msg_obj_ops = {
 	.oo_name			 = "idiag/idiag_msg",
@@ -721,8 +946,14 @@
 		[NL_DUMP_DETAILS]	 = idiag_msg_dump_details,
 		[NL_DUMP_STATS]		 = idiag_msg_dump_stats,
 	},
-	.oo_attrs2str			= idiagnl_attrs2str,
-	.oo_id_attrs			= (IDIAG_ATTR_INFO)
+	.oo_compare			= idiagnl_compare,
+	.oo_keygen			= idiagnl_keygen,
+	.oo_attrs2str			= _idiagnl_attrs2str,
+	.oo_id_attrs                    = (IDIAGNL_ATTR_FAMILY |
+	                                   IDIAGNL_ATTR_SRC |
+	                                   IDIAGNL_ATTR_DST |
+	                                   IDIAGNL_ATTR_SPORT |
+	                                   IDIAGNL_ATTR_DPORT),
 };
 /** @endcond */
 
diff --git a/lib/idiag/idiag_req_obj.c b/lib/idiag/idiag_req_obj.c
index d9dab8e..c825e40 100644
--- a/lib/idiag/idiag_req_obj.c
+++ b/lib/idiag/idiag_req_obj.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/idiag/idiagnl_req_obj.c Inet Diag Request Object
  *
@@ -68,7 +69,7 @@
 
 void idiagnl_req_set_ifindex(struct idiagnl_req *req, uint32_t ifindex)
 {
-	req->idiag_states = ifindex;
+	req->idiag_ifindex = ifindex;
 }
 
 uint32_t idiagnl_req_get_states(const struct idiagnl_req *req)
diff --git a/lib/idiag/idiag_vegasinfo_obj.c b/lib/idiag/idiag_vegasinfo_obj.c
index 5279e83..9a8f993 100644
--- a/lib/idiag/idiag_vegasinfo_obj.c
+++ b/lib/idiag/idiag_vegasinfo_obj.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/idiag/idiagnl_vegasinfo_obj.c Inet Diag TCP Vegas Info Object
  *
@@ -83,22 +84,25 @@
 }
 /** @} */
 
-static int idiagnl_vegasinfo_clone(struct nl_object *_dst,
-                                   struct nl_object *_src)
+/** @cond SKIP */
+static uint64_t idiagnl_vegasinfo_compare(struct nl_object *_a, struct nl_object *_b,
+                                          uint64_t attrs, int flags)
 {
-	struct idiagnl_vegasinfo *dst = (struct idiagnl_vegasinfo *) _dst;
-	struct idiagnl_vegasinfo *src = (struct idiagnl_vegasinfo *) _src;
+	struct idiagnl_vegasinfo *a = (struct idiagnl_vegasinfo *) _a;
+	struct idiagnl_vegasinfo *b = (struct idiagnl_vegasinfo *) _b;
 
-	memcpy(dst, src, sizeof(struct idiagnl_vegasinfo));
-
-	return 0;
+	/* vegasinfo is a very simple object. It has no attribe flags (ce_mask),
+	 * hence compare just returns 0 or 1, not a bit mask of attributes. */
+	return a->tcpv_enabled != b->tcpv_enabled ||
+	       a->tcpv_rttcnt != b->tcpv_rttcnt ||
+	       a->tcpv_rtt != b->tcpv_rtt ||
+	       a->tcpv_minrtt != b->tcpv_minrtt;
 }
 
-/** @cond SKIP */
 struct nl_object_ops idiagnl_vegasinfo_obj_ops = {
 	.oo_name	= "idiag/idiag_vegasinfo",
 	.oo_size	= sizeof(struct idiagnl_vegasinfo),
-	.oo_clone	= idiagnl_vegasinfo_clone,
+	.oo_compare     = idiagnl_vegasinfo_compare,
 };
 /** @endcond */
 /** @} */
diff --git a/lib/mpls.c b/lib/mpls.c
new file mode 100644
index 0000000..6d0e493
--- /dev/null
+++ b/lib/mpls.c
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * Adapted from mpls_ntop and mpls_pton copied from iproute2,
+ * lib/mpls_ntop.c and lib/mpls_pton.c
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <netlink/netlink-compat.h>
+#include <netlink-private/route/mpls.h>
+#include <linux-private/linux/mpls.h>
+
+static const char *mpls_ntop1(const struct mpls_label *addr,
+			      char *buf, size_t buflen)
+{
+	size_t destlen = buflen;
+	char *dest = buf;
+	int count = 0;
+
+	while (1) {
+		uint32_t entry = ntohl(addr[count++].entry);
+		uint32_t label = (entry & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT;
+		int len = snprintf(dest, destlen, "%u", label);
+
+		if (len >= destlen)
+			break;
+
+		/* Is this the end? */
+		if (entry & MPLS_LS_S_MASK)
+			return buf;
+
+		dest += len;
+		destlen -= len;
+		if (destlen) {
+			*dest = '/';
+			dest++;
+			destlen--;
+		}
+	}
+	errno = E2BIG;
+
+	return NULL;
+}
+
+const char *mpls_ntop(int af, const void *addr, char *buf, size_t buflen)
+{
+	switch(af) {
+	case AF_MPLS:
+		errno = 0;
+		return mpls_ntop1((struct mpls_label *)addr, buf, buflen);
+	}
+
+	errno = EINVAL;
+	return NULL;
+}
+
+static int mpls_pton1(const char *name, struct mpls_label *addr,
+		      unsigned int maxlabels)
+{
+	char *endp;
+	unsigned count;
+
+	for (count = 0; count < maxlabels; count++) {
+		unsigned long label;
+
+		label = strtoul(name, &endp, 0);
+		/* Fail when the label value is out or range */
+		if (label >= (1 << 20))
+			return 0;
+
+		if (endp == name) /* no digits */
+			return 0;
+
+		addr->entry = htonl(label << MPLS_LS_LABEL_SHIFT);
+		if (*endp == '\0') {
+			addr->entry |= htonl(1 << MPLS_LS_S_SHIFT);
+			return (count + 1) * sizeof(struct mpls_label);
+		}
+
+		/* Bad character in the address */
+		if (*endp != '/')
+			return 0;
+
+		name = endp + 1;
+		addr += 1;
+	}
+
+	/* The address was too long */
+	return 0;
+}
+
+int mpls_pton(int af, const char *src, void *addr, size_t alen)
+{
+	unsigned int maxlabels = alen / sizeof(struct mpls_label);
+	int err;
+
+	switch(af) {
+	case AF_MPLS:
+		errno = 0;
+		err = mpls_pton1(src, (struct mpls_label *)addr, maxlabels);
+		break;
+	default:
+		errno = EAFNOSUPPORT;
+		err = -1;
+	}
+
+	return err;
+}
diff --git a/lib/msg.c b/lib/msg.c
index bcf1aa8..c08b3a4 100644
--- a/lib/msg.c
+++ b/lib/msg.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/msg.c		Netlink Messages Interface
  *
@@ -27,6 +28,7 @@
  */
 
 #include <netlink-private/netlink.h>
+#include <netlink-private/utils.h>
 #include <netlink/netlink.h>
 #include <netlink/utils.h>
 #include <netlink/cache.h>
@@ -211,7 +213,7 @@
  * See nla_parse()
  */
 int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
-		int maxtype, struct nla_policy *policy)
+		int maxtype, const struct nla_policy *policy)
 {
 	if (!nlmsg_valid_hdr(nlh, hdrlen))
 		return -NLE_MSG_TOOSHORT;
@@ -242,7 +244,7 @@
  * @arg policy		validation policy
  */
 int nlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype,
-		   struct nla_policy *policy)
+		   const struct nla_policy *policy)
 {
 	if (!nlmsg_valid_hdr(nlh, hdrlen))
 		return -NLE_MSG_TOOSHORT;
@@ -349,6 +351,8 @@
 	struct nlmsghdr nlh = {
 		.nlmsg_type = nlmsgtype,
 		.nlmsg_flags = flags,
+		.nlmsg_seq = NL_AUTO_SEQ,
+		.nlmsg_pid = NL_AUTO_PID,
 	};
 
 	msg = nlmsg_inherit(&nlh);
@@ -406,10 +410,13 @@
  */
 void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad)
 {
-	void *buf = n->nm_nlh;
+	char *buf = (char *) n->nm_nlh;
 	size_t nlmsg_len = n->nm_nlh->nlmsg_len;
 	size_t tlen;
 
+	if (len > n->nm_size)
+		return NULL;
+
 	tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len;
 
 	if ((tlen + nlmsg_len) > n->nm_size)
@@ -637,10 +644,10 @@
  */
 
 static const struct trans_tbl nl_msgtypes[] = {
-	__ADD(NLMSG_NOOP,NOOP)
-	__ADD(NLMSG_ERROR,ERROR)
-	__ADD(NLMSG_DONE,DONE)
-	__ADD(NLMSG_OVERRUN,OVERRUN)
+	__ADD(NLMSG_NOOP,NOOP),
+	__ADD(NLMSG_ERROR,ERROR),
+	__ADD(NLMSG_DONE,DONE),
+	__ADD(NLMSG_OVERRUN,OVERRUN),
 };
 
 char *nl_nlmsgtype2str(int type, char *buf, size_t size)
@@ -831,7 +838,7 @@
 static void *print_genl_msg(struct nl_msg *msg, FILE *ofd, struct nlmsghdr *hdr,
 			    struct nl_cache_ops *ops, int *payloadlen)
 {
-	void *data = nlmsg_data(hdr);
+	char *data = nlmsg_data(hdr);
 
 	if (*payloadlen < GENL_HDRLEN)
 		return data;
@@ -894,7 +901,7 @@
 			prefix_line(ofd, prefix);
 			fprintf(ofd, "  [PADDING] %d octets\n",
 				padlen);
-			dump_hex(ofd, nla_data(nla) + alen,
+			dump_hex(ofd, (char *) nla_data(nla) + alen,
 				 padlen, prefix);
 		}
 	}
@@ -913,11 +920,10 @@
 	fprintf(ofd, "  [ERRORMSG] %zu octets\n", sizeof(*err));
 
 	if (nlmsg_len(hdr) >= sizeof(*err)) {
-		char buf[256];
 		struct nl_msg *errmsg;
 
 		fprintf(ofd, "    .error = %d \"%s\"\n", err->error,
-			strerror_r(-err->error, buf, sizeof(buf)));
+			nl_strerror_l(-err->error));
 		fprintf(ofd, "  [ORIGINAL MESSAGE] %zu octets\n", sizeof(*hdr));
 
 		errmsg = nlmsg_inherit(&err->msg);
diff --git a/lib/netfilter/ct.c b/lib/netfilter/ct.c
index 36a83db..98aaafc 100644
--- a/lib/netfilter/ct.c
+++ b/lib/netfilter/ct.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/netfilter/ct.c	Conntrack
  *
@@ -27,28 +28,10 @@
 #include <netlink/attr.h>
 #include <netlink/netfilter/nfnl.h>
 #include <netlink/netfilter/ct.h>
+#include <netlink-private/utils.h>
 
 static struct nl_cache_ops nfnl_ct_ops;
 
-#if __BYTE_ORDER == __BIG_ENDIAN
-static uint64_t ntohll(uint64_t x)
-{
-	return x;
-}
-static uint64_t htonll(uint64_t x)
-{
-	return x;
-}
-#elif __BYTE_ORDER == __LITTLE_ENDIAN
-static uint64_t ntohll(uint64_t x)
-{
-	return bswap_64(x);
-}
-static uint64_t htonll(uint64_t x)
-{
-	return bswap_64(x);
-}
-#endif
 
 static struct nla_policy ct_policy[CTA_MAX+1] = {
 	[CTA_TUPLE_ORIG]	= { .type = NLA_NESTED },
@@ -122,7 +105,7 @@
 	struct nl_addr *addr;
 	int err;
 
-        err = nla_parse_nested(tb, CTA_IP_MAX, attr, ct_ip_policy);
+	err = nla_parse_nested(tb, CTA_IP_MAX, attr, ct_ip_policy);
 	if (err < 0)
 		goto errout;
 
@@ -421,6 +404,13 @@
 	return err;
 }
 
+/**
+ * Send nfnl ct dump request
+ * @arg sk    Netlink socket.
+ *
+ * @return 0 on success or a negative error code. Due to a bug, this function
+ * returns the number of bytes sent. Treat any non-negative number as success.
+ */
 int nfnl_ct_dump_request(struct nl_sock *sk)
 {
 	return nfnl_send_simple(sk, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET,
diff --git a/lib/netfilter/ct_obj.c b/lib/netfilter/ct_obj.c
index 61b6a31..08aa945 100644
--- a/lib/netfilter/ct_obj.c
+++ b/lib/netfilter/ct_obj.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/netfilter/ct_obj.c	Conntrack Object
  *
@@ -297,12 +298,12 @@
 	}
 }
 
-static int ct_compare(struct nl_object *_a, struct nl_object *_b,
-			uint32_t attrs, int flags)
+static uint64_t ct_compare(struct nl_object *_a, struct nl_object *_b,
+			   uint64_t attrs, int flags)
 {
 	struct nfnl_ct *a = (struct nfnl_ct *) _a;
 	struct nfnl_ct *b = (struct nfnl_ct *) _b;
-	int diff = 0;
+	uint64_t diff = 0;
 
 #define CT_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, CT_ATTR_##ATTR, a, b, EXPR)
 #define CT_DIFF_VAL(ATTR, FIELD) CT_DIFF(ATTR, a->FIELD != b->FIELD)
@@ -351,32 +352,32 @@
 }
 
 static const struct trans_tbl ct_attrs[] = {
-	__ADD(CT_ATTR_FAMILY,		family)
-	__ADD(CT_ATTR_PROTO,		proto)
-	__ADD(CT_ATTR_TCP_STATE,	tcpstate)
-	__ADD(CT_ATTR_STATUS,		status)
-	__ADD(CT_ATTR_TIMEOUT,		timeout)
-	__ADD(CT_ATTR_MARK,		mark)
-	__ADD(CT_ATTR_USE,		use)
-	__ADD(CT_ATTR_ID,		id)
-	__ADD(CT_ATTR_ORIG_SRC,		origsrc)
-	__ADD(CT_ATTR_ORIG_DST,		origdst)
-	__ADD(CT_ATTR_ORIG_SRC_PORT,	origsrcport)
-	__ADD(CT_ATTR_ORIG_DST_PORT,	origdstport)
-	__ADD(CT_ATTR_ORIG_ICMP_ID,	origicmpid)
-	__ADD(CT_ATTR_ORIG_ICMP_TYPE,	origicmptype)
-	__ADD(CT_ATTR_ORIG_ICMP_CODE,	origicmpcode)
-	__ADD(CT_ATTR_ORIG_PACKETS,	origpackets)
-	__ADD(CT_ATTR_ORIG_BYTES,	origbytes)
-	__ADD(CT_ATTR_REPL_SRC,		replysrc)
-	__ADD(CT_ATTR_REPL_DST,		replydst)
-	__ADD(CT_ATTR_REPL_SRC_PORT,	replysrcport)
-	__ADD(CT_ATTR_REPL_DST_PORT,	replydstport)
-	__ADD(CT_ATTR_REPL_ICMP_ID,	replyicmpid)
-	__ADD(CT_ATTR_REPL_ICMP_TYPE,	replyicmptype)
-	__ADD(CT_ATTR_REPL_ICMP_CODE,	replyicmpcode)
-	__ADD(CT_ATTR_REPL_PACKETS,	replypackets)
-	__ADD(CT_ATTR_REPL_BYTES,	replybytes)
+	__ADD(CT_ATTR_FAMILY,		family),
+	__ADD(CT_ATTR_PROTO,		proto),
+	__ADD(CT_ATTR_TCP_STATE,	tcpstate),
+	__ADD(CT_ATTR_STATUS,		status),
+	__ADD(CT_ATTR_TIMEOUT,		timeout),
+	__ADD(CT_ATTR_MARK,		mark),
+	__ADD(CT_ATTR_USE,		use),
+	__ADD(CT_ATTR_ID,		id),
+	__ADD(CT_ATTR_ORIG_SRC,		origsrc),
+	__ADD(CT_ATTR_ORIG_DST,		origdst),
+	__ADD(CT_ATTR_ORIG_SRC_PORT,	origsrcport),
+	__ADD(CT_ATTR_ORIG_DST_PORT,	origdstport),
+	__ADD(CT_ATTR_ORIG_ICMP_ID,	origicmpid),
+	__ADD(CT_ATTR_ORIG_ICMP_TYPE,	origicmptype),
+	__ADD(CT_ATTR_ORIG_ICMP_CODE,	origicmpcode),
+	__ADD(CT_ATTR_ORIG_PACKETS,	origpackets),
+	__ADD(CT_ATTR_ORIG_BYTES,	origbytes),
+	__ADD(CT_ATTR_REPL_SRC,		replysrc),
+	__ADD(CT_ATTR_REPL_DST,		replydst),
+	__ADD(CT_ATTR_REPL_SRC_PORT,	replysrcport),
+	__ADD(CT_ATTR_REPL_DST_PORT,	replydstport),
+	__ADD(CT_ATTR_REPL_ICMP_ID,	replyicmpid),
+	__ADD(CT_ATTR_REPL_ICMP_TYPE,	replyicmptype),
+	__ADD(CT_ATTR_REPL_ICMP_CODE,	replyicmpcode),
+	__ADD(CT_ATTR_REPL_PACKETS,	replypackets),
+	__ADD(CT_ATTR_REPL_BYTES,	replybytes),
 };
 
 static char *ct_attrs2str(int attrs, char *buf, size_t len)
@@ -458,16 +459,16 @@
 }
 
 static const struct trans_tbl tcp_states[] = {
-	__ADD(TCP_CONNTRACK_NONE,NONE)
-	__ADD(TCP_CONNTRACK_SYN_SENT,SYN_SENT)
-	__ADD(TCP_CONNTRACK_SYN_RECV,SYN_RECV)
-	__ADD(TCP_CONNTRACK_ESTABLISHED,ESTABLISHED)
-	__ADD(TCP_CONNTRACK_FIN_WAIT,FIN_WAIT)
-	__ADD(TCP_CONNTRACK_CLOSE_WAIT,CLOSE_WAIT)
-	__ADD(TCP_CONNTRACK_LAST_ACK,LAST_ACK)
-	__ADD(TCP_CONNTRACK_TIME_WAIT,TIME_WAIT)
-	__ADD(TCP_CONNTRACK_CLOSE,CLOSE)
-	__ADD(TCP_CONNTRACK_LISTEN,LISTEN)
+	__ADD(TCP_CONNTRACK_NONE,NONE),
+	__ADD(TCP_CONNTRACK_SYN_SENT,SYN_SENT),
+	__ADD(TCP_CONNTRACK_SYN_RECV,SYN_RECV),
+	__ADD(TCP_CONNTRACK_ESTABLISHED,ESTABLISHED),
+	__ADD(TCP_CONNTRACK_FIN_WAIT,FIN_WAIT),
+	__ADD(TCP_CONNTRACK_CLOSE_WAIT,CLOSE_WAIT),
+	__ADD(TCP_CONNTRACK_LAST_ACK,LAST_ACK),
+	__ADD(TCP_CONNTRACK_TIME_WAIT,TIME_WAIT),
+	__ADD(TCP_CONNTRACK_CLOSE,CLOSE),
+	__ADD(TCP_CONNTRACK_LISTEN,LISTEN),
 };
 
 char *nfnl_ct_tcp_state2str(uint8_t state, char *buf, size_t len)
@@ -477,7 +478,7 @@
 
 int nfnl_ct_str2tcp_state(const char *name)
 {
-        return __str2type(name, tcp_states, ARRAY_SIZE(tcp_states));
+	return __str2type(name, tcp_states, ARRAY_SIZE(tcp_states));
 }
 
 void nfnl_ct_set_status(struct nfnl_ct *ct, uint32_t status)
@@ -505,17 +506,17 @@
 }
 
 static const struct trans_tbl status_flags[] = {
-	__ADD(IPS_EXPECTED, expected)
-	__ADD(IPS_SEEN_REPLY, seen_reply)
-	__ADD(IPS_ASSURED, assured)
-	__ADD(IPS_CONFIRMED, confirmed)
-	__ADD(IPS_SRC_NAT, snat)
-	__ADD(IPS_DST_NAT, dnat)
-	__ADD(IPS_SEQ_ADJUST, seqadjust)
-	__ADD(IPS_SRC_NAT_DONE, snat_done)
-	__ADD(IPS_DST_NAT_DONE, dnat_done)
-	__ADD(IPS_DYING, dying)
-	__ADD(IPS_FIXED_TIMEOUT, fixed_timeout)
+	__ADD(IPS_EXPECTED, expected),
+	__ADD(IPS_SEEN_REPLY, seen_reply),
+	__ADD(IPS_ASSURED, assured),
+	__ADD(IPS_CONFIRMED, confirmed),
+	__ADD(IPS_SRC_NAT, snat),
+	__ADD(IPS_DST_NAT, dnat),
+	__ADD(IPS_SEQ_ADJUST, seqadjust),
+	__ADD(IPS_SRC_NAT_DONE, snat_done),
+	__ADD(IPS_DST_NAT_DONE, dnat_done),
+	__ADD(IPS_DYING, dying),
+	__ADD(IPS_FIXED_TIMEOUT, fixed_timeout),
 };
 
 char * nfnl_ct_status2str(int flags, char *buf, size_t len)
diff --git a/lib/netfilter/exp.c b/lib/netfilter/exp.c
index 9cfdd2b..d475861 100644
--- a/lib/netfilter/exp.c
+++ b/lib/netfilter/exp.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/netfilter/exp.c	Conntrack Expectation
  *
@@ -305,6 +306,13 @@
 	return err;
 }
 
+/**
+ * Send nfnl exp dump request
+ * @arg sk    Netlink socket.
+ *
+ * @return 0 on success or a negative error code. Due to a bug, this function
+ * returns the number of bytes sent. Treat any non-negative number as success.
+ */
 int nfnl_exp_dump_request(struct nl_sock *sk)
 {
 	return nfnl_send_simple(sk, NFNL_SUBSYS_CTNETLINK_EXP, IPCTNL_MSG_EXP_GET,
@@ -345,15 +353,16 @@
 	struct nlattr *tuple, *ip, *proto;
 	struct nl_addr *addr;
 	int family;
+	int type;
 
 	family = nfnl_exp_get_family(exp);
 
-	int type = exp_get_tuple_attr(cta);
+	type = exp_get_tuple_attr(cta);
 
-    if (cta == CTA_EXPECT_NAT)
-        tuple = nla_nest_start(msg, CTA_EXPECT_NAT_TUPLE);
-    else
-        tuple = nla_nest_start(msg, cta);
+	if (cta == CTA_EXPECT_NAT)
+		tuple = nla_nest_start(msg, CTA_EXPECT_NAT_TUPLE);
+	else
+		tuple = nla_nest_start(msg, cta);
 
 	if (!tuple)
 		goto nla_put_failure;
@@ -483,6 +492,8 @@
 	return 0;
 
 nla_put_failure:
+	err = -NLE_NOMEM;
+
 err_out:
 	nlmsg_free(msg);
 	return err;
diff --git a/lib/netfilter/exp_obj.c b/lib/netfilter/exp_obj.c
index 69b4dd3..8cd59ee 100644
--- a/lib/netfilter/exp_obj.c
+++ b/lib/netfilter/exp_obj.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/netfilter/exp_obj.c	Conntrack Expectation Object
  *
@@ -132,7 +133,7 @@
 		dst->exp_mask.dst = addr;
 	}
 
-    // NAT
+	// NAT
 	if (src->exp_nat.src) {
 		addr = nl_addr_clone(src->exp_nat.src);
 		if (!addr)
@@ -200,7 +201,7 @@
 		if (nfnl_exp_test_dst(exp, i))
 			tuple_dst = nfnl_exp_get_dst(exp, i);
 
-        // Don't have tests for individual ports/types/codes/ids,
+		// Don't have tests for individual ports/types/codes/ids,
 		if (nfnl_exp_test_l4protonum(exp, i)) {
 			nl_dump(p, "%s ",
 				nl_ip_proto2str(nfnl_exp_get_l4protonum(exp, i), buf, sizeof(buf)));
@@ -286,27 +287,27 @@
 	// Must return 0 for match, 1 for mismatch
 	int d = 0;
 	d = ( (a->port.src != b->port.src) ||
-		  (a->port.dst != b->port.dst) );
+	      (a->port.dst != b->port.dst) );
 
 	return d;
 }
 
 static int exp_cmp_l4proto_icmp (union nfnl_exp_protodata *a, union nfnl_exp_protodata *b) {
-    // Must return 0 for match, 1 for mismatch
+	// Must return 0 for match, 1 for mismatch
 	int d = 0;
 	d = ( (a->icmp.code != b->icmp.code) ||
-		  (a->icmp.type != b->icmp.type) ||
-		  (a->icmp.id != b->icmp.id) );
+	      (a->icmp.type != b->icmp.type) ||
+	      (a->icmp.id != b->icmp.id) );
 
 	return d;
 }
 
-static int exp_compare(struct nl_object *_a, struct nl_object *_b,
-							uint32_t attrs, int flags)
+static uint64_t exp_compare(struct nl_object *_a, struct nl_object *_b,
+			    uint64_t attrs, int flags)
 {
 	struct nfnl_exp *a = (struct nfnl_exp *) _a;
 	struct nfnl_exp *b = (struct nfnl_exp *) _b;
-	int diff = 0;
+	uint64_t diff = 0;
 
 #define EXP_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, EXP_ATTR_##ATTR, a, b, EXPR)
 #define EXP_DIFF_VAL(ATTR, FIELD) EXP_DIFF(ATTR, a->FIELD != b->FIELD)
@@ -367,35 +368,35 @@
 
 // CLI arguments?
 static const struct trans_tbl exp_attrs[] = {
-	__ADD(EXP_ATTR_FAMILY,				family)
-	__ADD(EXP_ATTR_TIMEOUT,				timeout)
-	__ADD(EXP_ATTR_ID,				id)
-	__ADD(EXP_ATTR_HELPER_NAME,			helpername)
-	__ADD(EXP_ATTR_ZONE,				zone)
-	__ADD(EXP_ATTR_CLASS,				class)
-	__ADD(EXP_ATTR_FLAGS,				flags)
-	__ADD(EXP_ATTR_FN,				function)
-	__ADD(EXP_ATTR_EXPECT_IP_SRC,			expectipsrc)
-	__ADD(EXP_ATTR_EXPECT_IP_DST,			expectipdst)
-	__ADD(EXP_ATTR_EXPECT_L4PROTO_NUM,		expectprotonum)
-	__ADD(EXP_ATTR_EXPECT_L4PROTO_PORTS,		expectports)
-	__ADD(EXP_ATTR_EXPECT_L4PROTO_ICMP,		expecticmp)
-	__ADD(EXP_ATTR_MASTER_IP_SRC,			masteripsrc)
-	__ADD(EXP_ATTR_MASTER_IP_DST,			masteripdst)
-	__ADD(EXP_ATTR_MASTER_L4PROTO_NUM,		masterprotonum)
-	__ADD(EXP_ATTR_MASTER_L4PROTO_PORTS,		masterports)
-	__ADD(EXP_ATTR_MASTER_L4PROTO_ICMP,		mastericmp)
-	__ADD(EXP_ATTR_MASK_IP_SRC,			maskipsrc)
-	__ADD(EXP_ATTR_MASK_IP_DST,			maskipdst)
-	__ADD(EXP_ATTR_MASK_L4PROTO_NUM,		maskprotonum)
-	__ADD(EXP_ATTR_MASK_L4PROTO_PORTS,		maskports)
-	__ADD(EXP_ATTR_MASK_L4PROTO_ICMP,		maskicmp)
-	__ADD(EXP_ATTR_NAT_IP_SRC,			natipsrc)
-	__ADD(EXP_ATTR_NAT_IP_DST,			natipdst)
-	__ADD(EXP_ATTR_NAT_L4PROTO_NUM,			natprotonum)
-	__ADD(EXP_ATTR_NAT_L4PROTO_PORTS,		natports)
-	__ADD(EXP_ATTR_NAT_L4PROTO_ICMP,		naticmp)
-	__ADD(EXP_ATTR_NAT_DIR,				natdir)
+	__ADD(EXP_ATTR_FAMILY,				family),
+	__ADD(EXP_ATTR_TIMEOUT,				timeout),
+	__ADD(EXP_ATTR_ID,				id),
+	__ADD(EXP_ATTR_HELPER_NAME,			helpername),
+	__ADD(EXP_ATTR_ZONE,				zone),
+	__ADD(EXP_ATTR_CLASS,				class),
+	__ADD(EXP_ATTR_FLAGS,				flags),
+	__ADD(EXP_ATTR_FN,				function),
+	__ADD(EXP_ATTR_EXPECT_IP_SRC,			expectipsrc),
+	__ADD(EXP_ATTR_EXPECT_IP_DST,			expectipdst),
+	__ADD(EXP_ATTR_EXPECT_L4PROTO_NUM,		expectprotonum),
+	__ADD(EXP_ATTR_EXPECT_L4PROTO_PORTS,		expectports),
+	__ADD(EXP_ATTR_EXPECT_L4PROTO_ICMP,		expecticmp),
+	__ADD(EXP_ATTR_MASTER_IP_SRC,			masteripsrc),
+	__ADD(EXP_ATTR_MASTER_IP_DST,			masteripdst),
+	__ADD(EXP_ATTR_MASTER_L4PROTO_NUM,		masterprotonum),
+	__ADD(EXP_ATTR_MASTER_L4PROTO_PORTS,		masterports),
+	__ADD(EXP_ATTR_MASTER_L4PROTO_ICMP,		mastericmp),
+	__ADD(EXP_ATTR_MASK_IP_SRC,			maskipsrc),
+	__ADD(EXP_ATTR_MASK_IP_DST,			maskipdst),
+	__ADD(EXP_ATTR_MASK_L4PROTO_NUM,		maskprotonum),
+	__ADD(EXP_ATTR_MASK_L4PROTO_PORTS,		maskports),
+	__ADD(EXP_ATTR_MASK_L4PROTO_ICMP,		maskicmp),
+	__ADD(EXP_ATTR_NAT_IP_SRC,			natipsrc),
+	__ADD(EXP_ATTR_NAT_IP_DST,			natipdst),
+	__ADD(EXP_ATTR_NAT_L4PROTO_NUM,			natprotonum),
+	__ADD(EXP_ATTR_NAT_L4PROTO_PORTS,		natports),
+	__ADD(EXP_ATTR_NAT_L4PROTO_ICMP,		naticmp),
+	__ADD(EXP_ATTR_NAT_DIR,				natdir),
 };
 
 static char *exp_attrs2str(int attrs, char *buf, size_t len)
@@ -467,9 +468,9 @@
 }
 
 static const struct trans_tbl flag_table[] = {
-	__ADD(IPS_EXPECTED, expected)
-	__ADD(IPS_SEEN_REPLY, seen_reply)
-	__ADD(IPS_ASSURED, assured)
+	__ADD(IPS_EXPECTED, expected),
+	__ADD(IPS_SEEN_REPLY, seen_reply),
+	__ADD(IPS_ASSURED, assured),
 };
 
 char * nfnl_exp_flags2str(int flags, char *buf, size_t len)
diff --git a/lib/netfilter/log.c b/lib/netfilter/log.c
index 1bab9b6..1761808 100644
--- a/lib/netfilter/log.c
+++ b/lib/netfilter/log.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/netfilter/log.c	Netfilter Log
  *
diff --git a/lib/netfilter/log_msg.c b/lib/netfilter/log_msg.c
index 5ffdaf8..e1f92eb 100644
--- a/lib/netfilter/log_msg.c
+++ b/lib/netfilter/log_msg.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/netfilter/log_msg.c	Netfilter Log Message
  *
@@ -26,19 +27,7 @@
 #include <netlink/attr.h>
 #include <netlink/netfilter/nfnl.h>
 #include <netlink/netfilter/log_msg.h>
-#include <byteswap.h>
-
-#if __BYTE_ORDER == __BIG_ENDIAN
-static uint64_t ntohll(uint64_t x)
-{
-	return x;
-}
-#elif __BYTE_ORDER == __LITTLE_ENDIAN
-static uint64_t ntohll(uint64_t x)
-{
-	return bswap_64(x);
-}
-#endif
+#include <netlink-private/utils.h>
 
 static struct nla_policy log_msg_policy[NFULA_MAX+1] = {
 	[NFULA_PACKET_HDR]		= {
diff --git a/lib/netfilter/log_msg_obj.c b/lib/netfilter/log_msg_obj.c
index 57db9d4..90b7bc9 100644
--- a/lib/netfilter/log_msg_obj.c
+++ b/lib/netfilter/log_msg_obj.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/netfilter/log_msg_obj.c	Netfilter Log Object
  *
diff --git a/lib/netfilter/log_obj.c b/lib/netfilter/log_obj.c
index 2b11414..a33ef9f 100644
--- a/lib/netfilter/log_obj.c
+++ b/lib/netfilter/log_obj.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/netfilter/log_obj.c	Netfilter Log Object
  *
@@ -57,9 +58,9 @@
 }
 
 static const struct trans_tbl copy_modes[] = {
-	__ADD(NFNL_LOG_COPY_NONE,	none)
-	__ADD(NFNL_LOG_COPY_META,	meta)
-	__ADD(NFNL_LOG_COPY_PACKET,	packet)
+	__ADD(NFNL_LOG_COPY_NONE,	none),
+	__ADD(NFNL_LOG_COPY_META,	meta),
+	__ADD(NFNL_LOG_COPY_PACKET,	packet),
 };
 
 char *nfnl_log_copy_mode2str(enum nfnl_log_copy_mode copy_mode, char *buf,
@@ -69,7 +70,7 @@
 			  ARRAY_SIZE(copy_modes));
 }
 
-enum nfnl_log_copy_mode nfnl_log_str2copy_mode(const char *name)
+int nfnl_log_str2copy_mode(const char *name)
 {
 	return __str2type(name, copy_modes, ARRAY_SIZE(copy_modes));
 }
@@ -215,8 +216,8 @@
 }
 
 static const struct trans_tbl log_flags[] = {
-	__ADD(NFNL_LOG_FLAG_SEQ,	seq)
-	__ADD(NFNL_LOG_FLAG_SEQ_GLOBAL,	seq_global)
+	__ADD(NFNL_LOG_FLAG_SEQ,	seq),
+	__ADD(NFNL_LOG_FLAG_SEQ_GLOBAL,	seq_global),
 };
 
 char *nfnl_log_flags2str(unsigned int flags, char *buf, size_t len)
@@ -229,12 +230,12 @@
 	return __str2flags(name, log_flags, ARRAY_SIZE(log_flags));
 }
 
-static int nfnl_log_compare(struct nl_object *_a, struct nl_object *_b,
-			    uint32_t attrs, int flags)
+static uint64_t nfnl_log_compare(struct nl_object *_a, struct nl_object *_b,
+				 uint64_t attrs, int flags)
 {
 	struct nfnl_log *a = (struct nfnl_log *) _a;
 	struct nfnl_log *b = (struct nfnl_log *) _b;
-	int diff = 0;
+	uint64_t diff = 0;
 
 #define NFNL_LOG_DIFF(ATTR, EXPR) \
 	ATTR_DIFF(attrs, LOG_ATTR_##ATTR, a, b, EXPR)
@@ -255,12 +256,12 @@
 }
 
 static const struct trans_tbl nfnl_log_attrs[] = {
-	__ADD(LOG_ATTR_GROUP,		group)
-	__ADD(LOG_ATTR_COPY_MODE,	copy_mode)
-	__ADD(LOG_ATTR_COPY_RANGE,	copy_range)
-	__ADD(LOG_ATTR_FLUSH_TIMEOUT,	flush_timeout)
-	__ADD(LOG_ATTR_ALLOC_SIZE,	alloc_size)
-	__ADD(LOG_ATTR_QUEUE_THRESHOLD, queue_threshold)
+	__ADD(LOG_ATTR_GROUP,		group),
+	__ADD(LOG_ATTR_COPY_MODE,	copy_mode),
+	__ADD(LOG_ATTR_COPY_RANGE,	copy_range),
+	__ADD(LOG_ATTR_FLUSH_TIMEOUT,	flush_timeout),
+	__ADD(LOG_ATTR_ALLOC_SIZE,	alloc_size),
+	__ADD(LOG_ATTR_QUEUE_THRESHOLD, queue_threshold),
 };
 
 static char *nfnl_log_attrs2str(int attrs, char *buf, size_t len)
diff --git a/lib/netfilter/netfilter.c b/lib/netfilter/netfilter.c
index 0062994..dba435d 100644
--- a/lib/netfilter/netfilter.c
+++ b/lib/netfilter/netfilter.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/netfilter/netfilter.c    Netfilter Generic Functions
  *
@@ -14,12 +15,12 @@
 #include <linux/netfilter.h>
 
 static const struct trans_tbl nfnl_verdicts[] = {
-	__ADD(NF_DROP,		NF_DROP)
-	__ADD(NF_ACCEPT,	NF_ACCEPT)
-	__ADD(NF_STOLEN,	NF_STOLEN)
-	__ADD(NF_QUEUE,		NF_QUEUE)
-	__ADD(NF_REPEAT,	NF_REPEAT)
-	__ADD(NF_STOP,		NF_STOP)
+	__ADD(NF_DROP,		NF_DROP),
+	__ADD(NF_ACCEPT,	NF_ACCEPT),
+	__ADD(NF_STOLEN,	NF_STOLEN),
+	__ADD(NF_QUEUE,		NF_QUEUE),
+	__ADD(NF_REPEAT,	NF_REPEAT),
+	__ADD(NF_STOP,		NF_STOP),
 };
 
 char *nfnl_verdict2str(unsigned int verdict, char *buf, size_t len)
@@ -34,11 +35,11 @@
 }
 
 static const struct trans_tbl nfnl_inet_hooks[] = {
-	__ADD(NF_INET_PRE_ROUTING,	NF_INET_PREROUTING)
-	__ADD(NF_INET_LOCAL_IN,		NF_INET_LOCAL_IN)
-	__ADD(NF_INET_FORWARD,		NF_INET_FORWARD)
-	__ADD(NF_INET_LOCAL_OUT,	NF_INET_LOCAL_OUT)
-	__ADD(NF_INET_POST_ROUTING,	NF_INET_POST_ROUTING)
+	__ADD(NF_INET_PRE_ROUTING,	NF_INET_PREROUTING),
+	__ADD(NF_INET_LOCAL_IN,		NF_INET_LOCAL_IN),
+	__ADD(NF_INET_FORWARD,		NF_INET_FORWARD),
+	__ADD(NF_INET_LOCAL_OUT,	NF_INET_LOCAL_OUT),
+	__ADD(NF_INET_POST_ROUTING,	NF_INET_POST_ROUTING),
 };
 
 char *nfnl_inet_hook2str(unsigned int hook, char *buf, size_t len)
diff --git a/lib/netfilter/nfnl.c b/lib/netfilter/nfnl.c
index f028a85..ac502fb 100644
--- a/lib/netfilter/nfnl.c
+++ b/lib/netfilter/nfnl.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/netfilter/nfnl.c		Netfilter Netlink
  *
@@ -65,6 +66,8 @@
 #include <netlink/netlink.h>
 #include <netlink/netfilter/nfnl.h>
 
+#include <linux/netfilter/nfnetlink.h>
+
 /**
  * @name Socket Creating
  * @{
@@ -102,7 +105,8 @@
  * @arg family		nfnetlink address family
  * @arg res_id		nfnetlink resource id
  *
- * @return Newly allocated netlink message or NULL.
+ * @return 0 on success or a negative error code. Due to a bug, this function
+ * returns the number of bytes sent. Treat any non-negative number as success.
  */
 int nfnl_send_simple(struct nl_sock *sk, uint8_t subsys_id, uint8_t type,
 		     int flags, uint8_t family, uint16_t res_id)
diff --git a/lib/netfilter/queue.c b/lib/netfilter/queue.c
index 5655647..d20dee5 100644
--- a/lib/netfilter/queue.c
+++ b/lib/netfilter/queue.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/netfilter/queue.c	Netfilter Queue
  *
diff --git a/lib/netfilter/queue_msg.c b/lib/netfilter/queue_msg.c
index 3388923..68ed71e 100644
--- a/lib/netfilter/queue_msg.c
+++ b/lib/netfilter/queue_msg.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/netfilter/queue_msg.c	Netfilter Queue Messages
  *
@@ -24,22 +25,10 @@
 #include <netlink/attr.h>
 #include <netlink/netfilter/nfnl.h>
 #include <netlink/netfilter/queue_msg.h>
-#include <byteswap.h>
+#include <netlink-private/utils.h>
 
 static struct nl_cache_ops nfnl_queue_msg_ops;
 
-#if __BYTE_ORDER == __BIG_ENDIAN
-static uint64_t ntohll(uint64_t x)
-{
-	return x;
-}
-#elif __BYTE_ORDER == __LITTLE_ENDIAN
-static uint64_t ntohll(uint64_t x)
-{
-	return bswap_64(x);
-}
-#endif
-
 static struct nla_policy queue_policy[NFQA_MAX+1] = {
 	[NFQA_PACKET_HDR]		= {
 		.minlen	= sizeof(struct nfqnl_msg_packet_hdr),
diff --git a/lib/netfilter/queue_msg_obj.c b/lib/netfilter/queue_msg_obj.c
index 8085f1b..1e89cc4 100644
--- a/lib/netfilter/queue_msg_obj.c
+++ b/lib/netfilter/queue_msg_obj.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/netfilter/queue_msg_obj.c	Netfilter Queue Message Object
  *
@@ -405,12 +406,15 @@
 int nfnl_queue_msg_set_payload(struct nfnl_queue_msg *msg, uint8_t *payload,
 			       int len)
 {
-	free(msg->queue_msg_payload);
-	msg->queue_msg_payload = malloc(len);
-	if (!msg->queue_msg_payload)
-		return -NLE_NOMEM;
+	void *new_payload = malloc(len);
 
-	memcpy(msg->queue_msg_payload, payload, len);
+	if (new_payload == NULL)
+		return -NLE_NOMEM;
+	memcpy(new_payload, payload, len);
+
+	free(msg->queue_msg_payload);
+
+	msg->queue_msg_payload = new_payload;
 	msg->queue_msg_payload_len = len;
 	msg->ce_mask |= QUEUE_MSG_ATTR_PAYLOAD;
 	return 0;
@@ -455,20 +459,20 @@
 }
 
 static const struct trans_tbl nfnl_queue_msg_attrs[] = {
-	__ADD(QUEUE_MSG_ATTR_GROUP,		group)
-	__ADD(QUEUE_MSG_ATTR_FAMILY,		family)
-	__ADD(QUEUE_MSG_ATTR_PACKETID,		packetid)
-	__ADD(QUEUE_MSG_ATTR_HWPROTO,		hwproto)
-	__ADD(QUEUE_MSG_ATTR_HOOK,		hook)
-	__ADD(QUEUE_MSG_ATTR_MARK,		mark)
-	__ADD(QUEUE_MSG_ATTR_TIMESTAMP,		timestamp)
-	__ADD(QUEUE_MSG_ATTR_INDEV,		indev)
-	__ADD(QUEUE_MSG_ATTR_OUTDEV,		outdev)
-	__ADD(QUEUE_MSG_ATTR_PHYSINDEV,		physindev)
-	__ADD(QUEUE_MSG_ATTR_PHYSOUTDEV,	physoutdev)
-	__ADD(QUEUE_MSG_ATTR_HWADDR,		hwaddr)
-	__ADD(QUEUE_MSG_ATTR_PAYLOAD,		payload)
-	__ADD(QUEUE_MSG_ATTR_VERDICT,		verdict)
+	__ADD(QUEUE_MSG_ATTR_GROUP,		group),
+	__ADD(QUEUE_MSG_ATTR_FAMILY,		family),
+	__ADD(QUEUE_MSG_ATTR_PACKETID,		packetid),
+	__ADD(QUEUE_MSG_ATTR_HWPROTO,		hwproto),
+	__ADD(QUEUE_MSG_ATTR_HOOK,		hook),
+	__ADD(QUEUE_MSG_ATTR_MARK,		mark),
+	__ADD(QUEUE_MSG_ATTR_TIMESTAMP,		timestamp),
+	__ADD(QUEUE_MSG_ATTR_INDEV,		indev),
+	__ADD(QUEUE_MSG_ATTR_OUTDEV,		outdev),
+	__ADD(QUEUE_MSG_ATTR_PHYSINDEV,		physindev),
+	__ADD(QUEUE_MSG_ATTR_PHYSOUTDEV,	physoutdev),
+	__ADD(QUEUE_MSG_ATTR_HWADDR,		hwaddr),
+	__ADD(QUEUE_MSG_ATTR_PAYLOAD,		payload),
+	__ADD(QUEUE_MSG_ATTR_VERDICT,		verdict),
 };
 
 static char *nfnl_queue_msg_attrs2str(int attrs, char *buf, size_t len)
diff --git a/lib/netfilter/queue_obj.c b/lib/netfilter/queue_obj.c
index 5edcc68..690b26e 100644
--- a/lib/netfilter/queue_obj.c
+++ b/lib/netfilter/queue_obj.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/netfilter/queue_obj.c	Netfilter Queue
  *
@@ -53,9 +54,9 @@
 }
 
 static const struct trans_tbl copy_modes[] = {
-	__ADD(NFNL_QUEUE_COPY_NONE,	none)
-	__ADD(NFNL_QUEUE_COPY_META,	meta)
-	__ADD(NFNL_QUEUE_COPY_PACKET,	packet)
+	__ADD(NFNL_QUEUE_COPY_NONE,	none),
+	__ADD(NFNL_QUEUE_COPY_META,	meta),
+	__ADD(NFNL_QUEUE_COPY_PACKET,	packet),
 };
 
 char *nfnl_queue_copy_mode2str(enum nfnl_queue_copy_mode copy_mode, char *buf,
@@ -65,7 +66,7 @@
 			   ARRAY_SIZE(copy_modes));
 }
 
-enum nfnl_queue_copy_mode nfnl_queue_str2copy_mode(const char *name)
+int nfnl_queue_str2copy_mode(const char *name)
 {
 	return __str2type(name, copy_modes, ARRAY_SIZE(copy_modes));
 }
@@ -161,12 +162,12 @@
 	return queue->queue_copy_range;
 }
 
-static int nfnl_queue_compare(struct nl_object *_a, struct nl_object *_b,
-			      uint32_t attrs, int flags)
+static uint64_t nfnl_queue_compare(struct nl_object *_a, struct nl_object *_b,
+				   uint64_t attrs, int flags)
 {
 	struct nfnl_queue *a = (struct nfnl_queue *) _a;
 	struct nfnl_queue *b = (struct nfnl_queue *) _b;
-	int diff = 0;
+	uint64_t diff = 0;
 
 #define NFNL_QUEUE_DIFF(ATTR, EXPR) \
 	ATTR_DIFF(attrs, QUEUE_ATTR_##ATTR, a, b, EXPR)
@@ -185,10 +186,10 @@
 }
 
 static const struct trans_tbl nfnl_queue_attrs[] = {
-	__ADD(QUEUE_ATTR_GROUP,		group)
-	__ADD(QUEUE_ATTR_MAXLEN,	maxlen)
-	__ADD(QUEUE_ATTR_COPY_MODE,	copy_mode)
-	__ADD(QUEUE_ATTR_COPY_RANGE,	copy_range)
+	__ADD(QUEUE_ATTR_GROUP,		group),
+	__ADD(QUEUE_ATTR_MAXLEN,	maxlen),
+	__ADD(QUEUE_ATTR_COPY_MODE,	copy_mode),
+	__ADD(QUEUE_ATTR_COPY_RANGE,	copy_range),
 };
 
 static char *nfnl_queue_attrs2str(int attrs, char *buf, size_t len)
diff --git a/lib/nl.c b/lib/nl.c
index 25fd59c..bd8e331 100644
--- a/lib/nl.c
+++ b/lib/nl.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/nl.c		Core Netlink Interface
  *
@@ -27,11 +28,13 @@
 
 #include <netlink-private/netlink.h>
 #include <netlink-private/socket.h>
+#include <netlink-private/utils.h>
 #include <netlink/netlink.h>
 #include <netlink/utils.h>
 #include <netlink/handlers.h>
 #include <netlink/msg.h>
 #include <netlink/attr.h>
+#include <linux/socket.h>
 
 /**
  * @defgroup core_types Data Types
@@ -86,8 +89,13 @@
  *       This capability is indicated by
  *       `%NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE`.
  *
+ * @note nl_connect() creates and sets the file descriptor. You can setup the file
+ *       descriptor yourself by creating and binding it, and then calling
+ *       nl_socket_set_fd(). The result will be the same.
+ *
  * @see nl_socket_alloc()
  * @see nl_close()
+ * @see nl_socket_set_fd()
  *
  * @return 0 on success or a negative error code.
  *
@@ -98,95 +106,113 @@
 	int err, flags = 0;
 	int errsv;
 	socklen_t addrlen;
+	struct sockaddr_nl local = { 0 };
+	int try_bind = 1;
 
 #ifdef SOCK_CLOEXEC
 	flags |= SOCK_CLOEXEC;
 #endif
 
-        if (sk->s_fd != -1)
-                return -NLE_BAD_SOCK;
+	if (sk->s_fd != -1)
+		return -NLE_BAD_SOCK;
 
 	sk->s_fd = socket(AF_NETLINK, SOCK_RAW | flags, protocol);
 	if (sk->s_fd < 0) {
 		errsv = errno;
-		NL_DBG(4, "nl_connect(%p): socket() failed with %d\n", sk, errsv);
+		NL_DBG(4, "nl_connect(%p): socket() failed with %d (%s)\n", sk, errsv,
+			nl_strerror_l(errsv));
 		err = -nl_syserr2nlerr(errsv);
 		goto errout;
 	}
 
-	if (!(sk->s_flags & NL_SOCK_BUFSIZE_SET)) {
-		err = nl_socket_set_buffer_size(sk, 0, 0);
-		if (err < 0)
-			goto errout;
-	}
+	err = nl_socket_set_buffer_size(sk, 0, 0);
+	if (err < 0)
+		goto errout;
 
 	if (_nl_socket_is_local_port_unspecified (sk)) {
 		uint32_t port;
 		uint32_t used_ports[32] = { 0 };
+		int ntries = 0;
 
 		while (1) {
-			port = _nl_socket_generate_local_port_no_release(sk);
-
-			if (port == UINT32_MAX) {
-				NL_DBG(4, "nl_connect(%p): no more unused local ports.\n", sk);
-				_nl_socket_used_ports_release_all(used_ports);
-				err = -NLE_EXIST;
-				goto errout;
+			if (ntries++ > 5) {
+				/* try only a few times. We hit this only if many ports are already in
+				 * use but allocated *outside* libnl/generate_local_port(). */
+				_nl_socket_set_local_port_no_release (sk, 0);
+				break;
 			}
+
+			port = _nl_socket_set_local_port_no_release(sk, 1);
+			if (port == 0)
+				break;
+
 			err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
 				   sizeof(sk->s_local));
-			if (err == 0)
+			if (err == 0) {
+				try_bind = 0;
 				break;
+			}
 
 			errsv = errno;
 			if (errsv == EADDRINUSE) {
 				NL_DBG(4, "nl_connect(%p): local port %u already in use. Retry.\n", sk, (unsigned) port);
 				_nl_socket_used_ports_set(used_ports, port);
 			} else {
-				NL_DBG(4, "nl_connect(%p): bind() for port %u failed with %d\n", sk, (unsigned) port, errsv);
+				NL_DBG(4, "nl_connect(%p): bind() for port %u failed with %d (%s)\n",
+					sk, (unsigned) port, errsv, nl_strerror_l(errsv));
 				_nl_socket_used_ports_release_all(used_ports);
 				err = -nl_syserr2nlerr(errsv);
 				goto errout;
 			}
 		}
 		_nl_socket_used_ports_release_all(used_ports);
-	} else {
+	}
+	if (try_bind) {
 		err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
 			   sizeof(sk->s_local));
 		if (err != 0) {
 			errsv = errno;
-			NL_DBG(4, "nl_connect(%p): bind() failed with %d\n", sk, errsv);
+			NL_DBG(4, "nl_connect(%p): bind() failed with %d (%s)\n",
+				sk, errsv, nl_strerror_l(errsv));
 			err = -nl_syserr2nlerr(errsv);
 			goto errout;
 		}
 	}
 
-	addrlen = sizeof(sk->s_local);
-	err = getsockname(sk->s_fd, (struct sockaddr *) &sk->s_local,
+	addrlen = sizeof(local);
+	err = getsockname(sk->s_fd, (struct sockaddr *) &local,
 			  &addrlen);
 	if (err < 0) {
+		NL_DBG(4, "nl_connect(%p): getsockname() failed with %d (%s)\n",
+			sk, errno, nl_strerror_l(errno));
 		err = -nl_syserr2nlerr(errno);
 		goto errout;
 	}
 
-	if (addrlen != sizeof(sk->s_local)) {
+	if (addrlen != sizeof(local)) {
 		err = -NLE_NOADDR;
 		goto errout;
 	}
 
-	if (sk->s_local.nl_family != AF_NETLINK) {
+	if (local.nl_family != AF_NETLINK) {
 		err = -NLE_AF_NOSUPPORT;
 		goto errout;
 	}
 
+	if (sk->s_local.nl_pid != local.nl_pid) {
+		/* The port id is different. That can happen if the port id was zero
+		 * and kernel assigned a local port. */
+		nl_socket_set_local_port (sk, local.nl_pid);
+	}
+	sk->s_local = local;
 	sk->s_proto = protocol;
 
 	return 0;
 errout:
-        if (sk->s_fd != -1) {
-    		close(sk->s_fd);
-    		sk->s_fd = -1;
-        }
+	if (sk->s_fd != -1) {
+		close(sk->s_fd);
+		sk->s_fd = -1;
+	}
 
 	return err;
 }
@@ -254,8 +280,11 @@
 
 	ret = sendto(sk->s_fd, buf, size, 0, (struct sockaddr *)
 		     &sk->s_peer, sizeof(sk->s_peer));
-	if (ret < 0)
+	if (ret < 0) {
+		NL_DBG(4, "nl_sendto(%p): sendto() failed with %d (%s)\n",
+			sk, errno, nl_strerror_l(errno));
 		return -nl_syserr2nlerr(errno);
+	}
 
 	return ret;
 }
@@ -312,8 +341,11 @@
 			return ret;
 
 	ret = sendmsg(sk->s_fd, hdr, 0);
-	if (ret < 0)
+	if (ret < 0) {
+		NL_DBG(4, "nl_sendmsg(%p): sendmsg() failed with %d (%s)\n",
+			sk, errno, nl_strerror_l(errno));
 		return -nl_syserr2nlerr(errno);
+	}
 
 	NL_DBG(4, "sent %d bytes\n", ret);
 	return ret;
@@ -349,6 +381,7 @@
 		.msg_iov = iov,
 		.msg_iovlen = iovlen,
 	};
+	char buf[CMSG_SPACE(sizeof(struct ucred))];
 
 	/* Overwrite destination if specified in the message itself, defaults
 	 * to the peer address of the socket.
@@ -360,7 +393,6 @@
 	/* Add credentials if present. */
 	creds = nlmsg_get_creds(msg);
 	if (creds != NULL) {
-		char buf[CMSG_SPACE(sizeof(struct ucred))];
 		struct cmsghdr *cmsg;
 
 		hdr.msg_control = buf;
@@ -590,7 +622,9 @@
  *
  * This function blocks until data is available to be read unless the socket
  * has been put into non-blocking mode using nl_socket_set_nonblocking() in
- * which case this function will return immediately with a return value of 0.
+ * which case this function will return immediately with a return value of
+ * -NLA_AGAIN (versions before 3.2.22 returned instead 0, in which case you
+ * should check first clear errno and then check for errno EAGAIN).
  *
  * The buffer size used when reading from the netlink socket and thus limiting
  * the maximum size of a netlink message that can be read defaults to the size
@@ -641,13 +675,14 @@
 	if (!buf || !nla)
 		return -NLE_INVAL;
 
-	if (sk->s_flags & NL_MSG_PEEK)
+	if (   (sk->s_flags & NL_MSG_PEEK)
+	    || (!(sk->s_flags & NL_MSG_PEEK_EXPLICIT) && sk->s_bufsize == 0))
 		flags |= MSG_PEEK | MSG_TRUNC;
 
 	if (page_size == 0)
 		page_size = getpagesize() * 4;
 
-	iov.iov_len = sk->s_bufsize ? : page_size;
+	iov.iov_len = sk->s_bufsize ? sk->s_bufsize : page_size;
 	iov.iov_base = malloc(iov.iov_len);
 
 	if (!iov.iov_base) {
@@ -675,12 +710,22 @@
 			NL_DBG(3, "recvmsg() returned EINTR, retrying\n");
 			goto retry;
 		}
+
+		NL_DBG(4, "recvmsg(%p): nl_recv() failed with %d (%s)\n",
+			sk, errno, nl_strerror_l(errno));
 		retval = -nl_syserr2nlerr(errno);
 		goto abort;
 	}
 
 	if (msg.msg_flags & MSG_CTRUNC) {
 		void *tmp;
+
+		if (msg.msg_controllen == 0) {
+			retval = -NLE_MSG_TRUNC;
+			NL_DBG(4, "recvmsg(%p): Received unexpected control data", sk);
+			goto abort;
+		}
+
 		msg.msg_controllen *= 2;
 		tmp = realloc(msg.msg_control, msg.msg_controllen);
 		if (!tmp) {
@@ -693,6 +738,13 @@
 
 	if (iov.iov_len < n || (msg.msg_flags & MSG_TRUNC)) {
 		void *tmp;
+
+		/* respond with error to an incomplete message */
+		if (flags == 0) {
+			retval = -NLE_MSG_TRUNC;
+			goto abort;
+		}
+
 		/* Provided buffer is not long enough, enlarge it
 		 * to size of n (which should be total length of the message)
 		 * and try again. */
@@ -707,7 +759,7 @@
 		goto retry;
 	}
 
-        if (flags != 0) {
+	if (flags != 0) {
 		/* Buffer is big enough, do the actual reading */
 		flags = 0;
 		goto retry;
@@ -867,7 +919,7 @@
 				interrupted = 1;
 			}
 		}
-	
+
 		/* Other side wishes to see an ack for this message */
 		if (hdr->nlmsg_flags & NLM_F_ACK) {
 			if (cb->cb_set[NL_CB_SEND_ACK])
@@ -926,10 +978,13 @@
 					goto out;
 				}
 			} else if (e->error) {
+				NL_DBG(4, "recvmsgs(%p): RTNETLINK responded with %d (%s)\n",
+					sk, -e->error, nl_strerror_l(-e->error));
+
 				/* Error message reported back from kernel. */
 				if (cb->cb_err) {
 					err = cb->cb_err(&nla, e,
-							   cb->cb_err_arg);
+							 cb->cb_err_arg);
 					if (err < 0)
 						goto out;
 					else if (err == NL_SKIP)
@@ -955,7 +1010,7 @@
 		err = 0;
 		hdr = nlmsg_next(hdr, &n);
 	}
-	
+
 	nlmsg_free(msg);
 	free(buf);
 	free(creds);
@@ -1077,6 +1132,7 @@
 	int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *,
 		      struct nlmsghdr *, struct nl_parser_param *);
 	struct nl_object *result;
+	int *syserror;
 };
 
 static int __store_answer(struct nl_object *obj, struct nl_parser_param *p)
@@ -1103,20 +1159,45 @@
 	return pp->parser(NULL, &msg->nm_src, msg->nm_nlh, &parse_arg);
 }
 
+static int __pickup_answer_syserr(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg)
+{
+	*(((struct pickup_param *) arg)->syserror) = nlerr->error;
+
+	return -nl_syserr2nlerr(nlerr->error);
+}
+
 /** @endcond */
 
 /**
  * Pickup netlink answer, parse is and return object
- * @arg sk		Netlink socket
- * @arg parser		Parser function to parse answer
- * @arg result		Result pointer to return parsed object
+ * @arg sk              Netlink socket
+ * @arg parser          Parser function to parse answer
+ * @arg result          Result pointer to return parsed object
  *
  * @return 0 on success or a negative error code.
  */
 int nl_pickup(struct nl_sock *sk,
-	      int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *,
-			    struct nlmsghdr *, struct nl_parser_param *),
-	      struct nl_object **result)
+              int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *,
+                            struct nlmsghdr *, struct nl_parser_param *),
+              struct nl_object **result)
+{
+	return nl_pickup_keep_syserr(sk, parser, result, NULL);
+}
+
+/**
+ * Pickup netlink answer, parse is and return object with preserving system error
+ * @arg sk              Netlink socket
+ * @arg parser          Parser function to parse answer
+ * @arg result          Result pointer to return parsed object
+ * @arg syserr          Result pointer for the system error in case of failure
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nl_pickup_keep_syserr(struct nl_sock *sk,
+                          int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *,
+                                        struct nlmsghdr *, struct nl_parser_param *),
+                          struct nl_object **result,
+                          int *syserror)
 {
 	struct nl_cb *cb;
 	int err;
@@ -1129,6 +1210,11 @@
 		return -NLE_NOMEM;
 
 	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, __pickup_answer, &pp);
+	if (syserror) {
+		*syserror = 0;
+		pp.syserror = syserror;
+		nl_cb_err(cb, NL_CB_CUSTOM, __pickup_answer_syserr, &pp);
+	}
 
 	err = nl_recvmsgs(sk, cb);
 	if (err < 0)
diff --git a/lib/object.c b/lib/object.c
index 52bc873..4e14554 100644
--- a/lib/object.c
+++ b/lib/object.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/object.c		Generic Cacheable Object
  *
@@ -131,7 +132,7 @@
 	new->ce_mask = obj->ce_mask;
 
 	if (size)
-		memcpy((void *)new + doff, (void *)obj + doff, size);
+		memcpy((char *)new + doff, (char *)obj + doff, size);
 
 	if (ops->oo_clone) {
 		if (ops->oo_clone(new, obj) < 0) {
@@ -295,12 +296,12 @@
 
 void nl_object_dump_buf(struct nl_object *obj, char *buf, size_t len)
 {
-        struct nl_dump_params dp = {
-                .dp_buf = buf,
-                .dp_buflen = len,
-        };
+	struct nl_dump_params dp = {
+		.dp_buf = buf,
+		.dp_buflen = len,
+	};
 
-        return nl_object_dump(obj, &dp);
+	nl_object_dump(obj, &dp);
 }
 
 /**
@@ -343,7 +344,7 @@
 	if (ops->oo_compare == NULL)
 		return 0;
 
-	return !(ops->oo_compare(a, b, req_attrs, 0));
+	return !(ops->oo_compare(a, b, req_attrs, ID_COMPARISON));
 }
 
 /**
@@ -358,17 +359,42 @@
  *
  * @return Bitmask describing differences or 0 if they are completely identical.
  */
-uint32_t nl_object_diff(struct nl_object *a, struct nl_object *b)
+uint64_t nl_object_diff64(struct nl_object *a, struct nl_object *b)
 {
 	struct nl_object_ops *ops = obj_ops(a);
 
 	if (ops != obj_ops(b) || ops->oo_compare == NULL)
-		return UINT_MAX;
+		return UINT64_MAX;
 
 	return ops->oo_compare(a, b, ~0, 0);
 }
 
 /**
+ * Compute 32-bit bitmask representing difference in attribute values
+ * @arg a		an object
+ * @arg b		another object of same type
+ *
+ * The bitmask returned is specific to an object type, each bit set represents
+ * an attribute which mismatches in either of the two objects. Unavailability
+ * of an attribute in one object and presence in the other is regarded a
+ * mismatch as well.
+ *
+ * @return Bitmask describing differences or 0 if they are completely identical.
+ *	   32nd bit indicates if higher bits from the 64-bit compare were
+ *	   different.
+ */
+uint32_t nl_object_diff(struct nl_object *a, struct nl_object *b)
+{
+	uint64_t  diff;
+
+	diff = nl_object_diff64(a, b);
+
+	return (diff & ~((uint64_t) 0xFFFFFFFF))
+		? (uint32_t) diff | (1 << 31)
+		: (uint32_t) diff;
+}
+
+/**
  * Match a filter against an object
  * @arg obj		object to check
  * @arg filter		object of same type acting as filter
@@ -383,7 +409,7 @@
 
 	if (ops != obj_ops(filter) || ops->oo_compare == NULL)
 		return 0;
-	
+
 	return !(ops->oo_compare(obj, filter, filter->ce_mask,
 				 LOOSE_COMPARISON));
 }
diff --git a/lib/route/act.c b/lib/route/act.c
index 85ce445..a0aff7f 100644
--- a/lib/route/act.c
+++ b/lib/route/act.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/act.c       Action
  *
@@ -21,11 +22,21 @@
 #include <netlink/utils.h>
 #include <netlink-private/route/tc-api.h>
 #include <netlink/route/link.h>
+#include <netlink/route/action.h>
 
 
 static struct nl_object_ops act_obj_ops;
 static struct nl_cache_ops rtnl_act_ops;
 
+struct rtnl_act * rtnl_act_next(struct rtnl_act *act)
+{
+    if (act == NULL) {
+        return NULL;
+    }
+
+    return act->a_next;
+}
+
 int rtnl_act_append(struct rtnl_act **head, struct rtnl_act *new)
 {
 	struct rtnl_act *p_act;
@@ -53,14 +64,14 @@
 {
 	struct rtnl_act *a, **ap;
 
-        for (ap = head; (a = *ap) != NULL; ap = &a->a_next)
-                if (a == act)
-                        break;
-        if (a) {
-                *ap = a->a_next;
-                a->a_next = NULL;
-                return 0;
-        }
+	for (ap = head; (a = *ap) != NULL; ap = &a->a_next)
+		if (a == act)
+			break;
+	if (a) {
+		*ap = a->a_next;
+		a->a_next = NULL;
+		return 0;
+	}
 
 	return -NLE_OBJ_NOTFOUND;
 }
diff --git a/lib/route/act/gact.c b/lib/route/act/gact.c
new file mode 100644
index 0000000..e37ef9f
--- /dev/null
+++ b/lib/route/act/gact.c
@@ -0,0 +1,183 @@
+/*
+ * lib/route/act/gact.c		gact action
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2016 Sushma Sitaram <sushma.sitaram@intel.com>
+ */
+
+/**
+ * @ingroup act
+ * @defgroup act_gact GACT Editing
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/act/gact.h>
+
+static struct nla_policy gact_policy[TCA_GACT_MAX + 1] = {
+	[TCA_GACT_PARMS]             = { .minlen = sizeof(struct tc_gact) },
+};
+
+static int gact_msg_parser(struct rtnl_tc *tc, void *data)
+{
+	struct rtnl_gact *u = data;
+	struct nlattr *tb[TCA_GACT_MAX + 1];
+	int err;
+
+	err = tca_parse(tb, TCA_GACT_MAX, tc, gact_policy);
+	if (err < 0)
+		return err;
+
+	if (!tb[TCA_GACT_PARMS])
+		return -NLE_MISSING_ATTR;
+
+	nla_memcpy(&u->g_parm, tb[TCA_GACT_PARMS], sizeof(u->g_parm));
+
+	return 0;
+}
+
+static void gact_free_data(struct rtnl_tc *tc, void *data)
+{
+}
+
+static int gact_clone(void *_dst, void *_src)
+{
+	struct rtnl_gact *dst = _dst, *src = _src;
+
+	memcpy(&dst->g_parm, &src->g_parm, sizeof(src->g_parm));
+	return 0;
+}
+
+static void gact_dump_line(struct rtnl_tc *tc, void *data,
+			  struct nl_dump_params *p)
+{
+	struct rtnl_gact *u = data;
+
+	if (!u)
+		return;
+
+	switch(u->g_parm.action){
+	case TC_ACT_UNSPEC:
+		nl_dump(p, " continue");
+		break;
+	case TC_ACT_SHOT:
+		nl_dump(p, " drop");
+		break;
+	case TC_ACT_RECLASSIFY:
+		nl_dump(p, " reclassify");
+		break;
+	case TC_ACT_OK:
+		nl_dump(p, " pass");
+		break;
+	}
+
+}
+
+static void gact_dump_details(struct rtnl_tc *tc, void *data,
+			     struct nl_dump_params *p)
+{
+}
+
+static void gact_dump_stats(struct rtnl_tc *tc, void *data,
+			   struct nl_dump_params *p)
+{
+	struct rtnl_gact *u = data;
+
+	if (!u)
+		return;
+	/* TODO */
+}
+
+
+static int gact_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
+{
+	struct rtnl_gact *u = data;
+
+	if (!u)
+		return 0;
+
+	NLA_PUT(msg, TCA_GACT_PARMS, sizeof(u->g_parm), &u->g_parm);
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_NOMEM;
+}
+
+/**
+ * @name Attribute Modifications
+ * @{
+ */
+
+int rtnl_gact_set_action(struct rtnl_act *act, int action)
+{
+	struct rtnl_gact *u;
+
+	if (!(u = (struct rtnl_gact *) rtnl_tc_data(TC_CAST(act))))
+		return -NLE_NOMEM;
+
+	if (action > TC_ACT_SHOT || action < TC_ACT_UNSPEC)
+		return -NLE_INVAL;
+
+	switch (action) {
+	case TC_ACT_UNSPEC:
+	case TC_ACT_SHOT:
+		u->g_parm.action = action;
+		break;
+	case TC_ACT_OK:
+	case TC_ACT_RECLASSIFY:
+	default:
+		return NLE_OPNOTSUPP;
+	}
+
+	return 0;
+}
+
+int rtnl_gact_get_action(struct rtnl_act *act)
+{
+	struct rtnl_gact *u;
+
+	if (!(u = (struct rtnl_gact *) rtnl_tc_data(TC_CAST(act))))
+		return -NLE_NOMEM;
+	return u->g_parm.action;
+}
+
+
+/** @} */
+
+static struct rtnl_tc_ops gact_ops = {
+	.to_kind		= "gact",
+	.to_type		= RTNL_TC_TYPE_ACT,
+	.to_size		= sizeof(struct rtnl_gact),
+	.to_msg_parser		= gact_msg_parser,
+	.to_free_data		= gact_free_data,
+	.to_clone		= gact_clone,
+	.to_msg_fill		= gact_msg_fill,
+	.to_dump = {
+	    [NL_DUMP_LINE]	= gact_dump_line,
+	    [NL_DUMP_DETAILS]	= gact_dump_details,
+	    [NL_DUMP_STATS]	= gact_dump_stats,
+	},
+};
+
+static void __init gact_init(void)
+{
+	rtnl_tc_register(&gact_ops);
+}
+
+static void __exit gact_exit(void)
+{
+	rtnl_tc_unregister(&gact_ops);
+}
+
+/** @} */
diff --git a/lib/route/act/mirred.c b/lib/route/act/mirred.c
index d047e24..b674fb8 100644
--- a/lib/route/act/mirred.c
+++ b/lib/route/act/mirred.c
@@ -1,5 +1,5 @@
 /*
- * lib/route/cls/mirred.c		mirred action
+ * lib/route/act/mirred.c		mirred action
  *
  *	This library is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU Lesser General Public
diff --git a/lib/route/act/skbedit.c b/lib/route/act/skbedit.c
new file mode 100644
index 0000000..d85265e
--- /dev/null
+++ b/lib/route/act/skbedit.c
@@ -0,0 +1,290 @@
+/*
+ * lib/route/act/skbedit.c		skbedit action
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2015 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+/**
+ * @ingroup act
+ * @defgroup act_skbedit SKB Editing
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/act/skbedit.h>
+
+static struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = {
+	[TCA_SKBEDIT_PARMS]             = { .minlen = sizeof(struct tc_skbedit) },
+	[TCA_SKBEDIT_PRIORITY]          = { .type = NLA_U32 },
+	[TCA_SKBEDIT_QUEUE_MAPPING]     = { .type = NLA_U16 },
+	[TCA_SKBEDIT_MARK]              = { .type = NLA_U32 },
+};
+
+static int skbedit_msg_parser(struct rtnl_tc *tc, void *data)
+{
+	struct rtnl_skbedit *u = data;
+	struct nlattr *tb[TCA_SKBEDIT_MAX + 1];
+	int err;
+
+	err = tca_parse(tb, TCA_SKBEDIT_MAX, tc, skbedit_policy);
+	if (err < 0)
+		return err;
+
+	if (!tb[TCA_SKBEDIT_PARMS])
+		return -NLE_MISSING_ATTR;
+
+	u->s_flags = 0;
+	if (tb[TCA_SKBEDIT_PRIORITY] != NULL) {
+		u->s_flags |= SKBEDIT_F_PRIORITY;
+		u->s_prio = nla_get_u32(tb[TCA_SKBEDIT_PRIORITY]);
+	}
+
+	if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) {
+		u->s_flags |= SKBEDIT_F_QUEUE_MAPPING;
+		u->s_queue_mapping = nla_get_u16(tb[TCA_SKBEDIT_QUEUE_MAPPING]);
+	}
+
+	if (tb[TCA_SKBEDIT_MARK] != NULL) {
+		u->s_flags |= SKBEDIT_F_MARK;
+		u->s_mark = nla_get_u32(tb[TCA_SKBEDIT_MARK]);
+	}
+
+	return 0;
+}
+
+static void skbedit_free_data(struct rtnl_tc *tc, void *data)
+{
+}
+
+static int skbedit_clone(void *_dst, void *_src)
+{
+	struct rtnl_skbedit *dst = _dst, *src = _src;
+
+	memcpy(dst, src, sizeof(*src));
+	return 0;
+}
+
+static void skbedit_dump_line(struct rtnl_tc *tc, void *data,
+			  struct nl_dump_params *p)
+{
+	struct rtnl_skbedit *u = data;
+
+	if (!u)
+		return;
+
+	if (u->s_flags & SKBEDIT_F_PRIORITY)
+		nl_dump(p, " priority %u", u->s_prio);
+
+	if (u->s_flags & SKBEDIT_F_MARK)
+		nl_dump(p, " mark %u", u->s_mark);
+
+	if (u->s_flags & SKBEDIT_F_QUEUE_MAPPING)
+		nl_dump(p, " queue_mapping %u", u->s_queue_mapping);
+
+	switch(u->s_parm.action){
+	case TC_ACT_UNSPEC:
+		nl_dump(p, " unspecified");
+		break;
+	case TC_ACT_PIPE:
+		nl_dump(p, " pipe");
+		break;
+	case TC_ACT_STOLEN:
+		nl_dump(p, " stolen");
+		break;
+	case TC_ACT_SHOT:
+		nl_dump(p, " shot");
+		break;
+	case TC_ACT_QUEUED:
+		nl_dump(p, " queued");
+		break;
+	case TC_ACT_REPEAT:
+		nl_dump(p, " repeat");
+		break;
+	}
+}
+
+static void skbedit_dump_details(struct rtnl_tc *tc, void *data,
+			     struct nl_dump_params *p)
+{
+}
+
+static void skbedit_dump_stats(struct rtnl_tc *tc, void *data,
+			   struct nl_dump_params *p)
+{
+	struct rtnl_skbedit *u = data;
+
+	if (!u)
+		return;
+	/* TODO */
+}
+
+
+static int skbedit_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
+{
+	struct rtnl_skbedit *u = data;
+
+	if (!u)
+		return 0;
+
+	NLA_PUT(msg, TCA_SKBEDIT_PARMS, sizeof(u->s_parm), &u->s_parm);
+
+	if (u->s_flags & SKBEDIT_F_MARK)
+		NLA_PUT_U32(msg, TCA_SKBEDIT_MARK, u->s_mark);
+
+	if (u->s_flags & SKBEDIT_F_PRIORITY)
+		NLA_PUT_U32(msg, TCA_SKBEDIT_PRIORITY, u->s_prio);
+
+	if (u->s_flags & SKBEDIT_F_QUEUE_MAPPING)
+		NLA_PUT_U32(msg, TCA_SKBEDIT_QUEUE_MAPPING, u->s_queue_mapping);
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_NOMEM;
+}
+
+/**
+ * @name Attribute Modifications
+ * @{
+ */
+
+int rtnl_skbedit_set_action(struct rtnl_act *act, int action)
+{
+	struct rtnl_skbedit *u;
+
+	if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act))))
+		return -NLE_NOMEM;
+
+	if (action > TC_ACT_REPEAT || action < TC_ACT_UNSPEC)
+		return -NLE_INVAL;
+
+	u->s_parm.action = action;
+	return 0;
+}
+
+int rtnl_skbedit_get_action(struct rtnl_act *act)
+{
+	struct rtnl_skbedit *u;
+
+	if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act))))
+		return -NLE_NOMEM;
+	return u->s_parm.action;
+}
+
+int rtnl_skbedit_set_queue_mapping(struct rtnl_act *act, uint16_t index)
+{
+	struct rtnl_skbedit *u;
+
+	if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act))))
+		return -NLE_NOMEM;
+
+	u->s_queue_mapping = index;
+	u->s_flags |= SKBEDIT_F_QUEUE_MAPPING;
+	return 0;
+}
+
+int rtnl_skbedit_get_queue_mapping(struct rtnl_act *act, uint16_t *index)
+{
+	struct rtnl_skbedit *u;
+
+	u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act));
+	if (!u)
+		return -NLE_NOMEM;
+	if (!(u->s_flags & SKBEDIT_F_QUEUE_MAPPING))
+		return -NLE_NOATTR;
+
+	*index = u->s_queue_mapping;
+	return 0;
+}
+
+int rtnl_skbedit_set_mark(struct rtnl_act *act, uint32_t mark)
+{
+	struct rtnl_skbedit *u;
+
+	if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act))))
+		return -NLE_NOMEM;
+
+	u->s_mark = mark;
+	u->s_flags |= SKBEDIT_F_MARK;
+	return 0;
+}
+
+int rtnl_skbedit_get_mark(struct rtnl_act *act, uint32_t *mark)
+{
+	struct rtnl_skbedit *u;
+
+	u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act));
+	if (!u)
+		return -NLE_NOMEM;
+	if (!(u->s_flags & SKBEDIT_F_MARK))
+		return -NLE_NOATTR;
+
+	*mark = u->s_mark;
+	return 0;
+}
+
+int rtnl_skbedit_set_priority(struct rtnl_act *act, uint32_t prio)
+{
+	struct rtnl_skbedit *u;
+
+	if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act))))
+		return -NLE_NOMEM;
+
+	u->s_prio = prio;
+	u->s_flags |= SKBEDIT_F_PRIORITY;
+	return 0;
+}
+
+int rtnl_skbedit_get_priority(struct rtnl_act *act, uint32_t *prio)
+{
+	struct rtnl_skbedit *u;
+
+	u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act));
+	if (!u)
+		return -NLE_NOMEM;
+	if (!(u->s_flags & SKBEDIT_F_PRIORITY))
+		return -NLE_NOATTR;
+
+	*prio = u->s_prio;
+	return 0;
+}
+
+/** @} */
+
+static struct rtnl_tc_ops skbedit_ops = {
+	.to_kind		= "skbedit",
+	.to_type		= RTNL_TC_TYPE_ACT,
+	.to_size		= sizeof(struct rtnl_skbedit),
+	.to_msg_parser		= skbedit_msg_parser,
+	.to_free_data		= skbedit_free_data,
+	.to_clone		= skbedit_clone,
+	.to_msg_fill		= skbedit_msg_fill,
+	.to_dump = {
+	    [NL_DUMP_LINE]	= skbedit_dump_line,
+	    [NL_DUMP_DETAILS]	= skbedit_dump_details,
+	    [NL_DUMP_STATS]	= skbedit_dump_stats,
+	},
+};
+
+static void __init skbedit_init(void)
+{
+	rtnl_tc_register(&skbedit_ops);
+}
+
+static void __exit skbedit_exit(void)
+{
+	rtnl_tc_unregister(&skbedit_ops);
+}
+
+/** @} */
diff --git a/lib/route/act/vlan.c b/lib/route/act/vlan.c
new file mode 100644
index 0000000..69b6f24
--- /dev/null
+++ b/lib/route/act/vlan.c
@@ -0,0 +1,426 @@
+/*
+ * lib/route/act/vlan.c        vlan action
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2018 Volodymyr Bendiuga <volodymyr.bendiuga@gmail.com>
+ */
+
+/**
+ * @ingroup act
+ * @defgroup act_vlan VLAN Manipulation
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/act/vlan.h>
+
+
+#define VLAN_F_VID   (1 << 0)
+#define VLAN_F_PROTO (1 << 1)
+#define VLAN_F_PRIO  (1 << 2)
+#define VLAN_F_ACT   (1 << 3)
+#define VLAN_F_MODE  (1 << 4)
+
+static struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = {
+	[TCA_VLAN_PARMS]                = { .minlen = sizeof(struct tc_vlan) },
+	[TCA_VLAN_PUSH_VLAN_ID]         = { .type = NLA_U16 },
+	[TCA_VLAN_PUSH_VLAN_PROTOCOL]   = { .type = NLA_U16 },
+	[TCA_VLAN_PUSH_VLAN_PRIORITY]   = { .type = NLA_U8 },
+};
+
+static int vlan_msg_parser(struct rtnl_tc *tc, void *data)
+{
+	struct rtnl_vlan *v = data;
+	struct nlattr *tb[TCA_VLAN_MAX + 1];
+	int err;
+
+	err = tca_parse(tb, TCA_VLAN_MAX, tc, vlan_policy);
+	if (err < 0)
+		return err;
+
+	v->v_flags = 0;
+	if (!tb[TCA_VLAN_PARMS])
+		return -NLE_MISSING_ATTR;
+	else {
+		nla_memcpy(&v->v_parm, tb[TCA_VLAN_PARMS], sizeof(v->v_parm));
+		v->v_flags |= VLAN_F_ACT;
+		v->v_flags |= VLAN_F_MODE;
+	}
+
+	if (tb[TCA_VLAN_PUSH_VLAN_ID]) {
+		v->v_vid = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]);
+		v->v_flags |= VLAN_F_VID;
+	}
+
+	if (tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]) {
+		v->v_proto = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]);
+		v->v_flags |= VLAN_F_PROTO;
+	}
+
+	if (tb[TCA_VLAN_PUSH_VLAN_PRIORITY]) {
+		v->v_prio = nla_get_u8(tb[TCA_VLAN_PUSH_VLAN_PRIORITY]);
+		v->v_flags |= VLAN_F_PRIO;
+	}
+
+	return 0;
+}
+
+static int vlan_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
+{
+	struct rtnl_vlan *v = data;
+
+	if (!v)
+		return 0;
+	if (!(v->v_flags & VLAN_F_MODE))
+		return -NLE_MISSING_ATTR;
+
+	NLA_PUT(msg, TCA_VLAN_PARMS, sizeof(v->v_parm), &v->v_parm);
+
+	/* vid is required for PUSH & MODIFY modes */
+	if ((v->v_parm.v_action != TCA_VLAN_ACT_POP) && !(v->v_flags & VLAN_F_VID))
+		return -NLE_MISSING_ATTR;
+
+	if (v->v_flags & VLAN_F_VID)
+		NLA_PUT_U16(msg, TCA_VLAN_PUSH_VLAN_ID, v->v_vid);
+
+	if (v->v_flags & VLAN_F_PROTO)
+		NLA_PUT_U16(msg, TCA_VLAN_PUSH_VLAN_PROTOCOL, v->v_proto);
+
+	if (v->v_flags & VLAN_F_PRIO)
+		NLA_PUT_U8(msg, TCA_VLAN_PUSH_VLAN_PRIORITY, v->v_prio);
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_NOMEM;
+}
+
+static void vlan_free_data(struct rtnl_tc *tc, void *data)
+{
+}
+
+static int vlan_clone(void *_dst, void *_src)
+{
+	struct rtnl_vlan *dst = _dst, *src = _src;
+
+	memcpy(&dst->v_parm, &src->v_parm, sizeof(src->v_parm));
+	return 0;
+}
+
+static void vlan_dump_line(struct rtnl_tc *tc, void *data,
+                           struct nl_dump_params *p)
+{
+	struct rtnl_vlan *v = data;
+
+	if (!v)
+		return;
+
+	if (!(v->v_flags & VLAN_F_ACT))
+		return;
+
+	if (TC_ACT_EXT_CMP(v->v_parm.action, TC_ACT_GOTO_CHAIN))
+		nl_dump(p, " goto chain %u", v->v_parm.action & TC_ACT_EXT_VAL_MASK);
+
+	if (TC_ACT_EXT_CMP(v->v_parm.action, TC_ACT_JUMP))
+		nl_dump(p, " jump %u", v->v_parm.action & TC_ACT_EXT_VAL_MASK);
+
+	switch(v->v_parm.action){
+	case TC_ACT_UNSPEC:
+		nl_dump(p, " unspecified");
+		break;
+	case TC_ACT_PIPE:
+		nl_dump(p, " pipe");
+		break;
+	case TC_ACT_STOLEN:
+		nl_dump(p, " stolen");
+		break;
+	case TC_ACT_SHOT:
+		nl_dump(p, " shot");
+		break;
+	case TC_ACT_QUEUED:
+		nl_dump(p, " queued");
+		break;
+	case TC_ACT_REPEAT:
+		nl_dump(p, " repeat");
+		break;
+	}
+}
+
+static void vlan_dump_details(struct rtnl_tc *tc, void *data,
+                              struct nl_dump_params *p)
+{
+	struct rtnl_vlan *v = data;
+
+	if (!v)
+		return;
+
+	if (v->v_flags & VLAN_F_MODE) {
+		switch (v->v_parm.v_action) {
+		case TCA_VLAN_ACT_POP:
+			nl_dump(p, " mode POP");
+			break;
+		case TCA_VLAN_ACT_PUSH:
+			nl_dump(p, " mode PUSH");
+			break;
+		case TCA_VLAN_ACT_MODIFY:
+			nl_dump(p, " mode MODIFY");
+			break;
+		}
+	}
+
+	if (v->v_flags & VLAN_F_VID)
+		nl_dump(p, " vlan id %u", v->v_vid);
+
+	if (v->v_flags & VLAN_F_PRIO)
+		nl_dump(p, " priority %u", v->v_prio);
+
+	if (v->v_flags & VLAN_F_PROTO)
+		nl_dump(p, " protocol %u", v->v_proto);
+}
+
+/**
+ * @name Attribute Modifications
+ * @{
+ */
+
+/**
+ * Set vlan mode
+ * @arg act             vlan action
+ * @arg mode            one of (TCA_VLAN_ACT_*: POP, PUSH, MODIFY)
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_vlan_set_mode(struct rtnl_act *act, int mode)
+{
+	struct rtnl_vlan *v;
+
+	if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act))))
+		return -NLE_NOMEM;
+
+	if (mode > TCA_VLAN_ACT_MODIFY)
+		return -NLE_RANGE;
+
+	v->v_parm.v_action = mode;
+	v->v_flags |= VLAN_F_MODE;
+
+	return 0;
+}
+
+/**
+ * Get vlan mode
+ * @arg act             vlan action
+ * @arg out_mode        vlan mode output paramter
+ * @return 0 on success if the vlan mode was returned or a negative error code.
+*/
+int rtnl_vlan_get_mode(struct rtnl_act *act, int *out_mode)
+{
+	struct rtnl_vlan *v;
+
+	if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act))))
+		return -NLE_INVAL;
+
+	if (!(v->v_flags & VLAN_F_MODE))
+		return -NLE_MISSING_ATTR;
+
+	*out_mode = v->v_parm.v_action;
+	return 0;
+}
+
+/**
+ * Set general action
+ * @arg act             vlan action
+ * @arg action          one of (TCA_ACT_*: PIPE, SHOT, GOTO_CHAIN, etc)
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_vlan_set_action(struct rtnl_act *act, int action)
+{
+	struct rtnl_vlan *v;
+
+	if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act))))
+		return -NLE_NOMEM;
+
+	v->v_parm.action = action;
+	v->v_flags |= VLAN_F_ACT;
+
+	return 0;
+}
+
+/**
+ * Get general action
+ * @arg act             vlan action
+ * @arg out_action      output parameter
+ * @return general 0 if out_action was set or a negative error code.
+*/
+int rtnl_vlan_get_action(struct rtnl_act *act, int *out_action)
+{
+	struct rtnl_vlan *v;
+
+	if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act))))
+		return -NLE_INVAL;
+
+	if (!(v->v_flags & VLAN_F_ACT))
+		return -NLE_MISSING_ATTR;
+
+	*out_action = v->v_parm.action;
+	return 0;
+}
+
+/**
+ * Set protocol
+ * @arg act             vlan action
+ * @arg protocol        one of (ETH_P_8021Q || ETH_P_8021AD)
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_vlan_set_protocol(struct rtnl_act *act, uint16_t protocol)
+{
+	struct rtnl_vlan *v;
+
+	if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act))))
+		return -NLE_NOMEM;
+
+	v->v_proto = protocol;
+	v->v_flags |= VLAN_F_PROTO;
+
+	return 0;
+}
+
+/**
+ * Get protocol
+ * @arg act             vlan action
+ * @arg out_protocol    protocol output argument
+ * @return 0 if the protocol was returned or a negative error code.
+*/
+int rtnl_vlan_get_protocol(struct rtnl_act *act, uint16_t *out_protocol)
+{
+	struct rtnl_vlan *v;
+
+	if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act))))
+		return -NLE_INVAL;
+
+	if (!(v->v_flags & VLAN_F_PROTO))
+		return -NLE_MISSING_ATTR;
+
+	*out_protocol = v->v_proto;
+	return 0;
+}
+
+/**
+ * Set vlan id
+ * @arg act             vlan action
+ * @arg vid             vlan id
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_vlan_set_vlan_id(struct rtnl_act *act, uint16_t vid)
+{
+	struct rtnl_vlan *v;
+
+	if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act))))
+		return -NLE_NOMEM;
+
+	if (vid > 4095)
+		return -NLE_RANGE;
+
+	v->v_vid = vid;
+	v->v_flags |= VLAN_F_VID;
+
+	return 0;
+}
+
+/**
+ * Get vlan id
+ * @arg act             vlan action
+ * @arg out_vid         output vlan id
+ * @return 0 if the vlan id was returned or a negative error code.
+*/
+int rtnl_vlan_get_vlan_id(struct rtnl_act *act, uint16_t *out_vid)
+{
+	struct rtnl_vlan *v;
+
+	if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act))))
+		return -NLE_INVAL;
+
+	if (!(v->v_flags & VLAN_F_VID))
+		return -NLE_MISSING_ATTR;
+
+	*out_vid = v->v_vid;
+	return 0;
+}
+
+/**
+ * Set vlan prio
+ * @arg act             vlan action
+ * @arg prio            vlan priority (0 - 7)
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_vlan_set_vlan_prio(struct rtnl_act *act, uint8_t prio)
+{
+	struct rtnl_vlan *v;
+
+	if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act))))
+		return -NLE_NOMEM;
+
+	if (prio > 7)
+		return -NLE_RANGE;
+
+	v->v_prio = prio;
+	v->v_flags |= VLAN_F_PRIO;
+
+	return 0;
+}
+
+/**
+ * Get vlan prio
+ * @arg act             vlan action
+ * @arg out_prio        the output vlan prio
+ * @return 0 if the vlan prio was returned or a negative error code.
+*/
+int rtnl_vlan_get_vlan_prio(struct rtnl_act *act, uint8_t *out_prio)
+{
+	struct rtnl_vlan *v;
+
+	if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act))))
+		return -NLE_INVAL;
+
+	if (!(v->v_flags & VLAN_F_PRIO))
+		return -NLE_MISSING_ATTR;
+
+	*out_prio = v->v_prio;
+	return 0;
+}
+
+/** @} */
+
+static struct rtnl_tc_ops vlan_ops = {
+	.to_kind                = "vlan",
+	.to_type                = RTNL_TC_TYPE_ACT,
+	.to_size                = sizeof(struct rtnl_vlan),
+	.to_msg_parser          = vlan_msg_parser,
+	.to_free_data           = vlan_free_data,
+	.to_clone               = vlan_clone,
+	.to_msg_fill            = vlan_msg_fill,
+	.to_dump = {
+	    [NL_DUMP_LINE]      = vlan_dump_line,
+	    [NL_DUMP_DETAILS]   = vlan_dump_details,
+	},
+};
+
+static void __init vlan_init(void)
+{
+	rtnl_tc_register(&vlan_ops);
+}
+
+static void __exit vlan_exit(void)
+{
+	rtnl_tc_unregister(&vlan_ops);
+}
+
+/** @} */
diff --git a/lib/route/addr.c b/lib/route/addr.c
index e6e91d2..f65e7e9 100644
--- a/lib/route/addr.c
+++ b/lib/route/addr.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/addr.c		Addresses
  *
@@ -241,34 +242,69 @@
 		addr->ce_mask |= ADDR_ATTR_CACHEINFO;
 	}
 
-	if (tb[IFA_LOCAL]) {
-		addr->a_local = nl_addr_alloc_attr(tb[IFA_LOCAL], family);
+	if (family == AF_INET) {
+		uint32_t null = 0;
+
+		/* for IPv4/AF_INET, kernel always sets IFA_LOCAL and IFA_ADDRESS, unless it
+		 * is effectively 0.0.0.0. */
+		if (tb[IFA_LOCAL])
+			addr->a_local = nl_addr_alloc_attr(tb[IFA_LOCAL], family);
+		else
+			addr->a_local = nl_addr_build(family, &null, sizeof (null));
 		if (!addr->a_local)
 			goto errout_nomem;
 		addr->ce_mask |= ADDR_ATTR_LOCAL;
-		plen_addr = addr->a_local;
-	}
 
-	if (tb[IFA_ADDRESS]) {
-		struct nl_addr *a;
-
-		a = nl_addr_alloc_attr(tb[IFA_ADDRESS], family);
-		if (!a)
+		if (tb[IFA_ADDRESS])
+			addr->a_peer = nl_addr_alloc_attr(tb[IFA_ADDRESS], family);
+		else
+			addr->a_peer = nl_addr_build(family, &null, sizeof (null));
+		if (!addr->a_peer)
 			goto errout_nomem;
 
-		/* IPv6 sends the local address as IFA_ADDRESS with
-		 * no IFA_LOCAL, IPv4 sends both IFA_LOCAL and IFA_ADDRESS
-		 * with IFA_ADDRESS being the peer address if they differ */
-		if (!tb[IFA_LOCAL] || !nl_addr_cmp(a, addr->a_local)) {
-			nl_addr_put(addr->a_local);
-			addr->a_local = a;
-			addr->ce_mask |= ADDR_ATTR_LOCAL;
-		} else {
-			addr->a_peer = a;
+		if (!nl_addr_cmp (addr->a_local, addr->a_peer)) {
+			/* having IFA_ADDRESS equal to IFA_LOCAL does not really mean
+			 * there is no peer. It means the peer is equal to the local address,
+			 * which is the case for "normal" addresses.
+			 *
+			 * Still, clear the peer and pretend it is unset for backward
+			 * compatibility. */
+			nl_addr_put(addr->a_peer);
+			addr->a_peer = NULL;
+		} else
 			addr->ce_mask |= ADDR_ATTR_PEER;
+
+		plen_addr = addr->a_local;
+	} else {
+		if (tb[IFA_LOCAL]) {
+			addr->a_local = nl_addr_alloc_attr(tb[IFA_LOCAL], family);
+			if (!addr->a_local)
+				goto errout_nomem;
+			addr->ce_mask |= ADDR_ATTR_LOCAL;
+			plen_addr = addr->a_local;
 		}
 
-		plen_addr = a;
+		if (tb[IFA_ADDRESS]) {
+			struct nl_addr *a;
+
+			a = nl_addr_alloc_attr(tb[IFA_ADDRESS], family);
+			if (!a)
+				goto errout_nomem;
+
+			/* IPv6 sends the local address as IFA_ADDRESS with
+			 * no IFA_LOCAL, IPv4 sends both IFA_LOCAL and IFA_ADDRESS
+			 * with IFA_ADDRESS being the peer address if they differ */
+			if (!tb[IFA_LOCAL] || !nl_addr_cmp(a, addr->a_local)) {
+				nl_addr_put(addr->a_local);
+				addr->a_local = a;
+				addr->ce_mask |= ADDR_ATTR_LOCAL;
+			} else {
+				addr->a_peer = a;
+				addr->ce_mask |= ADDR_ATTR_PEER;
+			}
+
+			plen_addr = a;
+		}
 	}
 
 	if (plen_addr)
@@ -429,12 +465,33 @@
 	addr_dump_details(obj, p);
 }
 
-static int addr_compare(struct nl_object *_a, struct nl_object *_b,
-			uint32_t attrs, int flags)
+static uint32_t addr_id_attrs_get(struct nl_object *obj)
+{
+	struct rtnl_addr *addr = (struct rtnl_addr *)obj;
+	uint32_t rv;
+
+	switch (addr->a_family) {
+	case AF_INET:
+		rv = (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX |
+		      ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN);
+		if (addr->a_peer)
+			rv |= ADDR_ATTR_PEER;
+		return rv;
+	case AF_INET6:
+		return (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX |
+		        ADDR_ATTR_LOCAL);
+	default:
+		return (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX |
+		        ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN);
+	}
+}
+
+static uint64_t addr_compare(struct nl_object *_a, struct nl_object *_b,
+			     uint64_t attrs, int flags)
 {
 	struct rtnl_addr *a = (struct rtnl_addr *) _a;
 	struct rtnl_addr *b = (struct rtnl_addr *) _b;
-	int diff = 0;
+	uint64_t diff = 0;
 
 #define ADDR_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ADDR_ATTR_##ATTR, a, b, EXPR)
 
@@ -442,12 +499,27 @@
 	diff |= ADDR_DIFF(FAMILY,	a->a_family != b->a_family);
 	diff |= ADDR_DIFF(SCOPE,	a->a_scope != b->a_scope);
 	diff |= ADDR_DIFF(LABEL,	strcmp(a->a_label, b->a_label));
-	diff |= ADDR_DIFF(PEER,		nl_addr_cmp(a->a_peer, b->a_peer));
+	if (attrs & ADDR_ATTR_PEER) {
+		if (   (flags & ID_COMPARISON)
+		    && a->a_family == AF_INET
+		    && b->a_family == AF_INET
+		    && a->a_peer
+		    && b->a_peer
+		    && a->a_prefixlen == b->a_prefixlen) {
+			/* when comparing two IPv4 addresses for id-equality, the network part
+			 * of the PEER address shall be compared.
+			 */
+			diff |= ADDR_DIFF(PEER, nl_addr_cmp_prefix(a->a_peer, b->a_peer));
+		} else
+			diff |= ADDR_DIFF(PEER, nl_addr_cmp(a->a_peer, b->a_peer));
+	}
 	diff |= ADDR_DIFF(LOCAL,	nl_addr_cmp(a->a_local, b->a_local));
 	diff |= ADDR_DIFF(MULTICAST,	nl_addr_cmp(a->a_multicast,
 						    b->a_multicast));
 	diff |= ADDR_DIFF(BROADCAST,	nl_addr_cmp(a->a_bcast, b->a_bcast));
 	diff |= ADDR_DIFF(ANYCAST,	nl_addr_cmp(a->a_anycast, b->a_anycast));
+	diff |= ADDR_DIFF(CACHEINFO,    memcmp(&a->a_cacheinfo, &b->a_cacheinfo,
+	                                       sizeof (a->a_cacheinfo)));
 
 	if (flags & LOOSE_COMPARISON)
 		diff |= ADDR_DIFF(FLAGS,
@@ -461,17 +533,17 @@
 }
 
 static const struct trans_tbl addr_attrs[] = {
-	__ADD(ADDR_ATTR_FAMILY, family)
-	__ADD(ADDR_ATTR_PREFIXLEN, prefixlen)
-	__ADD(ADDR_ATTR_FLAGS, flags)
-	__ADD(ADDR_ATTR_SCOPE, scope)
-	__ADD(ADDR_ATTR_IFINDEX, ifindex)
-	__ADD(ADDR_ATTR_LABEL, label)
-	__ADD(ADDR_ATTR_CACHEINFO, cacheinfo)
-	__ADD(ADDR_ATTR_PEER, peer)
-	__ADD(ADDR_ATTR_LOCAL, local)
-	__ADD(ADDR_ATTR_BROADCAST, broadcast)
-	__ADD(ADDR_ATTR_MULTICAST, multicast)
+	__ADD(ADDR_ATTR_FAMILY, family),
+	__ADD(ADDR_ATTR_PREFIXLEN, prefixlen),
+	__ADD(ADDR_ATTR_FLAGS, flags),
+	__ADD(ADDR_ATTR_SCOPE, scope),
+	__ADD(ADDR_ATTR_IFINDEX, ifindex),
+	__ADD(ADDR_ATTR_LABEL, label),
+	__ADD(ADDR_ATTR_CACHEINFO, cacheinfo),
+	__ADD(ADDR_ATTR_PEER, peer),
+	__ADD(ADDR_ATTR_LOCAL, local),
+	__ADD(ADDR_ATTR_BROADCAST, broadcast),
+	__ADD(ADDR_ATTR_MULTICAST, multicast),
 };
 
 static char *addr_attrs2str(int attrs, char *buf, size_t len)
@@ -1061,15 +1133,15 @@
  */
 
 static const struct trans_tbl addr_flags[] = {
-	__ADD(IFA_F_SECONDARY, secondary)
-	__ADD(IFA_F_NODAD, nodad)
-	__ADD(IFA_F_OPTIMISTIC, optimistic)
-	__ADD(IFA_F_HOMEADDRESS, homeaddress)
-	__ADD(IFA_F_DEPRECATED, deprecated)
-	__ADD(IFA_F_TENTATIVE, tentative)
-	__ADD(IFA_F_PERMANENT, permanent)
-	__ADD(IFA_F_MANAGETEMPADDR, mngtmpaddr)
-	__ADD(IFA_F_NOPREFIXROUTE, noprefixroute)
+	__ADD(IFA_F_SECONDARY, secondary),
+	__ADD(IFA_F_NODAD, nodad),
+	__ADD(IFA_F_OPTIMISTIC, optimistic),
+	__ADD(IFA_F_HOMEADDRESS, homeaddress),
+	__ADD(IFA_F_DEPRECATED, deprecated),
+	__ADD(IFA_F_TENTATIVE, tentative),
+	__ADD(IFA_F_PERMANENT, permanent),
+	__ADD(IFA_F_MANAGETEMPADDR, mngtmpaddr),
+	__ADD(IFA_F_NOPREFIXROUTE, noprefixroute),
 };
 
 char *rtnl_addr_flags2str(int flags, char *buf, size_t size)
@@ -1098,6 +1170,7 @@
 	},
 	.oo_compare		= addr_compare,
 	.oo_attrs2str		= addr_attrs2str,
+	.oo_id_attrs_get	= addr_id_attrs_get,
 	.oo_id_attrs		= (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX |
 				   ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN),
 };
diff --git a/lib/route/class.c b/lib/route/class.c
index 56ad1d8..d164112 100644
--- a/lib/route/class.c
+++ b/lib/route/class.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/class.c            Traffic Classes
  *
@@ -367,6 +368,38 @@
 	return NULL;
 }
 
+/**
+ * Search class by interface index and parent
+ * @arg cache		Traffic class cache
+ * @arg ifindex		Interface index
+ * @arg parent		Handle of parent qdisc
+ *
+ * Searches a class cache previously allocated with rtnl_class_alloc_cache()
+ * and searches for a class matching the interface index and parent qdisc.
+ *
+ * The reference counter is incremented before returning the class, therefore
+ * the reference must be given back with rtnl_class_put() after usage.
+ *
+ * @return pointer to class inside the cache or NULL if no match was found.
+ */
+struct rtnl_class *rtnl_class_get_by_parent(struct nl_cache *cache, int ifindex,
+					    uint32_t parent)
+{
+	struct rtnl_class *class;
+
+	if (cache->c_ops != &rtnl_class_ops)
+		return NULL;
+
+	nl_list_for_each_entry(class, &cache->c_items, ce_list) {
+		if (class->c_parent == parent && class->c_ifindex == ifindex) {
+			nl_object_get((struct nl_object *) class);
+			return class;
+		}
+	}
+
+	return NULL;
+}
+
 /** @} */
 
 /**
diff --git a/lib/route/classid.c b/lib/route/classid.c
index f2d3a01..9dcf993 100644
--- a/lib/route/classid.c
+++ b/lib/route/classid.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/classid.c       ClassID Management
  *
@@ -328,7 +329,7 @@
 		}
 	}
 
-	if (!(fd = fopen(path, "r"))) {
+	if (!(fd = fopen(path, "re"))) {
 		err = -nl_syserr2nlerr(errno);
 		goto errout;
 	}
@@ -402,7 +403,7 @@
 	if (build_sysconf_path(&path, "classid") < 0)
 		return -NLE_NOMEM;
 
-	if (!(fd = fopen(path, "a"))) {
+	if (!(fd = fopen(path, "ae"))) {
 		err = -nl_syserr2nlerr(errno);
 		goto errout;
 	}
@@ -444,10 +445,11 @@
 		NL_DBG(1, "Failed to read classid file: %s\n", nl_geterror(err));
 }
 
-static void free_map(void *map) {
+static void free_map(void *map)
+{
 	free(((struct classid_map *)map)->name);
 	free(map);
-};
+}
 
 static void __exit classid_exit(void)
 {
diff --git a/lib/route/cls.c b/lib/route/cls.c
index 649a7d0..fa87cd4 100644
--- a/lib/route/cls.c
+++ b/lib/route/cls.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/classifier.c       Classifier
  *
@@ -324,7 +325,8 @@
  *
  * @return 0 on success or a negative error code.
  */
-int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent,			 struct nl_cache **result)
+int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent,
+			 struct nl_cache **result)
 {
 	struct nl_cache * cache;
 	int err;
@@ -344,6 +346,24 @@
 	return 0;
 }
 
+/**
+ * Set interface index and parent handle for classifier cache.
+ * @arg cache 		Pointer to cache
+ * @arg parent 		Parent qdisc/traffic class class
+ *
+ * Set the interface index and parent handle of a classifier cache.
+ * This is useful for reusing some existed classifier cache to reduce
+ * the overhead introduced by memory allocation.
+ *
+ * @return void.
+ */
+void rtnl_cls_cache_set_tc_params(struct nl_cache *cache,
+				  int ifindex, uint32_t parent)
+{
+	cache->c_iarg1 = ifindex;
+	cache->c_iarg2 = parent;
+}
+
 /** @} */
 
 static void cls_dump_line(struct rtnl_tc *tc, struct nl_dump_params *p)
diff --git a/lib/route/cls/basic.c b/lib/route/cls/basic.c
index 6af3844..3581c60 100644
--- a/lib/route/cls/basic.c
+++ b/lib/route/cls/basic.c
@@ -220,6 +220,7 @@
 int rtnl_basic_add_action(struct rtnl_cls *cls, struct rtnl_act *act)
 {
 	struct rtnl_basic *b;
+	int err;
 
 	if (!act)
 		return 0;
@@ -228,9 +229,25 @@
 		return -NLE_NOMEM;
 
 	b->b_mask |= BASIC_ATTR_ACTION;
+	if ((err = rtnl_act_append(&b->b_act, act)))
+		return err;
+
 	/* In case user frees it */
 	rtnl_act_get(act);
-	return rtnl_act_append(&b->b_act, act);
+	return 0;
+}
+
+struct rtnl_act* rtnl_basic_get_action(struct rtnl_cls *cls)
+{
+	struct rtnl_basic *b;
+
+	if (!(b = rtnl_tc_data_peek(TC_CAST(cls))))
+		return NULL;
+
+	if (!(b->b_mask & BASIC_ATTR_ACTION))
+		return NULL;
+
+	return b->b_act;
 }
 
 int rtnl_basic_del_action(struct rtnl_cls *cls, struct rtnl_act *act)
diff --git a/lib/route/cls/cgroup.c b/lib/route/cls/cgroup.c
index c5b7561..b145261 100644
--- a/lib/route/cls/cgroup.c
+++ b/lib/route/cls/cgroup.c
@@ -34,9 +34,22 @@
 	[TCA_CGROUP_EMATCHES]	= { .type = NLA_NESTED },
 };
 
-static int cgroup_clone(void *dst, void *src)
+static int cgroup_clone(void *_dst, void *_src)
 {
-	return -NLE_OPNOTSUPP;
+	struct rtnl_cgroup *dst = NULL, *src = _src;
+
+	dst = calloc(1, sizeof(*dst));
+	if (!dst)
+		return -NLE_NOMEM;
+
+	dst->cg_mask = src->cg_mask;
+	dst->cg_ematch = rtnl_ematch_tree_clone(src->cg_ematch);
+	if (!dst) {
+		free(dst);
+		return -NLE_NOMEM;
+	}
+
+	return 0;
 }
 
 static void cgroup_free_data(struct rtnl_tc *tc, void *data)
diff --git a/lib/route/cls/ematch.c b/lib/route/cls/ematch.c
index 6cbe274..18f5be9 100644
--- a/lib/route/cls/ematch.c
+++ b/lib/route/cls/ematch.c
@@ -22,6 +22,7 @@
 #include <netlink/route/classifier.h>
 #include <netlink/route/cls/ematch.h>
 #include <netlink/route/cls/ematch/cmp.h>
+#include <linux/tc_ematch/tc_em_cmp.h>
 
 #include "ematch_syntax.h"
 #include "ematch_grammar.h"
@@ -288,6 +289,63 @@
 	free(tree);
 }
 
+static int clone_ematch_list(struct nl_list_head *dst, struct nl_list_head *src)
+{
+	struct rtnl_ematch *new = NULL, *pos = NULL;
+
+	nl_list_for_each_entry(pos, src, e_list) {
+		new = rtnl_ematch_alloc();
+		if (!new)
+			goto nomem;
+
+		new->e_id      = pos->e_id;
+		new->e_kind    = pos->e_kind;
+		new->e_flags   = pos->e_flags;
+		new->e_index   = pos->e_index;
+		new->e_datalen = pos->e_datalen;
+
+		if (pos->e_ops) {
+			if (rtnl_ematch_set_ops(new, pos->e_ops))
+				goto nomem;
+		}
+
+		if (!nl_list_empty(&pos->e_childs)) {
+			if (clone_ematch_list(&new->e_childs, &pos->e_childs) < 0)
+				goto nomem;
+		}
+		nl_list_add_tail(&new->e_list, dst);
+	}
+
+	return 0;
+
+nomem:
+	if (new)
+		free(new);
+	free_ematch_list(dst);
+	return -NLE_NOMEM;
+}
+
+/**
+ * Clone ematch tree object
+ * @arg src		ematch tree object
+ *
+ * This function clones the ematch tree and all ematches attached to it.
+ */
+struct rtnl_ematch_tree *rtnl_ematch_tree_clone(struct rtnl_ematch_tree *src)
+{
+	struct rtnl_ematch_tree *dst = NULL;
+
+	if (!src)
+		return NULL;
+
+	if (!(dst = rtnl_ematch_tree_alloc(src->et_progid)))
+		return NULL;
+
+	clone_ematch_list(&dst->et_list, &src->et_list);
+
+	return dst;
+}
+
 /**
  * Add ematch object to the end of the ematch tree
  * @arg tree		ematch tree object
@@ -407,7 +465,7 @@
 		}
 
 		hdr = nla_data(a);
-		data = nla_data(a) + NLA_ALIGN(sizeof(*hdr));
+		data = (char *) nla_data(a) + NLA_ALIGN(sizeof(*hdr));
 		len = nla_len(a) - NLA_ALIGN(sizeof(*hdr));
 
 		NL_DBG(3, "ematch attribute matchid=%u, kind=%u, flags=%u\n",
diff --git a/lib/route/cls/ematch/cmp.c b/lib/route/cls/ematch/cmp.c
index 2997cdb..2e380c3 100644
--- a/lib/route/cls/ematch/cmp.c
+++ b/lib/route/cls/ematch/cmp.c
@@ -20,6 +20,7 @@
 #include <netlink-private/tc.h>
 #include <netlink/netlink.h>
 #include <netlink/route/cls/ematch.h>
+#include <netlink/route/cls/ematch/cmp.h>
 #include <linux/tc_ematch/tc_em_cmp.h>
 
 void rtnl_ematch_cmp_set(struct rtnl_ematch *e, struct tcf_em_cmp *cfg)
diff --git a/lib/route/cls/ematch/meta.c b/lib/route/cls/ematch/meta.c
index 44f11b9..a26ed4c 100644
--- a/lib/route/cls/ematch/meta.c
+++ b/lib/route/cls/ematch/meta.c
@@ -21,6 +21,7 @@
 #include <netlink/netlink.h>
 #include <netlink/route/cls/ematch.h>
 #include <netlink/route/cls/ematch/meta.h>
+#include <linux/tc_ematch/tc_em_meta.h>
 
 struct rtnl_meta_value
 {
@@ -51,7 +52,8 @@
 	value->mv_shift = shift;
 	value->mv_len = len;
 
-	memcpy(value + 1, data, len);
+	if (len)
+		memcpy(value + 1, data, len);
 
 	return value;
 }
@@ -165,51 +167,51 @@
 }
 
 static const struct trans_tbl meta_int[] = {
-	__ADD(TCF_META_ID_RANDOM, random)
-	__ADD(TCF_META_ID_LOADAVG_0, loadavg_0)
-	__ADD(TCF_META_ID_LOADAVG_1, loadavg_1)
-	__ADD(TCF_META_ID_LOADAVG_2, loadavg_2)
-	__ADD(TCF_META_ID_DEV, dev)
-	__ADD(TCF_META_ID_PRIORITY, prio)
-	__ADD(TCF_META_ID_PROTOCOL, proto)
-	__ADD(TCF_META_ID_PKTTYPE, pkttype)
-	__ADD(TCF_META_ID_PKTLEN, pktlen)
-	__ADD(TCF_META_ID_DATALEN, datalen)
-	__ADD(TCF_META_ID_MACLEN, maclen)
-	__ADD(TCF_META_ID_NFMARK, mark)
-	__ADD(TCF_META_ID_TCINDEX, tcindex)
-	__ADD(TCF_META_ID_RTCLASSID, rtclassid)
-	__ADD(TCF_META_ID_RTIIF, rtiif)
-	__ADD(TCF_META_ID_SK_FAMILY, sk_family)
-	__ADD(TCF_META_ID_SK_STATE, sk_state)
-	__ADD(TCF_META_ID_SK_REUSE, sk_reuse)
-	__ADD(TCF_META_ID_SK_REFCNT, sk_refcnt)
-	__ADD(TCF_META_ID_SK_RCVBUF, sk_rcvbuf)
-	__ADD(TCF_META_ID_SK_SNDBUF, sk_sndbuf)
-	__ADD(TCF_META_ID_SK_SHUTDOWN, sk_sutdown)
-	__ADD(TCF_META_ID_SK_PROTO, sk_proto)
-	__ADD(TCF_META_ID_SK_TYPE, sk_type)
-	__ADD(TCF_META_ID_SK_RMEM_ALLOC, sk_rmem_alloc)
-	__ADD(TCF_META_ID_SK_WMEM_ALLOC, sk_wmem_alloc)
-	__ADD(TCF_META_ID_SK_WMEM_QUEUED, sk_wmem_queued)
-	__ADD(TCF_META_ID_SK_RCV_QLEN, sk_rcv_qlen)
-	__ADD(TCF_META_ID_SK_SND_QLEN, sk_snd_qlen)
-	__ADD(TCF_META_ID_SK_ERR_QLEN, sk_err_qlen)
-	__ADD(TCF_META_ID_SK_FORWARD_ALLOCS, sk_forward_allocs)
-	__ADD(TCF_META_ID_SK_ALLOCS, sk_allocs)
-	__ADD(TCF_META_ID_SK_ROUTE_CAPS, sk_route_caps)
-	__ADD(TCF_META_ID_SK_HASH, sk_hash)
-	__ADD(TCF_META_ID_SK_LINGERTIME, sk_lingertime)
-	__ADD(TCF_META_ID_SK_ACK_BACKLOG, sk_ack_backlog)
-	__ADD(TCF_META_ID_SK_MAX_ACK_BACKLOG, sk_max_ack_backlog)
-	__ADD(TCF_META_ID_SK_PRIO, sk_prio)
-	__ADD(TCF_META_ID_SK_RCVLOWAT, sk_rcvlowat)
-	__ADD(TCF_META_ID_SK_RCVTIMEO, sk_rcvtimeo)
-	__ADD(TCF_META_ID_SK_SNDTIMEO, sk_sndtimeo)
-	__ADD(TCF_META_ID_SK_SENDMSG_OFF, sk_sendmsg_off)
-	__ADD(TCF_META_ID_SK_WRITE_PENDING, sk_write_pending)
-	__ADD(TCF_META_ID_VLAN_TAG, vlan)
-	__ADD(TCF_META_ID_RXHASH, rxhash)
+	__ADD(TCF_META_ID_RANDOM, random),
+	__ADD(TCF_META_ID_LOADAVG_0, loadavg_0),
+	__ADD(TCF_META_ID_LOADAVG_1, loadavg_1),
+	__ADD(TCF_META_ID_LOADAVG_2, loadavg_2),
+	__ADD(TCF_META_ID_DEV, dev),
+	__ADD(TCF_META_ID_PRIORITY, prio),
+	__ADD(TCF_META_ID_PROTOCOL, proto),
+	__ADD(TCF_META_ID_PKTTYPE, pkttype),
+	__ADD(TCF_META_ID_PKTLEN, pktlen),
+	__ADD(TCF_META_ID_DATALEN, datalen),
+	__ADD(TCF_META_ID_MACLEN, maclen),
+	__ADD(TCF_META_ID_NFMARK, mark),
+	__ADD(TCF_META_ID_TCINDEX, tcindex),
+	__ADD(TCF_META_ID_RTCLASSID, rtclassid),
+	__ADD(TCF_META_ID_RTIIF, rtiif),
+	__ADD(TCF_META_ID_SK_FAMILY, sk_family),
+	__ADD(TCF_META_ID_SK_STATE, sk_state),
+	__ADD(TCF_META_ID_SK_REUSE, sk_reuse),
+	__ADD(TCF_META_ID_SK_REFCNT, sk_refcnt),
+	__ADD(TCF_META_ID_SK_RCVBUF, sk_rcvbuf),
+	__ADD(TCF_META_ID_SK_SNDBUF, sk_sndbuf),
+	__ADD(TCF_META_ID_SK_SHUTDOWN, sk_sutdown),
+	__ADD(TCF_META_ID_SK_PROTO, sk_proto),
+	__ADD(TCF_META_ID_SK_TYPE, sk_type),
+	__ADD(TCF_META_ID_SK_RMEM_ALLOC, sk_rmem_alloc),
+	__ADD(TCF_META_ID_SK_WMEM_ALLOC, sk_wmem_alloc),
+	__ADD(TCF_META_ID_SK_WMEM_QUEUED, sk_wmem_queued),
+	__ADD(TCF_META_ID_SK_RCV_QLEN, sk_rcv_qlen),
+	__ADD(TCF_META_ID_SK_SND_QLEN, sk_snd_qlen),
+	__ADD(TCF_META_ID_SK_ERR_QLEN, sk_err_qlen),
+	__ADD(TCF_META_ID_SK_FORWARD_ALLOCS, sk_forward_allocs),
+	__ADD(TCF_META_ID_SK_ALLOCS, sk_allocs),
+	__ADD(__TCF_META_ID_SK_ROUTE_CAPS, sk_route_caps),
+	__ADD(TCF_META_ID_SK_HASH, sk_hash),
+	__ADD(TCF_META_ID_SK_LINGERTIME, sk_lingertime),
+	__ADD(TCF_META_ID_SK_ACK_BACKLOG, sk_ack_backlog),
+	__ADD(TCF_META_ID_SK_MAX_ACK_BACKLOG, sk_max_ack_backlog),
+	__ADD(TCF_META_ID_SK_PRIO, sk_prio),
+	__ADD(TCF_META_ID_SK_RCVLOWAT, sk_rcvlowat),
+	__ADD(TCF_META_ID_SK_RCVTIMEO, sk_rcvtimeo),
+	__ADD(TCF_META_ID_SK_SNDTIMEO, sk_sndtimeo),
+	__ADD(TCF_META_ID_SK_SENDMSG_OFF, sk_sendmsg_off),
+	__ADD(TCF_META_ID_SK_WRITE_PENDING, sk_write_pending),
+	__ADD(TCF_META_ID_VLAN_TAG, vlan),
+	__ADD(TCF_META_ID_RXHASH, rxhash),
 };
 
 static char *int_id2str(int id, char *buf, size_t size)
@@ -218,8 +220,8 @@
 }
 
 static const struct trans_tbl meta_var[] = {
-	__ADD(TCF_META_ID_DEV,devname)
-	__ADD(TCF_META_ID_SK_BOUND_IF,sk_bound_if)
+	__ADD(TCF_META_ID_DEV,devname),
+	__ADD(TCF_META_ID_SK_BOUND_IF,sk_bound_if),
 };
 
 static char *var_id2str(int id, char *buf, size_t size)
diff --git a/lib/route/cls/ematch/nbyte.c b/lib/route/cls/ematch/nbyte.c
index 8852489..2942c0d 100644
--- a/lib/route/cls/ematch/nbyte.c
+++ b/lib/route/cls/ematch/nbyte.c
@@ -21,6 +21,7 @@
 #include <netlink/netlink.h>
 #include <netlink/route/cls/ematch.h>
 #include <netlink/route/cls/ematch/nbyte.h>
+#include <linux/tc_ematch/tc_em_nbyte.h>
 
 struct nbyte_data
 {
@@ -93,7 +94,7 @@
 		if (!(n->pattern = calloc(1, plen)))
 			return -NLE_NOMEM;
 
-		memcpy(n->pattern, data + hdrlen, plen);
+		memcpy(n->pattern, (char *) data + hdrlen, plen);
 	}
 
 	return 0;
diff --git a/lib/route/cls/ematch/text.c b/lib/route/cls/ematch/text.c
index e8cdcae..4dcd4f0 100644
--- a/lib/route/cls/ematch/text.c
+++ b/lib/route/cls/ematch/text.c
@@ -21,6 +21,7 @@
 #include <netlink/netlink.h>
 #include <netlink/route/cls/ematch.h>
 #include <netlink/route/cls/ematch/text.h>
+#include <linux/tc_ematch/tc_em_text.h>
 
 struct text_data
 {
@@ -91,6 +92,7 @@
 	struct text_data *t = rtnl_ematch_data(e);
 
 	strncpy(t->cfg.algo, algo, sizeof(t->cfg.algo));
+	t->cfg.algo[sizeof(t->cfg.algo) - 1] = '\0';
 }
 
 char *rtnl_ematch_text_get_algo(struct rtnl_ematch *e)
@@ -115,7 +117,7 @@
 		if (!(t->pattern = calloc(1, t->cfg.pattern_len)))
 			return -NLE_NOMEM;
 
-		memcpy(t->pattern, data + hdrlen, t->cfg.pattern_len);
+		memcpy(t->pattern, (char *) data + hdrlen, t->cfg.pattern_len);
 	}
 
 	return 0;
@@ -128,7 +130,7 @@
 
 	nl_dump(p, "text(%s \"%s\"",
 		t->cfg.algo[0] ? t->cfg.algo : "no-algo",
-		t->pattern ? : "no-pattern");
+		t->pattern ? t->pattern : "no-pattern");
 
 	if (t->cfg.from_layer || t->cfg.from_offset) {
 		nl_dump(p, " from %s",
diff --git a/lib/route/cls/ematch_grammar.l b/lib/route/cls/ematch_grammar.l
index 96ef1a0..e97f9fe 100644
--- a/lib/route/cls/ematch_grammar.l
+++ b/lib/route/cls/ematch_grammar.l
@@ -15,7 +15,11 @@
  #include <netlink/netlink.h>
  #include <netlink/route/cls/ematch.h>
  #include <netlink/route/cls/ematch/cmp.h>
+ #include <linux/tc_ematch/tc_em_cmp.h>
  #include "ematch_syntax.h"
+
+ int ematch_get_column(yyscan_t);
+ void ematch_set_column(int, yyscan_t);
 %}
 
 %option 8bit
diff --git a/lib/route/cls/ematch_syntax.y b/lib/route/cls/ematch_syntax.y
index da21039..82d753d 100644
--- a/lib/route/cls/ematch_syntax.y
+++ b/lib/route/cls/ematch_syntax.y
@@ -20,6 +20,8 @@
 #include <netlink/route/cls/ematch/nbyte.h>
 #include <netlink/route/cls/ematch/text.h>
 #include <netlink/route/cls/ematch/meta.h>
+#include <linux/tc_ematch/tc_em_meta.h>
+#include <linux/tc_ematch/tc_em_cmp.h>
 
 #define META_ALLOC rtnl_meta_value_alloc_id
 #define META_ID(name) TCF_META_ID_##name
@@ -53,9 +55,9 @@
 static void yyerror(void *scanner, char **errp, struct nl_list_head *root, const char *msg)
 {
 	if (msg)
-            *errp = strdup(msg);
-        else
-	    *errp = NULL;
+		*errp = strdup(msg);
+	else
+		*errp = NULL;
 }
 %}
 
@@ -374,7 +376,7 @@
 	| META_SK_ERR_QLEN		{ $$ = META_ID(SK_ERR_QLEN); }
 	| META_SK_FORWARD_ALLOCS	{ $$ = META_ID(SK_FORWARD_ALLOCS); }
 	| META_SK_ALLOCS		{ $$ = META_ID(SK_ALLOCS); }
-	| META_SK_ROUTE_CAPS		{ $$ = META_ID(SK_ROUTE_CAPS); }
+	| META_SK_ROUTE_CAPS		{ $$ = __TCF_META_ID_SK_ROUTE_CAPS; }
 	| META_SK_HASH			{ $$ = META_ID(SK_HASH); }
 	| META_SK_LINGERTIME		{ $$ = META_ID(SK_LINGERTIME); }
 	| META_SK_ACK_BACKLOG		{ $$ = META_ID(SK_ACK_BACKLOG); }
diff --git a/lib/route/cls/mall.c b/lib/route/cls/mall.c
new file mode 100644
index 0000000..e13ee92
--- /dev/null
+++ b/lib/route/cls/mall.c
@@ -0,0 +1,305 @@
+/*
+ * lib/route/cls/mall.c		match-all classifier
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2017 Volodymyr Bendiuga <volodymyr.bendiuga@gmail.com>
+ */
+
+/**
+ * @ingroup cls
+ * @defgroup cls_mall Match-all Classifier
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/cls/matchall.h>
+#include <netlink/route/action.h>
+
+
+#define MALL_ATTR_CLASSID 0x01
+#define MALL_ATTR_FLAGS   0x02
+#define MALL_ATTR_ACTION  0x03
+
+
+static struct nla_policy mall_policy[TCA_MATCHALL_MAX + 1] = {
+	[TCA_MATCHALL_CLASSID]	= { .type = NLA_U32 },
+	[TCA_MATCHALL_FLAGS]	= { .type = NLA_U32 },
+};
+
+/**
+ * @name Attribute Modifications
+ * @{
+ */
+
+int rtnl_mall_set_classid(struct rtnl_cls *cls, uint32_t classid)
+{
+	struct rtnl_mall *mall;
+	if (!(mall = rtnl_tc_data(TC_CAST(cls))))
+		return -NLE_NOMEM;
+
+	mall->m_classid = classid;
+	mall->m_mask |= MALL_ATTR_CLASSID;
+
+	return 0;
+}
+
+int rtnl_mall_get_classid(struct rtnl_cls *cls, uint32_t *classid)
+{
+	struct rtnl_mall *mall;
+
+	if (!(mall = rtnl_tc_data_peek(TC_CAST(cls))))
+		return -NLE_INVAL;
+
+	if (!(mall->m_mask & MALL_ATTR_CLASSID))
+		return -NLE_INVAL;
+
+	*classid = mall->m_classid;
+	return 0;
+}
+
+int rtnl_mall_set_flags(struct rtnl_cls *cls, uint32_t flags)
+{
+	struct rtnl_mall *mall;
+
+	if (!(mall = rtnl_tc_data(TC_CAST(cls))))
+		return -NLE_NOMEM;
+
+	mall->m_flags = flags;
+	mall->m_mask |= MALL_ATTR_FLAGS;
+
+	return 0;
+}
+
+int rtnl_mall_get_flags(struct rtnl_cls *cls, uint32_t *flags)
+{
+	struct rtnl_mall *mall;
+
+	if (!(mall = rtnl_tc_data_peek(TC_CAST(cls))))
+		return -NLE_INVAL;
+
+	if (!(mall->m_mask & MALL_ATTR_FLAGS))
+		return -NLE_INVAL;
+
+	*flags = mall->m_flags;
+	return 0;
+}
+
+int rtnl_mall_append_action(struct rtnl_cls *cls, struct rtnl_act *act)
+{
+	struct rtnl_mall *mall;
+	int err;
+
+	if (!act)
+		return 0;
+
+	if (!(mall = rtnl_tc_data(TC_CAST(cls))))
+		return -NLE_NOMEM;
+
+	mall->m_mask |= MALL_ATTR_ACTION;
+	err = rtnl_act_append(&mall->m_act, act);
+	if (err)
+	        return err;
+
+	rtnl_act_get(act);
+	return 0;
+}
+
+struct rtnl_act *rtnl_mall_get_first_action(struct rtnl_cls *cls)
+{
+	struct rtnl_mall *mall;
+	struct rtnl_act *act;
+
+	if (!(mall = rtnl_tc_data(TC_CAST(cls))))
+		return NULL;
+
+	if (!(mall->m_mask & MALL_ATTR_ACTION))
+		return NULL;
+
+	act = mall->m_act;
+	rtnl_act_get(act);
+
+	return act;
+}
+
+int rtnl_mall_del_action(struct rtnl_cls *cls, struct rtnl_act *act)
+{
+	struct rtnl_mall *mall;
+	int ret;
+
+	if (!act)
+		return 0;
+
+	if (!(mall = rtnl_tc_data(TC_CAST(cls))))
+		return -NLE_NOMEM;
+
+	if (!(mall->m_mask & MALL_ATTR_ACTION))
+		return -NLE_INVAL;
+
+	ret = rtnl_act_remove(&mall->m_act, act);
+	if (ret < 0)
+		return ret;
+
+	rtnl_act_put(act);
+
+	return 0;
+}
+
+/** @} */
+
+static void mall_free_data(struct rtnl_tc *tc, void *data)
+{
+	struct rtnl_mall *mall = data;
+
+	if (mall->m_act)
+		rtnl_act_put_all(&mall->m_act);
+}
+
+static int mall_msg_parser(struct rtnl_tc *tc, void *data)
+{
+	struct rtnl_mall *mall = data;
+	struct nlattr *tb[TCA_MATCHALL_MAX + 1];
+	int err;
+
+	err = tca_parse(tb, TCA_MATCHALL_MAX, tc, mall_policy);
+	if (err < 0)
+		return err;
+
+	if (tb[TCA_MATCHALL_CLASSID]) {
+		mall->m_classid = nla_get_u32(tb[TCA_MATCHALL_CLASSID]);
+		mall->m_mask |= MALL_ATTR_CLASSID;
+	}
+
+	if (tb[TCA_MATCHALL_FLAGS]) {
+		mall->m_flags = nla_get_u32(tb[TCA_MATCHALL_FLAGS]);
+		mall->m_mask |= MALL_ATTR_FLAGS;
+	}
+
+	if (tb[TCA_MATCHALL_ACT]) {
+		mall->m_mask |= MALL_ATTR_ACTION;
+		err = rtnl_act_parse(&mall->m_act, tb[TCA_MATCHALL_ACT]);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int mall_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
+{
+	struct rtnl_mall *mall = data;
+
+	if (!mall)
+		return 0;
+
+	if (mall->m_mask & MALL_ATTR_CLASSID)
+		NLA_PUT_U32(msg, TCA_MATCHALL_CLASSID, mall->m_classid);
+
+	if (mall->m_mask & MALL_ATTR_FLAGS)
+		NLA_PUT_U32(msg, TCA_MATCHALL_FLAGS, mall->m_flags);
+
+	if (mall->m_mask & MALL_ATTR_ACTION) {
+		int err;
+
+		err = rtnl_act_fill(msg, TCA_MATCHALL_ACT, mall->m_act);
+		if (err)
+			return err;
+	}
+
+	return 0;
+
+ nla_put_failure:
+	return -NLE_NOMEM;
+}
+
+static int mall_clone(void *_dst, void *_src)
+{
+	struct rtnl_mall *dst = _dst, *src = _src;
+	struct rtnl_act *next, *new;
+	int err;
+
+	if (src->m_act) {
+		if (!(dst->m_act = rtnl_act_alloc()))
+			return -NLE_NOMEM;
+
+		/* action nl list next and prev pointers must be updated */
+		nl_init_list_head(&dst->m_act->ce_list);
+
+		memcpy(dst->m_act, src->m_act, sizeof(struct rtnl_act));
+		next = rtnl_act_next(src->m_act);
+		while (next) {
+			new = (struct rtnl_act *) nl_object_clone((struct nl_object *) next);
+			if (!new)
+				return -NLE_NOMEM;
+
+			err = rtnl_act_append(&dst->m_act, new);
+			if (err < 0)
+				return err;
+
+			next = rtnl_act_next(next);
+		}
+	}
+
+	return 0;
+}
+
+static void mall_dump_line(struct rtnl_tc *tc, void *data,
+			   struct nl_dump_params *p)
+{
+	struct rtnl_mall *mall = data;
+	char buf[32];
+
+	if (!mall)
+		return;
+
+	if (mall->m_mask & MALL_ATTR_CLASSID)
+		nl_dump(p, " target %s",
+			rtnl_tc_handle2str(mall->m_classid, buf, sizeof(buf)));
+}
+
+static void mall_dump_details(struct rtnl_tc *tc, void *data,
+			      struct nl_dump_params *p)
+{
+	struct rtnl_mall *mall = data;
+
+	if (!mall)
+		return;
+
+	nl_dump(p, "no details for match-all");
+}
+
+static struct rtnl_tc_ops mall_ops = {
+	.to_kind		= "matchall",
+	.to_type		= RTNL_TC_TYPE_CLS,
+	.to_size		= sizeof(struct rtnl_mall),
+	.to_msg_parser		= mall_msg_parser,
+	.to_free_data		= mall_free_data,
+	.to_clone		= mall_clone,
+	.to_msg_fill		= mall_msg_fill,
+	.to_dump = {
+	    [NL_DUMP_LINE]	= mall_dump_line,
+	    [NL_DUMP_DETAILS]	= mall_dump_details,
+	},
+};
+
+static void __init mall_init(void)
+{
+	rtnl_tc_register(&mall_ops);
+}
+
+static void __exit mall_exit(void)
+{
+	rtnl_tc_unregister(&mall_ops);
+}
+
+/** @} */
diff --git a/lib/route/cls/police.c b/lib/route/cls/police.c
index 1f5d284..14b5608 100644
--- a/lib/route/cls/police.c
+++ b/lib/route/cls/police.c
@@ -23,12 +23,12 @@
  */
 
 static const struct trans_tbl police_types[] = {
-	__ADD(TC_POLICE_UNSPEC,unspec)
-	__ADD(TC_POLICE_OK,ok)
-	__ADD(TC_POLICE_RECLASSIFY,reclassify)
-	__ADD(TC_POLICE_SHOT,shot)
+	__ADD(TC_POLICE_UNSPEC,unspec),
+	__ADD(TC_POLICE_OK,ok),
+	__ADD(TC_POLICE_RECLASSIFY,reclassify),
+	__ADD(TC_POLICE_SHOT,shot),
 #ifdef TC_POLICE_PIPE
-	__ADD(TC_POLICE_PIPE,pipe)
+	__ADD(TC_POLICE_PIPE,pipe),
 #endif
 };
 
diff --git a/lib/route/cls/u32.c b/lib/route/cls/u32.c
index 0a4e83c..f06bc24 100644
--- a/lib/route/cls/u32.c
+++ b/lib/route/cls/u32.c
@@ -38,6 +38,7 @@
 #define U32_ATTR_ACTION       0x040
 #define U32_ATTR_POLICE       0x080
 #define U32_ATTR_INDEV        0x100
+#define U32_ATTR_MARK	      0x200
 /** @endcond */
 
 static inline struct tc_u32_sel *u32_selector(struct rtnl_u32 *u)
@@ -53,6 +54,14 @@
 	return u32_selector(u);
 }
 
+static inline struct tc_u32_mark *u32_mark_alloc(struct rtnl_u32 *u)
+{
+	if (!u->cu_mark)
+		u->cu_mark = nl_data_alloc(NULL, sizeof(struct tc_u32_mark));
+
+	return (struct tc_u32_mark *) u->cu_mark->d_data;
+}
+
 static struct nla_policy u32_policy[TCA_U32_MAX+1] = {
 	[TCA_U32_DIVISOR]	= { .type = NLA_U32 },
 	[TCA_U32_HASH]		= { .type = NLA_U32 },
@@ -62,6 +71,7 @@
 				    .maxlen = IFNAMSIZ },
 	[TCA_U32_SEL]		= { .minlen = sizeof(struct tc_u32_sel) },
 	[TCA_U32_PCNT]		= { .minlen = sizeof(struct tc_u32_pcnt) },
+	[TCA_U32_MARK]		= { .minlen = sizeof(struct tc_u32_mark) }
 };
 
 static int u32_msg_parser(struct rtnl_tc *tc, void *data)
@@ -86,6 +96,13 @@
 		u->cu_mask |= U32_ATTR_SELECTOR;
 	}
 
+	if (tb[TCA_U32_MARK]) {
+		u->cu_mark = nl_data_alloc_attr(tb[TCA_U32_MARK]);
+		if (!u->cu_mark)
+			goto errout_nomem;
+		u->cu_mask |= U32_ATTR_MARK;
+	}
+
 	if (tb[TCA_U32_HASH]) {
 		u->cu_hash = nla_get_u32(tb[TCA_U32_HASH]);
 		u->cu_mask |= U32_ATTR_HASH;
@@ -123,7 +140,7 @@
 			err = -NLE_MISSING_ATTR;
 			goto errout;
 		}
-		
+
 		sel = u->cu_selector->d_data;
 		pcnt_size = sizeof(struct tc_u32_pcnt) +
 				(sel->nkeys * sizeof(uint64_t));
@@ -157,6 +174,7 @@
 
 	if (u->cu_act)
 		rtnl_act_put_all(&u->cu_act);
+	nl_data_free(u->cu_mark);
 	nl_data_free(u->cu_selector);
 	nl_data_free(u->cu_police);
 	nl_data_free(u->cu_pcnt);
@@ -170,6 +188,10 @@
 	    !(dst->cu_selector = nl_data_clone(src->cu_selector)))
 		return -NLE_NOMEM;
 
+	if (src->cu_mark &&
+	    !(dst->cu_mark = nl_data_clone(src->cu_mark)))
+		return -NLE_NOMEM;
+
 	if (src->cu_act) {
 		if (!(dst->cu_act = rtnl_act_alloc()))
 			return -NLE_NOMEM;
@@ -191,7 +213,7 @@
 {
 	struct rtnl_u32 *u = data;
 	char buf[32];
-	
+
 	if (!u)
 		return;
 
@@ -239,10 +261,10 @@
 
 		nl_dump(p, ">");
 	}
-		
-	
+
+
 	for (i = 0; i < sel->nkeys; i++) {
-		key = (struct tc_u32_key *) ((char *) sel + sizeof(*sel)) + i;
+		key = &sel->keys[i];
 
 		nl_dump(p, "\n");
 		nl_dump_line(p, "      match key at %s%u ",
@@ -265,31 +287,44 @@
 			     struct nl_dump_params *p)
 {
 	struct rtnl_u32 *u = data;
-	struct tc_u32_sel *s;
+	struct tc_u32_sel *s = NULL;
+	struct tc_u32_mark *m;
 
 	if (!u)
 		return;
 
-	if (!(u->cu_mask & U32_ATTR_SELECTOR)) {
-		nl_dump(p, "no-selector\n");
+	if (!(u->cu_mask & (U32_ATTR_SELECTOR & U32_ATTR_MARK))) {
+		nl_dump(p, "no-selector no-mark\n");
 		return;
 	}
-	
-	s = u->cu_selector->d_data;
 
-	nl_dump(p, "nkeys %u ", s->nkeys);
+	if (!(u->cu_mask & U32_ATTR_SELECTOR)) {
+		nl_dump(p, "no-selector");
+	} else {
+		s = u->cu_selector->d_data;
+		nl_dump(p, "nkeys %u", s->nkeys);
+	}
+
+	if (!(u->cu_mask & U32_ATTR_MARK)) {
+		nl_dump(p, " no-mark");
+	} else {
+		m = u->cu_mark->d_data;
+		nl_dump(p, " mark 0x%u 0x%u", m->val, m->mask);
+	}
 
 	if (u->cu_mask & U32_ATTR_HASH)
-		nl_dump(p, "ht key 0x%x hash 0x%u",
+		nl_dump(p, " ht key 0x%x hash 0x%u",
 			TC_U32_USERHTID(u->cu_hash), TC_U32_HASH(u->cu_hash));
 
 	if (u->cu_mask & U32_ATTR_LINK)
-		nl_dump(p, "link %u ", u->cu_link);
+		nl_dump(p, " link %u", u->cu_link);
 
 	if (u->cu_mask & U32_ATTR_INDEV)
-		nl_dump(p, "indev %s ", u->cu_indev);
+		nl_dump(p, " indev %s", u->cu_indev);
 
-	print_selector(p, s, u);
+	if (u->cu_mask & U32_ATTR_SELECTOR)
+		print_selector(p, s, u);
+
 	nl_dump(p, "\n");
 }
 
@@ -315,7 +350,7 @@
 
 	if (!u)
 		return 0;
-	
+
 	if (u->cu_mask & U32_ATTR_DIVISOR)
 		NLA_PUT_U32(msg, TCA_U32_DIVISOR, u->cu_divisor);
 
@@ -331,6 +366,9 @@
 	if (u->cu_mask & U32_ATTR_SELECTOR)
 		NLA_PUT_DATA(msg, TCA_U32_SEL, u->cu_selector);
 
+	if (u->cu_mask & U32_ATTR_MARK)
+		NLA_PUT_DATA(msg, TCA_U32_MARK, u->cu_mark);
+
 	if (u->cu_mask & U32_ATTR_ACTION) {
 		int err;
 
@@ -363,20 +401,34 @@
 
 	rtnl_tc_set_handle((struct rtnl_tc *) cls, handle );
 }
- 
+
 int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid)
 {
 	struct rtnl_u32 *u;
 
 	if (!(u = rtnl_tc_data(TC_CAST(cls))))
 		return -NLE_NOMEM;
-	
+
 	u->cu_classid = classid;
 	u->cu_mask |= U32_ATTR_CLASSID;
 
 	return 0;
 }
 
+int rtnl_u32_get_classid(struct rtnl_cls *cls, uint32_t *classid)
+{
+	struct rtnl_u32 *u;
+
+	if (!(u = rtnl_tc_data_peek(TC_CAST(cls))))
+		return -NLE_INVAL;
+
+	if (!(u->cu_mask & U32_ATTR_CLASSID))
+		return -NLE_INVAL;
+
+	*classid = u->cu_classid;
+	return 0;
+}
+
 int rtnl_u32_set_divisor(struct rtnl_cls *cls, uint32_t divisor)
 {
 	struct rtnl_u32 *u;
@@ -417,7 +469,6 @@
 {
 	struct rtnl_u32 *u;
 	struct tc_u32_sel *sel;
-	int err;
 
 	hashmask = htonl(hashmask);
 
@@ -428,22 +479,17 @@
 	if (!sel)
 		return -NLE_NOMEM;
 
-	err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key));
-	if(err < 0)
-		return err;
-
-	sel = u32_selector(u);
-
 	sel->hmask = hashmask;
 	sel->hoff = offset;
 	return 0;
 }
 
-int rtnl_u32_set_cls_terminal(struct rtnl_cls *cls)
+int rtnl_u32_set_selector(struct rtnl_cls *cls, int offoff, uint32_t offmask, char offshift, uint16_t off, char flags)
 {
 	struct rtnl_u32 *u;
 	struct tc_u32_sel *sel;
-	int err;
+
+	offmask = ntohs(offmask);
 
 	if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
 		return -NLE_NOMEM;
@@ -452,11 +498,26 @@
 	if (!sel)
 		return -NLE_NOMEM;
 
-	err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key));
-	if(err < 0)
-		return err;
+	sel->offoff = offoff;
+	sel->offmask = offmask;
+	sel->offshift = offshift;
+	sel->flags |= TC_U32_VAROFFSET;
+	sel->off = off;
+	sel->flags |= flags;
+	return 0;
+}
 
-	sel = u32_selector(u);
+int rtnl_u32_set_cls_terminal(struct rtnl_cls *cls)
+{
+	struct rtnl_u32 *u;
+	struct tc_u32_sel *sel;
+
+	if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
+		return -NLE_NOMEM;
+
+	sel = u32_selector_alloc(u);
+	if (!sel)
+		return -NLE_NOMEM;
 
 	sel->flags |= TC_U32_TERMINAL;
 	return 0;
@@ -465,6 +526,7 @@
 int rtnl_u32_add_action(struct rtnl_cls *cls, struct rtnl_act *act)
 {
 	struct rtnl_u32 *u;
+	int err;
 
 	if (!act)
 		return 0;
@@ -473,9 +535,25 @@
 		return -NLE_NOMEM;
 
 	u->cu_mask |= U32_ATTR_ACTION;
+	if ((err = rtnl_act_append(&u->cu_act, act)))
+		return err;
+
 	/* In case user frees it */
 	rtnl_act_get(act);
-	return rtnl_act_append(&u->cu_act, act);
+	return 0;
+}
+
+struct rtnl_act* rtnl_u32_get_action(struct rtnl_cls *cls)
+{
+    struct rtnl_u32 *u;
+
+    if (!(u = rtnl_tc_data_peek(TC_CAST(cls))))
+        return NULL;
+
+    if (!(u->cu_mask & U32_ATTR_ACTION))
+        return NULL;
+
+    return u->cu_act;
 }
 
 int rtnl_u32_del_action(struct rtnl_cls *cls, struct rtnl_act *act)
@@ -554,6 +632,9 @@
 	if (!sel)
 		return -NLE_NOMEM;
 
+	if (sel->nkeys == UCHAR_MAX)
+		return -NLE_NOMEM;
+
 	err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key));
 	if (err < 0)
 		return err;
@@ -571,6 +652,46 @@
 	return 0;
 }
 
+int rtnl_u32_add_mark(struct rtnl_cls *cls, uint32_t val, uint32_t mask)
+{
+	struct tc_u32_mark *mark;
+	struct rtnl_u32 *u;
+
+	if (!(u = rtnl_tc_data(TC_CAST(cls))))
+		return -NLE_NOMEM;
+
+	mark = u32_mark_alloc(u);
+	if (!mark)
+		return -NLE_NOMEM;
+
+	mark->mask = mask;
+	mark->val = val;
+
+	u->cu_mask |= U32_ATTR_MARK;
+
+	return 0;
+}
+
+int rtnl_u32_del_mark(struct rtnl_cls *cls)
+{
+	struct rtnl_u32 *u;
+
+	if (!(u = rtnl_tc_data(TC_CAST(cls))))
+		return -NLE_NOMEM;
+
+	if (!(u->cu_mask))
+		return -NLE_INVAL;
+
+	if (!(u->cu_mask & U32_ATTR_MARK))
+		return -NLE_INVAL;
+
+	nl_data_free(u->cu_mark);
+	u->cu_mark = NULL;
+	u->cu_mask &= ~U32_ATTR_MARK;
+
+	return 0;
+}
+
 /**
  * Get the 32-bit key from the selector
  *
@@ -594,7 +715,6 @@
 	if (!(u->cu_mask & U32_ATTR_SELECTOR))
 		return -NLE_INVAL;
 
-	/* the selector might have been moved by realloc */
 	sel = u32_selector(u);
 	if (index >= sel->nkeys)
 		return -NLE_RANGE;
diff --git a/lib/route/link.c b/lib/route/link.c
index 3d31ffc..df01a71 100644
--- a/lib/route/link.c
+++ b/lib/route/link.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/link.c	Links (Interfaces)
  *
@@ -28,6 +29,8 @@
 #include <netlink/route/rtnl.h>
 #include <netlink/route/link.h>
 #include <netlink-private/route/link/api.h>
+#include <netlink-private/route/link/sriov.h>
+#include <netlink-private/utils.h>
 
 /** @cond SKIP */
 #define LINK_ATTR_MTU		(1 <<  0)
@@ -61,11 +64,31 @@
 #define LINK_ATTR_PHYS_PORT_ID	(1 << 28)
 #define LINK_ATTR_NS_FD		(1 << 29)
 #define LINK_ATTR_NS_PID	(1 << 30)
+/* 31 used by 32-bit api */
+#define LINK_ATTR_LINK_NETNSID  	((uint64_t) 1 << 32)
+#define LINK_ATTR_VF_LIST		((uint64_t) 1 << 33)
+#define LINK_ATTR_CARRIER_CHANGES	((uint64_t) 1 << 34)
+#define LINK_ATTR_PHYS_PORT_NAME	((uint64_t) 1 << 35)
+#define LINK_ATTR_PHYS_SWITCH_ID	((uint64_t) 1 << 36)
+#define LINK_ATTR_GSO_MAX_SEGS		((uint64_t) 1 << 37)
+#define LINK_ATTR_GSO_MAX_SIZE		((uint64_t) 1 << 38)
+#define LINK_ATTR_LINKINFO_SLAVE_KIND	((uint64_t) 1 << 39)
 
 static struct nl_cache_ops rtnl_link_ops;
 static struct nl_object_ops link_obj_ops;
 /** @endcond */
 
+struct rtnl_link *link_lookup(struct nl_cache *cache, int ifindex)
+{
+	if (!cache) {
+		cache = __nl_cache_mngt_require("route/link");
+		if (!cache)
+			return NULL;
+	}
+
+	return rtnl_link_get(cache, ifindex);
+}
+
 static struct rtnl_link_af_ops *af_lookup_and_alloc(struct rtnl_link *link,
 						    int family)
 {
@@ -95,6 +118,17 @@
 	return 0;
 }
 
+static int af_request_type(int af_type, struct rtnl_link *changes)
+{
+	struct rtnl_link_af_ops *ops;
+
+	ops = rtnl_link_af_ops_lookup(af_type);
+	if (ops && ops->ao_override_rtm(changes))
+		return RTM_SETLINK;
+
+	return RTM_NEWLINK;
+}
+
 static int af_clone(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
 		    void *data, void *arg)
 {
@@ -111,19 +145,45 @@
 		   void *data, void *arg)
 {
 	struct nl_msg *msg = arg;
-	struct nlattr *af_attr;
+	struct nlattr *af_attr = NULL;
 	int err;
 
 	if (!ops->ao_fill_af)
 		return 0;
 
-	if (!(af_attr = nla_nest_start(msg, ops->ao_family)))
-		return -NLE_MSGSIZE;
+	if (!ops->ao_fill_af_no_nest)
+		if (!(af_attr = nla_nest_start(msg, ops->ao_family)))
+			return -NLE_MSGSIZE;
 
 	if ((err = ops->ao_fill_af(link, arg, data)) < 0)
 		return err;
 
-	nla_nest_end(msg, af_attr);
+	if (!ops->ao_fill_af_no_nest)
+		nla_nest_end(msg, af_attr);
+
+	return 0;
+}
+
+static int af_fill_pi(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
+		   void *data, void *arg)
+{
+	struct nl_msg *msg = arg;
+	struct nlattr *pi_attr;
+	int err, pi_type = IFLA_PROTINFO;
+
+	if (!ops->ao_fill_pi)
+		return 0;
+
+	if (ops->ao_fill_pi_flags > 0)
+		pi_type |= ops->ao_fill_pi_flags;
+
+	if (!(pi_attr = nla_nest_start(msg, pi_type)))
+		return -NLE_MSGSIZE;
+
+	if ((err = ops->ao_fill_pi(link, arg, data)) < 0)
+		return err;
+
+	nla_nest_end(msg, pi_attr);
 
 	return 0;
 }
@@ -208,10 +268,7 @@
 	struct rtnl_link *link = nl_object_priv(c);
 
 	if (link) {
-		struct rtnl_link_info_ops *io;
-
-		if ((io = link->l_info_ops) != NULL)
-			release_link_info(link);
+		release_link_info(link);
 
 		/* proto info af reference */
 		rtnl_link_af_ops_put(link->l_af_ops);
@@ -221,10 +278,15 @@
 
 		free(link->l_ifalias);
 		free(link->l_info_kind);
+		free(link->l_info_slave_kind);
 
 		do_foreach_af(link, af_free, NULL);
 
 		nl_data_free(link->l_phys_port_id);
+		nl_data_free(link->l_phys_switch_id);
+
+		if (link->ce_mask & LINK_ATTR_VF_LIST)
+			rtnl_link_sriov_free_data(link);
 	}
 }
 
@@ -250,6 +312,10 @@
 		if (!(dst->l_info_kind = strdup(src->l_info_kind)))
 			return -NLE_NOMEM;
 
+	if (src->l_info_slave_kind)
+		if (!(dst->l_info_slave_kind = strdup(src->l_info_slave_kind)))
+			return -NLE_NOMEM;
+
 	if (src->l_info_ops && src->l_info_ops->io_clone) {
 		err = src->l_info_ops->io_clone(dst, src);
 		if (err < 0)
@@ -263,6 +329,14 @@
 		if (!(dst->l_phys_port_id = nl_data_clone(src->l_phys_port_id)))
 			return -NLE_NOMEM;
 
+	if (src->l_phys_switch_id)
+		if (!(dst->l_phys_switch_id = nl_data_clone(src->l_phys_switch_id)))
+			return -NLE_NOMEM;
+
+	if (src->ce_mask & LINK_ATTR_VF_LIST)
+		if ((err = rtnl_link_sriov_clone(dst, src)) < 0)
+			return err;
+
 	return 0;
 }
 
@@ -279,18 +353,24 @@
 	[IFLA_LINKINFO]		= { .type = NLA_NESTED },
 	[IFLA_QDISC]		= { .type = NLA_STRING,
 				    .maxlen = IFQDISCSIZ },
-	[IFLA_STATS]		= { .minlen = sizeof(struct rtnl_link_stats) },
-	[IFLA_STATS64]		= { .minlen = sizeof(struct rtnl_link_stats64)},
+	[IFLA_STATS]		= { .minlen = _nl_offsetofend (struct rtnl_link_stats, tx_compressed) },
+	[IFLA_STATS64]		= { .minlen = _nl_offsetofend (struct rtnl_link_stats64, tx_compressed) },
 	[IFLA_MAP]		= { .minlen = sizeof(struct rtnl_link_ifmap) },
 	[IFLA_IFALIAS]		= { .type = NLA_STRING, .maxlen = IFALIASZ },
 	[IFLA_NUM_VF]		= { .type = NLA_U32 },
+	[IFLA_VFINFO_LIST]	= { .type = NLA_NESTED },
 	[IFLA_AF_SPEC]		= { .type = NLA_NESTED },
 	[IFLA_PROMISCUITY]	= { .type = NLA_U32 },
 	[IFLA_NUM_TX_QUEUES]	= { .type = NLA_U32 },
 	[IFLA_NUM_RX_QUEUES]	= { .type = NLA_U32 },
+	[IFLA_GSO_MAX_SEGS]	= { .type = NLA_U32 },
+	[IFLA_GSO_MAX_SIZE]	= { .type = NLA_U32 },
 	[IFLA_GROUP]		= { .type = NLA_U32 },
 	[IFLA_CARRIER]		= { .type = NLA_U8 },
+	[IFLA_CARRIER_CHANGES]	= { .type = NLA_U32 },
 	[IFLA_PHYS_PORT_ID]	= { .type = NLA_UNSPEC },
+	[IFLA_PHYS_PORT_NAME]	= { .type = NLA_STRING, .maxlen = IFNAMSIZ },
+	[IFLA_PHYS_SWITCH_ID]	= { .type = NLA_UNSPEC },
 	[IFLA_NET_NS_PID]	= { .type = NLA_U32 },
 	[IFLA_NET_NS_FD]	= { .type = NLA_U32 },
 };
@@ -339,6 +419,14 @@
 		link->l_stats[RTNL_LINK_RX_COMPRESSED]	= st->rx_compressed;
 		link->l_stats[RTNL_LINK_TX_COMPRESSED]	= st->tx_compressed;
 
+		/* beware: @st might not be the full struct, only fields up to
+		 * tx_compressed are present. See _nl_offsetofend() above. */
+
+		if (nla_len(tb[IFLA_STATS]) >= _nl_offsetofend (struct rtnl_link_stats, rx_nohandler))
+			link->l_stats[RTNL_LINK_RX_NOHANDLER] = st->rx_nohandler;
+		else
+			link->l_stats[RTNL_LINK_RX_NOHANDLER] = 0;
+
 		link->ce_mask |= LINK_ATTR_STATS;
 	}
 
@@ -350,11 +438,10 @@
 		 * Therefore, copy the data to the stack and access it from
 		 * there, where it will be aligned to 8.
 		 */
-		struct rtnl_link_stats64 st;
+		struct rtnl_link_stats64 st = { 0 };
 
-		nla_memcpy(&st, tb[IFLA_STATS64], 
-			   sizeof(struct rtnl_link_stats64));
-		
+		nla_memcpy(&st, tb[IFLA_STATS64], sizeof (st));
+
 		link->l_stats[RTNL_LINK_RX_PACKETS]	= st.rx_packets;
 		link->l_stats[RTNL_LINK_TX_PACKETS]	= st.tx_packets;
 		link->l_stats[RTNL_LINK_RX_BYTES]	= st.rx_bytes;
@@ -382,6 +469,11 @@
 		link->l_stats[RTNL_LINK_RX_COMPRESSED]	= st.rx_compressed;
 		link->l_stats[RTNL_LINK_TX_COMPRESSED]	= st.tx_compressed;
 
+		/* beware: @st might not be the full struct, only fields up to
+		 * tx_compressed are present. See _nl_offsetofend() above. */
+
+		link->l_stats[RTNL_LINK_RX_NOHANDLER]	= st.rx_nohandler;
+
 		link->ce_mask |= LINK_ATTR_STATS;
 	}
 
@@ -419,6 +511,11 @@
 		link->ce_mask |= LINK_ATTR_LINK;
 	}
 
+	if (tb[IFLA_LINK_NETNSID]) {
+		link->l_link_netnsid = nla_get_s32(tb[IFLA_LINK_NETNSID]);
+		link->ce_mask |= LINK_ATTR_LINK_NETNSID;
+	}
+
 	if (tb[IFLA_WEIGHT]) {
 		link->l_weight = nla_get_u32(tb[IFLA_WEIGHT]);
 		link->ce_mask |= LINK_ATTR_WEIGHT;
@@ -430,7 +527,7 @@
 	}
 
 	if (tb[IFLA_MAP]) {
-		nla_memcpy(&link->l_map, tb[IFLA_MAP], 
+		nla_memcpy(&link->l_map, tb[IFLA_MAP],
 			   sizeof(struct rtnl_link_ifmap));
 		link->ce_mask |= LINK_ATTR_MAP;
 	}
@@ -445,6 +542,11 @@
 		link->ce_mask |= LINK_ATTR_CARRIER;
 	}
 
+	if (tb[IFLA_CARRIER_CHANGES]) {
+		link->l_carrier_changes = nla_get_u32(tb[IFLA_CARRIER_CHANGES]);
+		link->ce_mask |= LINK_ATTR_CARRIER_CHANGES;
+	}
+
 	if (tb[IFLA_OPERSTATE]) {
 		link->l_operstate = nla_get_u8(tb[IFLA_OPERSTATE]);
 		link->ce_mask |= LINK_ATTR_OPERSTATE;
@@ -482,6 +584,7 @@
 	struct ifinfomsg *ifi;
 	struct nlattr *tb[IFLA_MAX+1];
 	struct rtnl_link_af_ops *af_ops = NULL;
+	struct rtnl_link_af_ops *af_ops_family;
 	int err, family;
 	struct nla_policy real_link_policy[IFLA_MAX+1];
 
@@ -495,8 +598,10 @@
 
 	link->ce_msgtype = n->nlmsg_type;
 
-	if (!nlmsg_valid_hdr(n, sizeof(*ifi)))
-		return -NLE_MSG_TOOSHORT;
+	if (!nlmsg_valid_hdr(n, sizeof(*ifi))) {
+		err = -NLE_MSG_TOOSHORT;
+		goto errout;
+	}
 
 	ifi = nlmsg_data(n);
 	link->l_family = family = ifi->ifi_family;
@@ -505,10 +610,10 @@
 	link->l_flags = ifi->ifi_flags;
 	link->l_change = ifi->ifi_change;
 	link->ce_mask = (LINK_ATTR_IFNAME | LINK_ATTR_FAMILY |
-			  LINK_ATTR_ARPTYPE| LINK_ATTR_IFINDEX |
-			  LINK_ATTR_FLAGS | LINK_ATTR_CHANGE);
+			 LINK_ATTR_ARPTYPE| LINK_ATTR_IFINDEX |
+			 LINK_ATTR_FLAGS | LINK_ATTR_CHANGE);
 
-	if ((af_ops = af_lookup_and_alloc(link, family))) {
+	if ((af_ops_family = af_ops = af_lookup_and_alloc(link, family))) {
 		if (af_ops->ao_protinfo_policy) {
 			memcpy(&real_link_policy[IFLA_PROTINFO],
 			       af_ops->ao_protinfo_policy,
@@ -520,15 +625,21 @@
 
 	err = nlmsg_parse(n, sizeof(*ifi), tb, IFLA_MAX, real_link_policy);
 	if (err < 0)
-		return err;
+		goto errout;
 
 	err = rtnl_link_info_parse(link, tb);
 	if (err < 0)
-		return err;
+		goto errout;
 
 	if (tb[IFLA_NUM_VF]) {
 		link->l_num_vf = nla_get_u32(tb[IFLA_NUM_VF]);
 		link->ce_mask |= LINK_ATTR_NUM_VF;
+		if (link->l_num_vf && tb[IFLA_VFINFO_LIST]) {
+			if ((err = rtnl_link_sriov_parse_vflist(link, tb)) < 0) {
+				goto errout;
+			}
+			link->ce_mask |= LINK_ATTR_VF_LIST;
+		}
 	}
 
 	if (tb[IFLA_LINKINFO]) {
@@ -541,7 +652,7 @@
 
 		if (li[IFLA_INFO_KIND]) {
 			struct rtnl_link_info_ops *ops;
-			char *kind = nla_get_string(li[IFLA_INFO_KIND]);
+			const char *kind = nla_get_string(li[IFLA_INFO_KIND]);
 			int af;
 
 			err = rtnl_link_set_type(link, kind);
@@ -554,7 +665,7 @@
 				if (af_ops->ao_protinfo_policy) {
 					tb[IFLA_PROTINFO] = (struct nlattr *)af_ops->ao_protinfo_policy;
 				}
-				link->l_family = family = af;
+				link->l_family = af;
 				link->l_af_ops = af_ops;
 			}
 
@@ -572,8 +683,19 @@
 					/* XXX: Warn about unparsed info? */
 				}
 			}
+
+			link->ce_mask |= LINK_ATTR_LINKINFO;
 		}
-		link->ce_mask |= LINK_ATTR_LINKINFO;
+
+		if (li[IFLA_INFO_SLAVE_KIND]) {
+			const char *kind = nla_get_string(li[IFLA_INFO_SLAVE_KIND]);
+
+			err = rtnl_link_set_slave_type(link, kind);
+			if (err < 0)
+				goto errout;
+
+			link->ce_mask |= LINK_ATTR_LINKINFO_SLAVE_KIND;
+		}
 	}
 
 	if (tb[IFLA_PROTINFO] && af_ops && af_ops->ao_parse_protinfo) {
@@ -585,21 +707,35 @@
 	}
 
 	if (tb[IFLA_AF_SPEC]) {
-		struct nlattr *af_attr;
-		int remaining;
+		/* parsing of IFLA_AF_SPEC is dependent on the family used
+		 * in the request message.
+		 */
+		if (af_ops_family && af_ops_family->ao_parse_af_full) {
+			err = af_ops_family->ao_parse_af_full(link,
+			                                      tb[IFLA_AF_SPEC],
+			                                      link->l_af_data[af_ops_family->ao_family]);
+			if (err < 0)
+				goto errout;
+			link->ce_mask |= LINK_ATTR_AF_SPEC;
+		} else if (family == AF_UNSPEC) {
+			struct nlattr *af_attr;
+			int remaining;
 
-		nla_for_each_nested(af_attr, tb[IFLA_AF_SPEC], remaining) {
-			af_ops = af_lookup_and_alloc(link, nla_type(af_attr));
-			if (af_ops && af_ops->ao_parse_af) {
-				char *af_data = link->l_af_data[nla_type(af_attr)];
+			nla_for_each_nested(af_attr, tb[IFLA_AF_SPEC], remaining) {
+				af_ops = af_lookup_and_alloc(link, nla_type(af_attr));
+				if (af_ops && af_ops->ao_parse_af) {
+					char *af_data = link->l_af_data[nla_type(af_attr)];
 
-				err = af_ops->ao_parse_af(link, af_attr, af_data);
-				if (err < 0)
-					goto errout;
+					err = af_ops->ao_parse_af(link, af_attr, af_data);
+					if (err < 0)
+						goto errout;
+				}
 			}
-
+			link->ce_mask |= LINK_ATTR_AF_SPEC;
+		} else {
+			NL_DBG(3, "IFLA_AF_SPEC parsing not implemented for family %d\n",
+			          family);
 		}
-		link->ce_mask |= LINK_ATTR_AF_SPEC;
 	}
 
 	if (tb[IFLA_PROMISCUITY]) {
@@ -617,6 +753,16 @@
 		link->ce_mask |= LINK_ATTR_NUM_RX_QUEUES;
 	}
 
+	if (tb[IFLA_GSO_MAX_SEGS]) {
+		link->l_gso_max_segs = nla_get_u32(tb[IFLA_GSO_MAX_SEGS]);
+		link->ce_mask |= LINK_ATTR_GSO_MAX_SEGS;
+	}
+
+	if (tb[IFLA_GSO_MAX_SIZE]) {
+		link->l_gso_max_size = nla_get_u32(tb[IFLA_GSO_MAX_SIZE]);
+		link->ce_mask |= LINK_ATTR_GSO_MAX_SIZE;
+	}
+
 	if (tb[IFLA_GROUP]) {
 		link->l_group = nla_get_u32(tb[IFLA_GROUP]);
 		link->ce_mask |= LINK_ATTR_GROUP;
@@ -631,6 +777,20 @@
 		link->ce_mask |= LINK_ATTR_PHYS_PORT_ID;
 	}
 
+	if (tb[IFLA_PHYS_PORT_NAME]) {
+		nla_strlcpy(link->l_phys_port_name, tb[IFLA_PHYS_PORT_NAME], IFNAMSIZ);
+		link->ce_mask |= LINK_ATTR_PHYS_PORT_NAME;
+	}
+
+	if (tb[IFLA_PHYS_SWITCH_ID]) {
+		link->l_phys_switch_id = nl_data_alloc_attr(tb[IFLA_PHYS_SWITCH_ID]);
+		if (link->l_phys_switch_id == NULL) {
+			err = -NLE_NOMEM;
+			goto errout;
+		}
+		link->ce_mask |= LINK_ATTR_PHYS_SWITCH_ID;
+	}
+
 	err = pp->pp_cb((struct nl_object *) link, pp);
 errout:
 	rtnl_link_af_ops_put(af_ops);
@@ -641,8 +801,40 @@
 static int link_request_update(struct nl_cache *cache, struct nl_sock *sk)
 {
 	int family = cache->c_iarg1;
+	struct ifinfomsg hdr = { .ifi_family = family };
+	struct rtnl_link_af_ops *ops;
+	struct nl_msg *msg;
+	int err;
+	__u32 ext_filter_mask = RTEXT_FILTER_VF;
 
-	return nl_rtgen_request(sk, RTM_GETLINK, family, NLM_F_DUMP);
+	msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_DUMP);
+	if (!msg)
+		return -NLE_NOMEM;
+
+	err = -NLE_MSGSIZE;
+	if (nlmsg_append(msg, &hdr, sizeof(hdr), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	ops = rtnl_link_af_ops_lookup(family);
+	if (ops && ops->ao_get_af) {
+		err = ops->ao_get_af(msg, &ext_filter_mask);
+		if (err)
+			goto nla_put_failure;
+	}
+
+	if (ext_filter_mask) {
+		err = nla_put(msg, IFLA_EXT_MASK, sizeof(ext_filter_mask), &ext_filter_mask);
+		if (err)
+			goto nla_put_failure;
+	}
+
+	err = nl_send_auto(sk, msg);
+	if (err > 0)
+		err = 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return err;
 }
 
 static void link_dump_line(struct nl_object *obj, struct nl_dump_params *p)
@@ -657,6 +849,9 @@
 		fetched_cache = 1;
 	}
 
+	if (link->l_family != AF_UNSPEC)
+		nl_dump_line(p, "%s ", nl_af2str(link->l_family, buf, sizeof(buf)));
+
 	nl_dump_line(p, "%s %s ", link->l_name,
 		     nl_llproto2str(link->l_arptype, buf, sizeof(buf)));
 
@@ -678,7 +873,8 @@
 		nl_dump(p, "<%s> ", buf);
 
 	if (link->ce_mask & LINK_ATTR_LINK) {
-		if (cache) {
+		if (   cache
+		    && !(link->ce_mask & LINK_ATTR_LINK_NETNSID)) {
 			struct rtnl_link *ll = rtnl_link_get(cache, link->l_link);
 			nl_dump(p, "slave-of %s ", ll ? ll->l_name : "NONE");
 			if (ll)
@@ -686,6 +882,8 @@
 		} else
 			nl_dump(p, "slave-of %d ", link->l_link);
 	}
+	if (link->ce_mask & LINK_ATTR_LINK_NETNSID)
+		nl_dump(p, "link-netnsid %d ", link->l_link_netnsid);
 
 	if (link->ce_mask & LINK_ATTR_GROUP)
 		nl_dump(p, "group %u ", link->l_group);
@@ -755,12 +953,18 @@
 	nl_dump(p, "carrier %s",
 		rtnl_link_carrier2str(link->l_carrier, buf, sizeof(buf)));
 
+	if (link->ce_mask & LINK_ATTR_CARRIER_CHANGES)
+		nl_dump(p, " carrier-changes %u", link->l_carrier_changes);
+
 	nl_dump(p, "\n");
 
 	if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_DETAILS])
 		link->l_info_ops->io_dump[NL_DUMP_DETAILS](link, p);
 
 	do_foreach_af(link, af_dump_details, p);
+
+	if (link->ce_mask & LINK_ATTR_VF_LIST)
+		rtnl_link_sriov_dump_details(link, p);
 }
 
 static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
@@ -768,7 +972,7 @@
 	struct rtnl_link *link = (struct rtnl_link *) obj;
 	char *unit, fmt[64];
 	float res;
-	
+
 	link_dump_details(obj, p);
 
 	nl_dump_line(p, "    Stats:    bytes    packets     errors "
@@ -778,7 +982,7 @@
 
 	strcpy(fmt, "     RX %X.2f %s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n");
 	fmt[9] = *unit == 'B' ? '9' : '7';
-	
+
 	nl_dump_line(p, fmt, res, unit,
 		link->l_stats[RTNL_LINK_RX_PACKETS],
 		link->l_stats[RTNL_LINK_RX_ERRORS],
@@ -790,7 +994,7 @@
 
 	strcpy(fmt, "     TX %X.2f %s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n");
 	fmt[9] = *unit == 'B' ? '9' : '7';
-	
+
 	nl_dump_line(p, fmt, res, unit,
 		link->l_stats[RTNL_LINK_TX_PACKETS],
 		link->l_stats[RTNL_LINK_TX_ERRORS],
@@ -813,7 +1017,7 @@
 
 	nl_dump_line(p, "            aborted    carrier  heartbeat "
 			"    window  collision\n");
-	
+
 	nl_dump_line(p, "     TX  %10" PRIu64 " %10" PRIu64 " %10"
 			PRIu64 " %10" PRIu64 " %10" PRIu64 "\n",
 		link->l_stats[RTNL_LINK_TX_ABORT_ERR],
@@ -826,6 +1030,9 @@
 		link->l_info_ops->io_dump[NL_DUMP_STATS](link, p);
 
 	do_foreach_af(link, af_dump_stats, p);
+
+	if (link->ce_mask & LINK_ATTR_VF_LIST)
+		rtnl_link_sriov_dump_stats(link, p);
 }
 
 #if 0
@@ -897,18 +1104,19 @@
 	return;
 }
 
-static int link_compare(struct nl_object *_a, struct nl_object *_b,
-			uint32_t attrs, int flags)
+static uint64_t link_compare(struct nl_object *_a, struct nl_object *_b,
+			     uint64_t attrs, int flags)
 {
 	struct rtnl_link *a = (struct rtnl_link *) _a;
 	struct rtnl_link *b = (struct rtnl_link *) _b;
-	int diff = 0;
+	uint64_t diff = 0;
 
 #define LINK_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, LINK_ATTR_##ATTR, a, b, EXPR)
 
 	diff |= LINK_DIFF(IFINDEX,	a->l_index != b->l_index);
 	diff |= LINK_DIFF(MTU,		a->l_mtu != b->l_mtu);
 	diff |= LINK_DIFF(LINK,		a->l_link != b->l_link);
+	diff |= LINK_DIFF(LINK_NETNSID, a->l_link_netnsid != b->l_link_netnsid);
 	diff |= LINK_DIFF(TXQLEN,	a->l_txqlen != b->l_txqlen);
 	diff |= LINK_DIFF(WEIGHT,	a->l_weight != b->l_weight);
 	diff |= LINK_DIFF(MASTER,	a->l_master != b->l_master);
@@ -940,6 +1148,7 @@
 			goto protinfo_mismatch;
 	}
 
+	diff |= LINK_DIFF(LINKINFO, rtnl_link_info_data_compare(a, b, flags) != 0);
 out:
 	return diff;
 
@@ -951,32 +1160,40 @@
 }
 
 static const struct trans_tbl link_attrs[] = {
-	__ADD(LINK_ATTR_MTU, mtu)
-	__ADD(LINK_ATTR_LINK, link)
-	__ADD(LINK_ATTR_TXQLEN, txqlen)
-	__ADD(LINK_ATTR_WEIGHT, weight)
-	__ADD(LINK_ATTR_MASTER, master)
-	__ADD(LINK_ATTR_QDISC, qdisc)
-	__ADD(LINK_ATTR_MAP, map)
-	__ADD(LINK_ATTR_ADDR, address)
-	__ADD(LINK_ATTR_BRD, broadcast)
-	__ADD(LINK_ATTR_FLAGS, flags)
-	__ADD(LINK_ATTR_IFNAME, name)
-	__ADD(LINK_ATTR_IFINDEX, ifindex)
-	__ADD(LINK_ATTR_FAMILY, family)
-	__ADD(LINK_ATTR_ARPTYPE, arptype)
-	__ADD(LINK_ATTR_STATS, stats)
-	__ADD(LINK_ATTR_CHANGE, change)
-	__ADD(LINK_ATTR_OPERSTATE, operstate)
-	__ADD(LINK_ATTR_LINKMODE, linkmode)
-	__ADD(LINK_ATTR_IFALIAS, ifalias)
-	__ADD(LINK_ATTR_NUM_VF, num_vf)
-	__ADD(LINK_ATTR_PROMISCUITY, promiscuity)
-	__ADD(LINK_ATTR_NUM_TX_QUEUES, num_tx_queues)
-	__ADD(LINK_ATTR_NUM_RX_QUEUES, num_rx_queues)
-	__ADD(LINK_ATTR_GROUP, group)
-	__ADD(LINK_ATTR_CARRIER, carrier)
-	__ADD(LINK_ATTR_PHYS_PORT_ID, phys_port_id)
+	__ADD(LINK_ATTR_MTU, mtu),
+	__ADD(LINK_ATTR_LINK, link),
+	__ADD(LINK_ATTR_TXQLEN, txqlen),
+	__ADD(LINK_ATTR_WEIGHT, weight),
+	__ADD(LINK_ATTR_MASTER, master),
+	__ADD(LINK_ATTR_QDISC, qdisc),
+	__ADD(LINK_ATTR_MAP, map),
+	__ADD(LINK_ATTR_ADDR, address),
+	__ADD(LINK_ATTR_BRD, broadcast),
+	__ADD(LINK_ATTR_FLAGS, flags),
+	__ADD(LINK_ATTR_IFNAME, name),
+	__ADD(LINK_ATTR_IFINDEX, ifindex),
+	__ADD(LINK_ATTR_FAMILY, family),
+	__ADD(LINK_ATTR_ARPTYPE, arptype),
+	__ADD(LINK_ATTR_STATS, stats),
+	__ADD(LINK_ATTR_CHANGE, change),
+	__ADD(LINK_ATTR_OPERSTATE, operstate),
+	__ADD(LINK_ATTR_LINKMODE, linkmode),
+	__ADD(LINK_ATTR_IFALIAS, ifalias),
+	__ADD(LINK_ATTR_NUM_VF, num_vf),
+	__ADD(LINK_ATTR_PROMISCUITY, promiscuity),
+	__ADD(LINK_ATTR_NUM_TX_QUEUES, num_tx_queues),
+	__ADD(LINK_ATTR_NUM_RX_QUEUES, num_rx_queues),
+	__ADD(LINK_ATTR_GSO_MAX_SEGS, gso_max_segs),
+	__ADD(LINK_ATTR_GSO_MAX_SIZE, gso_max_size),
+	__ADD(LINK_ATTR_GROUP, group),
+	__ADD(LINK_ATTR_CARRIER, carrier),
+	__ADD(LINK_ATTR_CARRIER_CHANGES, carrier_changes),
+	__ADD(LINK_ATTR_PHYS_PORT_ID, phys_port_id),
+	__ADD(LINK_ATTR_PHYS_PORT_NAME, phys_port_name),
+	__ADD(LINK_ATTR_PHYS_SWITCH_ID, phys_switch_id),
+	__ADD(LINK_ATTR_NS_FD, ns_fd),
+	__ADD(LINK_ATTR_NS_PID, ns_pid),
+	__ADD(LINK_ATTR_LINK_NETNSID, link_netnsid),
 };
 
 static char *link_attrs2str(int attrs, char *buf, size_t len)
@@ -996,6 +1213,55 @@
  * @arg sk		Netlink socket.
  * @arg family		Link address family or AF_UNSPEC
  * @arg result		Pointer to store resulting cache.
+ * @arg flags		Flags to set in link cache before filling
+ *
+ * Allocates and initializes a new link cache. If \c sk is valid, a netlink
+ * message is sent to the kernel requesting a full dump of all configured
+ * links. The returned messages are parsed and filled into the cache. If
+ * the operation succeeds, the resulting cache will contain a link object for
+ * each link configured in the kernel. If \c sk is NULL, returns 0 but the
+ * cache is still empty.
+ *
+ * If \c family is set to an address family other than \c AF_UNSPEC the
+ * contents of the cache can be limited to a specific address family.
+ * Currently the following address families are supported:
+ * - AF_BRIDGE
+ * - AF_INET6
+ *
+ * @route_doc{link_list, Get List of Links}
+ * @see rtnl_link_get()
+ * @see rtnl_link_get_by_name()
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_alloc_cache_flags(struct nl_sock *sk, int family,
+				struct nl_cache **result, unsigned int flags)
+{
+	struct nl_cache * cache;
+	int err;
+
+	cache = nl_cache_alloc(&rtnl_link_ops);
+	if (!cache)
+		return -NLE_NOMEM;
+
+	cache->c_iarg1 = family;
+
+	if (flags)
+		nl_cache_set_flags(cache, flags);
+
+	if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
+		nl_cache_free(cache);
+		return err;
+	}
+
+	*result = cache;
+	return 0;
+}
+
+/**
+ * Allocate link cache and fill in all configured links.
+ * @arg sk		Netlink socket.
+ * @arg family		Link address family or AF_UNSPEC
+ * @arg result		Pointer to store resulting cache.
  *
  * Allocates and initializes a new link cache. If \c sk is valid, a netlink
  * message is sent to the kernel requesting a full dump of all configured
@@ -1017,24 +1283,10 @@
  */
 int rtnl_link_alloc_cache(struct nl_sock *sk, int family, struct nl_cache **result)
 {
-	struct nl_cache * cache;
-	int err;
-	
-	cache = nl_cache_alloc(&rtnl_link_ops);
-	if (!cache)
-		return -NLE_NOMEM;
-
-	cache->c_iarg1 = family;
-	
-	if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
-		nl_cache_free(cache);
-		return err;
-	}
-
-	*result = cache;
-	return 0;
+	return rtnl_link_alloc_cache_flags(sk, family, result, 0);
 }
 
+
 /**
  * Lookup link in cache by interface index
  * @arg cache		Link cache
@@ -1119,6 +1371,8 @@
 {
 	struct ifinfomsg ifi;
 	struct nl_msg *msg;
+	__u32 vf_mask = RTEXT_FILTER_VF;
+	int err = -NLE_MSGSIZE;
 
 	if (ifindex <= 0 && !name) {
 		APPBUG("ifindex or name must be specified");
@@ -1133,18 +1387,24 @@
 	if (ifindex > 0)
 		ifi.ifi_index = ifindex;
 
-	if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
+	if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) {
+		err = -NLE_MSGSIZE;
 		goto nla_put_failure;
+	}
 
 	if (name)
 		NLA_PUT_STRING(msg, IFLA_IFNAME, name);
 
+	err = nla_put(msg, IFLA_EXT_MASK, sizeof(vf_mask), &vf_mask);
+	if (err)
+		goto nla_put_failure;
+
 	*result = msg;
 	return 0;
 
 nla_put_failure:
 	nlmsg_free(msg);
-	return -NLE_MSGSIZE;
+	return err;
 }
 
 /**
@@ -1160,6 +1420,11 @@
  * pointer or -NLE_OBJ_NOTFOUND is returned if no matching link was
  * found.
  *
+ * Older kernels do not support lookup by name. In that case, libnl
+ * will fail with -NLE_OPNOTSUPP. Note that previous version of libnl
+ * failed in this case with -NLE_INVAL. You can check libnl behavior
+ * using NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP capability.
+ *
  * @route_doc{link_direct_lookup, Lookup Single Link (Direct Lookup)}
  * @return 0 on success or a negative error code.
  */
@@ -1169,6 +1434,7 @@
 	struct nl_msg *msg = NULL;
 	struct nl_object *obj;
 	int err;
+	int syserr;
 
 	if ((err = rtnl_link_build_get_request(ifindex, name, &msg)) < 0)
 		return err;
@@ -1178,15 +1444,25 @@
 	if (err < 0)
 		return err;
 
-	if ((err = nl_pickup(sk, link_msg_parser, &obj)) < 0)
+	if ((err = nl_pickup_keep_syserr(sk, link_msg_parser, &obj, &syserr)) < 0) {
+		if (syserr == -EINVAL &&
+		    ifindex <= 0 &&
+		    name && *name) {
+			/* Older kernels do not support lookup by ifname. This was added
+			 * by commit kernel a3d1289126e7b14307074b76bf1677015ea5036f .
+			 * Detect this error case and return NLE_OPNOTSUPP instead of
+			 * NLE_INVAL. */
+			return -NLE_OPNOTSUPP;
+		}
 		return err;
+	}
 
 	/* We have used link_msg_parser(), object is definitely a link */
 	*result = (struct rtnl_link *) obj;
 
 	/* If an object has been returned, we also need to wait for the ACK */
-	 if (err == 0 && obj)
-		 wait_for_ack(sk);
+	if (err == 0 && obj)
+		wait_for_ack(sk);
 
 	return 0;
 }
@@ -1232,7 +1508,7 @@
 {
 	int ifindex = 0;
 	struct rtnl_link *link;
-	
+
 	link = rtnl_link_get_by_name(cache, name);
 	if (link) {
 		ifindex = link->l_index;
@@ -1279,6 +1555,9 @@
 	if (link->ce_mask & LINK_ATTR_LINK)
 		NLA_PUT_U32(msg, IFLA_LINK, link->l_link);
 
+	if (link->ce_mask & LINK_ATTR_LINK_NETNSID)
+		NLA_PUT_S32(msg, IFLA_LINK_NETNSID, link->l_link_netnsid);
+
 	if (link->ce_mask & LINK_ATTR_MASTER)
 		NLA_PUT_U32(msg, IFLA_MASTER, link->l_master);
 
@@ -1319,23 +1598,37 @@
 	if (link->ce_mask & LINK_ATTR_GROUP)
 		NLA_PUT_U32(msg, IFLA_GROUP, link->l_group);
 
-	if (link->ce_mask & LINK_ATTR_LINKINFO) {
+	if (link->ce_mask & (LINK_ATTR_LINKINFO|LINK_ATTR_LINKINFO_SLAVE_KIND)) {
 		struct nlattr *info;
 
 		if (!(info = nla_nest_start(msg, IFLA_LINKINFO)))
 			goto nla_put_failure;
 
-		NLA_PUT_STRING(msg, IFLA_INFO_KIND, link->l_info_kind);
+		if (link->ce_mask & LINK_ATTR_LINKINFO) {
+			NLA_PUT_STRING(msg, IFLA_INFO_KIND, link->l_info_kind);
 
-		if (link->l_info_ops) {
-			if (link->l_info_ops->io_put_attrs &&
-			    link->l_info_ops->io_put_attrs(msg, link) < 0)
-				goto nla_put_failure;
+			if (link->l_info_ops) {
+				if (link->l_info_ops->io_put_attrs &&
+				    link->l_info_ops->io_put_attrs(msg, link) < 0)
+					goto nla_put_failure;
+			}
+		}
+
+		if (link->ce_mask & LINK_ATTR_LINKINFO_SLAVE_KIND) {
+			NLA_PUT_STRING(msg, IFLA_INFO_SLAVE_KIND, link->l_info_slave_kind);
 		}
 
 		nla_nest_end(msg, info);
 	}
 
+	if (link->ce_mask & LINK_ATTR_VF_LIST) {
+		if (rtnl_link_sriov_fill_vflist(msg, link) < 0)
+			goto nla_put_failure;
+	}
+
+	if (do_foreach_af(link, af_fill_pi, msg) < 0)
+		goto nla_put_failure;
+
 	if (!(af_spec = nla_nest_start(msg, IFLA_AF_SPEC)))
 		goto nla_put_failure;
 
@@ -1380,6 +1673,7 @@
 		.ifi_family = link->l_family,
 		.ifi_index = link->l_index,
 		.ifi_flags = link->l_flags,
+		.ifi_change = link->l_flag_mask,
 	};
 
 	return build_link_msg(RTM_NEWLINK, &ifi, link, flags, result);
@@ -1406,7 +1700,7 @@
 {
 	struct nl_msg *msg;
 	int err;
-	
+
 	err = rtnl_link_build_add_request(link, flags, &msg);
 	if (err < 0)
 		return err;
@@ -1442,11 +1736,12 @@
 		.ifi_family = orig->l_family,
 		.ifi_index = orig->l_index,
 	};
-	int err;
+	int err, rt;
 
 	if (changes->ce_mask & LINK_ATTR_FLAGS) {
 		ifi.ifi_flags = orig->l_flags & ~changes->l_flag_mask;
 		ifi.ifi_flags |= changes->l_flags;
+		ifi.ifi_change = changes->l_flag_mask;
 	}
 
 	if (changes->l_family && changes->l_family != orig->l_family) {
@@ -1461,7 +1756,9 @@
 	    !strcmp(orig->l_name, changes->l_name))
 		changes->ce_mask &= ~LINK_ATTR_IFNAME;
 
-	if ((err = build_link_msg(RTM_NEWLINK, &ifi, changes, flags, result)) < 0)
+	rt = af_request_type(orig->l_family, changes);
+
+	if ((err = build_link_msg(rt, &ifi, changes, flags, result)) < 0)
 		goto errout;
 
 	return 0;
@@ -1499,6 +1796,10 @@
  * @note The link name can only be changed if the link has been put
  *       in opertional down state. (~IF_UP)
  *
+ * @note On versions up to 3.4.0, \c NLE_SEQ_MISMATCH would be returned if the
+ *       kernel does not supports \c RTM_NEWLINK. It is advised to ignore the
+ *       error code if you cannot upgrade the library.
+ *
  * @return 0 on success or a negative error code.
  */
 int rtnl_link_change(struct nl_sock *sk, struct rtnl_link *orig,
@@ -1506,11 +1807,12 @@
 {
 	struct nl_msg *msg;
 	int err;
-	
+
 	err = rtnl_link_build_change_request(orig, changes, flags, &msg);
 	if (err < 0)
 		return err;
 
+	BUG_ON(msg->nm_nlh->nlmsg_seq != NL_AUTO_SEQ);
 retry:
 	err = nl_send_auto_complete(sk, msg);
 	if (err < 0)
@@ -1519,6 +1821,7 @@
 	err = wait_for_ack(sk);
 	if (err == -NLE_OPNOTSUPP && msg->nm_nlh->nlmsg_type == RTM_NEWLINK) {
 		msg->nm_nlh->nlmsg_type = RTM_SETLINK;
+		msg->nm_nlh->nlmsg_seq = NL_AUTO_SEQ;
 		goto retry;
 	}
 
@@ -1605,7 +1908,7 @@
 {
 	struct nl_msg *msg;
 	int err;
-	
+
 	if ((err = rtnl_link_build_delete_request(link, &msg)) < 0)
 		return err;
 
@@ -1971,6 +2274,40 @@
 }
 
 /**
+ * Set the netnsid of the link
+ * @arg link            Link object
+ * @link_netnsid        the netnsid to set
+ *
+ * Sets the IFLA_LINK_NETNSID attribute of the link
+ * @returns 0 on success
+ */
+int rtnl_link_set_link_netnsid(struct rtnl_link *link, int32_t link_netnsid)
+{
+	link->l_link_netnsid = link_netnsid;
+	link->ce_mask |= LINK_ATTR_LINK_NETNSID;
+	return 0;
+}
+
+/**
+ * Get the netnsid of the link
+ * @arg link            Link object
+ * @out_link_netnsid    the netnsid
+ *
+ * Gets the IFLA_LINK_NETNSID attribute of the link
+ * or returns an error if the value is unset.
+ *
+ * @returns 0 on success
+ */
+int rtnl_link_get_link_netnsid(const struct rtnl_link *link, int32_t *out_link_netnsid)
+{
+	if (!(link->ce_mask & LINK_ATTR_LINK_NETNSID))
+		return -NLE_INVAL;
+
+	*out_link_netnsid = link->l_link_netnsid;
+	return 0;
+}
+
+/**
  * Set master link of link object
  * @arg link		Link object
  * @arg ifindex		Interface index of master link
@@ -2021,6 +2358,24 @@
 }
 
 /**
+ * Return carrier on/off changes of link object
+ * @arg link		Link object
+ * @arg carrier_changes	Pointer to store number of carrier changes
+ *
+ * @return 0 on success, negative error number otherwise
+ */
+int rtnl_link_get_carrier_changes(struct rtnl_link *link, uint32_t *carrier_changes)
+{
+	if (!(link->ce_mask & LINK_ATTR_CARRIER_CHANGES))
+		return -NLE_NOATTR;
+
+	if (carrier_changes)
+		*carrier_changes = link->l_carrier_changes;
+
+	return 0;
+}
+
+/**
  * Set operational status of link object
  * @arg link		Link object
  * @arg status		New opertional status
@@ -2208,7 +2563,7 @@
  * be released with all link type specific attributes lost.
  *
  * @route_doc{link_modules, Link Modules}
- * @return 0 on success or a negative errror code.
+ * @return 0 on success or a negative error code.
  */
 int rtnl_link_set_type(struct rtnl_link *link, const char *type)
 {
@@ -2218,8 +2573,7 @@
 
 	free(link->l_info_kind);
 	link->ce_mask &= ~LINK_ATTR_LINKINFO;
-	if (link->l_info_ops)
-		release_link_info(link);
+	release_link_info(link);
 
 	if (!type)
 		return 0;
@@ -2259,6 +2613,49 @@
 }
 
 /**
+ * Set type of slave link object
+ * @arg link		Link object (slave)
+ * @arg type		Name of link type
+ *
+ * If a slave type has been assigned already it will be released.
+ *
+ * @route_doc{link_modules, Link Modules}
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_set_slave_type(struct rtnl_link *link, const char *type)
+{
+	char *kind = NULL;
+
+	if (type) {
+		kind = strdup(type);
+		if (!kind)
+			return -NLE_NOMEM;
+	}
+
+	free(link->l_info_slave_kind);
+	link->l_info_slave_kind = kind;
+
+	if (kind)
+		link->ce_mask |= LINK_ATTR_LINKINFO_SLAVE_KIND;
+	else
+		link->ce_mask &= ~LINK_ATTR_LINKINFO_SLAVE_KIND;
+	return 0;
+}
+
+/**
+ * Return type of enslaved link
+ * @arg link		Link object
+ *
+ * @route_doc{link_modules, Link Modules}
+ * @return Name of enslaved link type or NULL if not specified.
+ */
+const char *rtnl_link_get_slave_type(const struct rtnl_link *link)
+{
+	return link->l_info_slave_kind;
+}
+
+
+/**
  * Set link promiscuity count
  * @arg link		Link object
  * @arg count		New promiscuity count
@@ -2348,6 +2745,42 @@
 }
 
 /**
+ * Return maximum number of segments for generic segmentation offload
+ * @arg link		Link object
+ * @arg gso_max_segs	Pointer to store maximum number GSO segments
+ *
+ * @return 0 on success, negative error number otherwise
+ */
+int rtnl_link_get_gso_max_segs(struct rtnl_link *link, uint32_t *gso_max_segs)
+{
+	if (!(link->ce_mask & LINK_ATTR_GSO_MAX_SEGS))
+		return -NLE_NOATTR;
+
+	if (gso_max_segs)
+		*gso_max_segs = link->l_gso_max_segs;
+
+	return 0;
+}
+
+/**
+ * Return maximum size for generic segmentation offload
+ * @arg link		Link object
+ * @arg gso_max_segs	Pointer to store maximum GSO size
+ *
+ * @return 0 on success, negative error number otherwise
+ */
+int rtnl_link_get_gso_max_size(struct rtnl_link *link, uint32_t *gso_max_size)
+{
+	if (!(link->ce_mask & LINK_ATTR_GSO_MAX_SIZE))
+		return -NLE_NOATTR;
+
+	if (gso_max_size)
+		*gso_max_size = link->l_gso_max_size;
+
+	return 0;
+}
+
+/**
  * Return physical port id of link object
  * @arg link		Link object
  *
@@ -2358,6 +2791,28 @@
 	return link->l_phys_port_id;
 }
 
+/**
+ * Return physical port name of link object
+ * @arg link		Link object
+ *
+ * @return Physical port name or NULL if not set.
+ */
+char *rtnl_link_get_phys_port_name(struct rtnl_link *link)
+{
+	return link->l_phys_port_name;
+}
+
+/*
+ * Return physical switch id of link object
+ * @arg link		Link object
+ *
+ * @return Physical switch id or NULL if not set.
+ */
+struct nl_data *rtnl_link_get_phys_switch_id(struct rtnl_link *link)
+{
+	return link->l_phys_switch_id;
+}
+
 void rtnl_link_set_ns_fd(struct rtnl_link *link, int fd)
 {
 	link->l_ns_fd = fd;
@@ -2410,7 +2865,7 @@
 
 	rtnl_link_set_ifindex(link, slave);
 	rtnl_link_set_master(link, master);
-	
+
 	if ((err = rtnl_link_change(sock, link, link, 0)) < 0)
 		goto errout;
 
@@ -2511,25 +2966,25 @@
  */
 
 static const struct trans_tbl link_flags[] = {
-	__ADD(IFF_LOOPBACK, loopback)
-	__ADD(IFF_BROADCAST, broadcast)
-	__ADD(IFF_POINTOPOINT, pointopoint)
-	__ADD(IFF_MULTICAST, multicast)
-	__ADD(IFF_NOARP, noarp)
-	__ADD(IFF_ALLMULTI, allmulti)
-	__ADD(IFF_PROMISC, promisc)
-	__ADD(IFF_MASTER, master)
-	__ADD(IFF_SLAVE, slave)
-	__ADD(IFF_DEBUG, debug)
-	__ADD(IFF_DYNAMIC, dynamic)
-	__ADD(IFF_AUTOMEDIA, automedia)
-	__ADD(IFF_PORTSEL, portsel)
-	__ADD(IFF_NOTRAILERS, notrailers)
-	__ADD(IFF_UP, up)
-	__ADD(IFF_RUNNING, running)
-	__ADD(IFF_LOWER_UP, lowerup)
-	__ADD(IFF_DORMANT, dormant)
-	__ADD(IFF_ECHO, echo)
+	__ADD(IFF_LOOPBACK, loopback),
+	__ADD(IFF_BROADCAST, broadcast),
+	__ADD(IFF_POINTOPOINT, pointopoint),
+	__ADD(IFF_MULTICAST, multicast),
+	__ADD(IFF_NOARP, noarp),
+	__ADD(IFF_ALLMULTI, allmulti),
+	__ADD(IFF_PROMISC, promisc),
+	__ADD(IFF_MASTER, master),
+	__ADD(IFF_SLAVE, slave),
+	__ADD(IFF_DEBUG, debug),
+	__ADD(IFF_DYNAMIC, dynamic),
+	__ADD(IFF_AUTOMEDIA, automedia),
+	__ADD(IFF_PORTSEL, portsel),
+	__ADD(IFF_NOTRAILERS, notrailers),
+	__ADD(IFF_UP, up),
+	__ADD(IFF_RUNNING, running),
+	__ADD(IFF_LOWER_UP, lowerup),
+	__ADD(IFF_DORMANT, dormant),
+	__ADD(IFF_ECHO, echo),
 };
 
 char *rtnl_link_flags2str(int flags, char *buf, size_t len)
@@ -2544,69 +2999,70 @@
 }
 
 static const struct trans_tbl link_stats[] = {
-	__ADD(RTNL_LINK_RX_PACKETS, rx_packets)
-	__ADD(RTNL_LINK_TX_PACKETS, tx_packets)
-	__ADD(RTNL_LINK_RX_BYTES, rx_bytes)
-	__ADD(RTNL_LINK_TX_BYTES, tx_bytes)
-	__ADD(RTNL_LINK_RX_ERRORS, rx_errors)
-	__ADD(RTNL_LINK_TX_ERRORS, tx_errors)
-	__ADD(RTNL_LINK_RX_DROPPED, rx_dropped)
-	__ADD(RTNL_LINK_TX_DROPPED, tx_dropped)
-	__ADD(RTNL_LINK_RX_COMPRESSED, rx_compressed)
-	__ADD(RTNL_LINK_TX_COMPRESSED, tx_compressed)
-	__ADD(RTNL_LINK_RX_FIFO_ERR, rx_fifo_err)
-	__ADD(RTNL_LINK_TX_FIFO_ERR, tx_fifo_err)
-	__ADD(RTNL_LINK_RX_LEN_ERR, rx_len_err)
-	__ADD(RTNL_LINK_RX_OVER_ERR, rx_over_err)
-	__ADD(RTNL_LINK_RX_CRC_ERR, rx_crc_err)
-	__ADD(RTNL_LINK_RX_FRAME_ERR, rx_frame_err)
-	__ADD(RTNL_LINK_RX_MISSED_ERR, rx_missed_err)
-	__ADD(RTNL_LINK_TX_ABORT_ERR, tx_abort_err)
-	__ADD(RTNL_LINK_TX_CARRIER_ERR, tx_carrier_err)
-	__ADD(RTNL_LINK_TX_HBEAT_ERR, tx_hbeat_err)
-	__ADD(RTNL_LINK_TX_WIN_ERR, tx_win_err)
-	__ADD(RTNL_LINK_COLLISIONS, collisions)
-	__ADD(RTNL_LINK_MULTICAST, multicast)
-	__ADD(RTNL_LINK_IP6_INPKTS, Ip6InReceives)
-	__ADD(RTNL_LINK_IP6_INHDRERRORS, Ip6InHdrErrors)
-	__ADD(RTNL_LINK_IP6_INTOOBIGERRORS, Ip6InTooBigErrors)
-	__ADD(RTNL_LINK_IP6_INNOROUTES, Ip6InNoRoutes)
-	__ADD(RTNL_LINK_IP6_INADDRERRORS, Ip6InAddrErrors)
-	__ADD(RTNL_LINK_IP6_INUNKNOWNPROTOS, Ip6InUnknownProtos)
-	__ADD(RTNL_LINK_IP6_INTRUNCATEDPKTS, Ip6InTruncatedPkts)
-	__ADD(RTNL_LINK_IP6_INDISCARDS, Ip6InDiscards)
-	__ADD(RTNL_LINK_IP6_INDELIVERS, Ip6InDelivers)
-	__ADD(RTNL_LINK_IP6_OUTFORWDATAGRAMS, Ip6OutForwDatagrams)
-	__ADD(RTNL_LINK_IP6_OUTPKTS, Ip6OutRequests)
-	__ADD(RTNL_LINK_IP6_OUTDISCARDS, Ip6OutDiscards)
-	__ADD(RTNL_LINK_IP6_OUTNOROUTES, Ip6OutNoRoutes)
-	__ADD(RTNL_LINK_IP6_REASMTIMEOUT, Ip6ReasmTimeout)
-	__ADD(RTNL_LINK_IP6_REASMREQDS, Ip6ReasmReqds)
-	__ADD(RTNL_LINK_IP6_REASMOKS, Ip6ReasmOKs)
-	__ADD(RTNL_LINK_IP6_REASMFAILS, Ip6ReasmFails)
-	__ADD(RTNL_LINK_IP6_FRAGOKS, Ip6FragOKs)
-	__ADD(RTNL_LINK_IP6_FRAGFAILS, Ip6FragFails)
-	__ADD(RTNL_LINK_IP6_FRAGCREATES, Ip6FragCreates)
-	__ADD(RTNL_LINK_IP6_INMCASTPKTS, Ip6InMcastPkts)
-	__ADD(RTNL_LINK_IP6_OUTMCASTPKTS, Ip6OutMcastPkts)
-	__ADD(RTNL_LINK_IP6_INBCASTPKTS, Ip6InBcastPkts)
-	__ADD(RTNL_LINK_IP6_OUTBCASTPKTS, Ip6OutBcastPkts)
-	__ADD(RTNL_LINK_IP6_INOCTETS, Ip6InOctets)
-	__ADD(RTNL_LINK_IP6_OUTOCTETS, Ip6OutOctets)
-	__ADD(RTNL_LINK_IP6_INMCASTOCTETS, Ip6InMcastOctets)
-	__ADD(RTNL_LINK_IP6_OUTMCASTOCTETS, Ip6OutMcastOctets)
-	__ADD(RTNL_LINK_IP6_INBCASTOCTETS, Ip6InBcastOctets)
-	__ADD(RTNL_LINK_IP6_OUTBCASTOCTETS, Ip6OutBcastOctets)
-	__ADD(RTNL_LINK_ICMP6_INMSGS, ICMP6_InMsgs)
-	__ADD(RTNL_LINK_ICMP6_INERRORS, ICMP6_InErrors)
-	__ADD(RTNL_LINK_ICMP6_OUTMSGS, ICMP6_OutMsgs)
-	__ADD(RTNL_LINK_ICMP6_OUTERRORS, ICMP6_OutErrors)
-	__ADD(RTNL_LINK_ICMP6_CSUMERRORS, ICMP6_InCsumErrors)
-	__ADD(RTNL_LINK_IP6_CSUMERRORS, Ip6_InCsumErrors)
-	__ADD(RTNL_LINK_IP6_NOECTPKTS, Ip6_InNoECTPkts)
-	__ADD(RTNL_LINK_IP6_ECT1PKTS, Ip6_InECT1Pkts)
-	__ADD(RTNL_LINK_IP6_ECT0PKTS, Ip6_InECT0Pkts)
-	__ADD(RTNL_LINK_IP6_CEPKTS, Ip6_InCEPkts)
+	__ADD(RTNL_LINK_RX_PACKETS, rx_packets),
+	__ADD(RTNL_LINK_TX_PACKETS, tx_packets),
+	__ADD(RTNL_LINK_RX_BYTES, rx_bytes),
+	__ADD(RTNL_LINK_TX_BYTES, tx_bytes),
+	__ADD(RTNL_LINK_RX_ERRORS, rx_errors),
+	__ADD(RTNL_LINK_TX_ERRORS, tx_errors),
+	__ADD(RTNL_LINK_RX_DROPPED, rx_dropped),
+	__ADD(RTNL_LINK_TX_DROPPED, tx_dropped),
+	__ADD(RTNL_LINK_RX_COMPRESSED, rx_compressed),
+	__ADD(RTNL_LINK_TX_COMPRESSED, tx_compressed),
+	__ADD(RTNL_LINK_RX_FIFO_ERR, rx_fifo_err),
+	__ADD(RTNL_LINK_TX_FIFO_ERR, tx_fifo_err),
+	__ADD(RTNL_LINK_RX_LEN_ERR, rx_len_err),
+	__ADD(RTNL_LINK_RX_OVER_ERR, rx_over_err),
+	__ADD(RTNL_LINK_RX_CRC_ERR, rx_crc_err),
+	__ADD(RTNL_LINK_RX_FRAME_ERR, rx_frame_err),
+	__ADD(RTNL_LINK_RX_MISSED_ERR, rx_missed_err),
+	__ADD(RTNL_LINK_TX_ABORT_ERR, tx_abort_err),
+	__ADD(RTNL_LINK_TX_CARRIER_ERR, tx_carrier_err),
+	__ADD(RTNL_LINK_TX_HBEAT_ERR, tx_hbeat_err),
+	__ADD(RTNL_LINK_TX_WIN_ERR, tx_win_err),
+	__ADD(RTNL_LINK_COLLISIONS, collisions),
+	__ADD(RTNL_LINK_MULTICAST, multicast),
+	__ADD(RTNL_LINK_IP6_INPKTS, Ip6InReceives),
+	__ADD(RTNL_LINK_IP6_INHDRERRORS, Ip6InHdrErrors),
+	__ADD(RTNL_LINK_IP6_INTOOBIGERRORS, Ip6InTooBigErrors),
+	__ADD(RTNL_LINK_IP6_INNOROUTES, Ip6InNoRoutes),
+	__ADD(RTNL_LINK_IP6_INADDRERRORS, Ip6InAddrErrors),
+	__ADD(RTNL_LINK_IP6_INUNKNOWNPROTOS, Ip6InUnknownProtos),
+	__ADD(RTNL_LINK_IP6_INTRUNCATEDPKTS, Ip6InTruncatedPkts),
+	__ADD(RTNL_LINK_IP6_INDISCARDS, Ip6InDiscards),
+	__ADD(RTNL_LINK_IP6_INDELIVERS, Ip6InDelivers),
+	__ADD(RTNL_LINK_IP6_OUTFORWDATAGRAMS, Ip6OutForwDatagrams),
+	__ADD(RTNL_LINK_IP6_OUTPKTS, Ip6OutRequests),
+	__ADD(RTNL_LINK_IP6_OUTDISCARDS, Ip6OutDiscards),
+	__ADD(RTNL_LINK_IP6_OUTNOROUTES, Ip6OutNoRoutes),
+	__ADD(RTNL_LINK_IP6_REASMTIMEOUT, Ip6ReasmTimeout),
+	__ADD(RTNL_LINK_IP6_REASMREQDS, Ip6ReasmReqds),
+	__ADD(RTNL_LINK_IP6_REASMOKS, Ip6ReasmOKs),
+	__ADD(RTNL_LINK_IP6_REASMFAILS, Ip6ReasmFails),
+	__ADD(RTNL_LINK_IP6_FRAGOKS, Ip6FragOKs),
+	__ADD(RTNL_LINK_IP6_FRAGFAILS, Ip6FragFails),
+	__ADD(RTNL_LINK_IP6_FRAGCREATES, Ip6FragCreates),
+	__ADD(RTNL_LINK_IP6_INMCASTPKTS, Ip6InMcastPkts),
+	__ADD(RTNL_LINK_IP6_OUTMCASTPKTS, Ip6OutMcastPkts),
+	__ADD(RTNL_LINK_IP6_INBCASTPKTS, Ip6InBcastPkts),
+	__ADD(RTNL_LINK_IP6_OUTBCASTPKTS, Ip6OutBcastPkts),
+	__ADD(RTNL_LINK_IP6_INOCTETS, Ip6InOctets),
+	__ADD(RTNL_LINK_IP6_OUTOCTETS, Ip6OutOctets),
+	__ADD(RTNL_LINK_IP6_INMCASTOCTETS, Ip6InMcastOctets),
+	__ADD(RTNL_LINK_IP6_OUTMCASTOCTETS, Ip6OutMcastOctets),
+	__ADD(RTNL_LINK_IP6_INBCASTOCTETS, Ip6InBcastOctets),
+	__ADD(RTNL_LINK_IP6_OUTBCASTOCTETS, Ip6OutBcastOctets),
+	__ADD(RTNL_LINK_ICMP6_INMSGS, ICMP6_InMsgs),
+	__ADD(RTNL_LINK_ICMP6_INERRORS, ICMP6_InErrors),
+	__ADD(RTNL_LINK_ICMP6_OUTMSGS, ICMP6_OutMsgs),
+	__ADD(RTNL_LINK_ICMP6_OUTERRORS, ICMP6_OutErrors),
+	__ADD(RTNL_LINK_ICMP6_CSUMERRORS, ICMP6_InCsumErrors),
+	__ADD(RTNL_LINK_IP6_CSUMERRORS, Ip6_InCsumErrors),
+	__ADD(RTNL_LINK_IP6_NOECTPKTS, Ip6_InNoECTPkts),
+	__ADD(RTNL_LINK_IP6_ECT1PKTS, Ip6_InECT1Pkts),
+	__ADD(RTNL_LINK_IP6_ECT0PKTS, Ip6_InECT0Pkts),
+	__ADD(RTNL_LINK_IP6_CEPKTS, Ip6_InCEPkts),
+	__ADD(RTNL_LINK_RX_NOHANDLER, rx_nohandler),
 };
 
 char *rtnl_link_stat2str(int st, char *buf, size_t len)
@@ -2620,13 +3076,13 @@
 }
 
 static const struct trans_tbl link_operstates[] = {
-	__ADD(IF_OPER_UNKNOWN, unknown)
-	__ADD(IF_OPER_NOTPRESENT, notpresent)
-	__ADD(IF_OPER_DOWN, down)
-	__ADD(IF_OPER_LOWERLAYERDOWN, lowerlayerdown)
-	__ADD(IF_OPER_TESTING, testing)
-	__ADD(IF_OPER_DORMANT, dormant)
-	__ADD(IF_OPER_UP, up)
+	__ADD(IF_OPER_UNKNOWN, unknown),
+	__ADD(IF_OPER_NOTPRESENT, notpresent),
+	__ADD(IF_OPER_DOWN, down),
+	__ADD(IF_OPER_LOWERLAYERDOWN, lowerlayerdown),
+	__ADD(IF_OPER_TESTING, testing),
+	__ADD(IF_OPER_DORMANT, dormant),
+	__ADD(IF_OPER_UP, up),
 };
 
 char *rtnl_link_operstate2str(uint8_t st, char *buf, size_t len)
@@ -2642,13 +3098,13 @@
 }
 
 static const struct trans_tbl link_modes[] = {
-	__ADD(IF_LINK_MODE_DEFAULT, default)
-	__ADD(IF_LINK_MODE_DORMANT, dormant)
+	__ADD(IF_LINK_MODE_DEFAULT, default),
+	__ADD(IF_LINK_MODE_DORMANT, dormant),
 };
 
 static const struct trans_tbl carrier_states[] = {
-	__ADD(IF_CARRIER_DOWN, down)
-	__ADD(IF_CARRIER_UP, up)
+	__ADD(0, down),
+	__ADD(1, up),
 };
 
 char *rtnl_link_mode2str(uint8_t st, char *buf, size_t len)
@@ -2672,6 +3128,31 @@
 	return __str2type(name, carrier_states, ARRAY_SIZE(carrier_states));
 }
 
+int rtnl_link_has_vf_list(struct rtnl_link *link) {
+	if (link->ce_mask & LINK_ATTR_VF_LIST)
+		return 1;
+	else
+		return 0;
+}
+
+void rtnl_link_set_vf_list(struct rtnl_link *link) {
+	int err;
+
+	if (!(err = rtnl_link_has_vf_list(link)))
+		link->ce_mask |= LINK_ATTR_VF_LIST;
+
+	return;
+}
+
+void rtnl_link_unset_vf_list(struct rtnl_link *link) {
+	int err;
+
+	if ((err = rtnl_link_has_vf_list(link)))
+		link->ce_mask &= ~LINK_ATTR_VF_LIST;
+
+	return;
+}
+
 /** @} */
 
 /**
diff --git a/lib/route/link/api.c b/lib/route/link/api.c
index 6d1e12f..d406783 100644
--- a/lib/route/link/api.c
+++ b/lib/route/link/api.c
@@ -391,6 +391,28 @@
 	return ret;
 }
 
+/**
+ * Compare link info data
+ * @arg a              Link object a
+ * @arg b              Link object b
+ *
+ * This function will compare link_info data between two links
+ * a and b
+ *
+ * @return 0 if link_info data matches or is not present
+ * or != 0 if it mismatches.
+ */
+int rtnl_link_info_data_compare(struct rtnl_link *a, struct rtnl_link *b, int flags)
+{
+	if (a->l_info_ops != b->l_info_ops)
+		return ~0;
+
+	if (!a->l_info_ops || !a->l_info_ops->io_compare)
+		return 0;
+
+	return a->l_info_ops->io_compare(a, b, flags);
+}
+
 /** @} */
 
 /** @} */
diff --git a/lib/route/link/bonding.c b/lib/route/link/bonding.c
index f4c520b..11b6d3d 100644
--- a/lib/route/link/bonding.c
+++ b/lib/route/link/bonding.c
@@ -22,6 +22,7 @@
 
 #include <netlink-private/netlink.h>
 #include <netlink/netlink.h>
+#include <netlink/route/link/bonding.h>
 #include <netlink-private/route/link/api.h>
 
 /**
diff --git a/lib/route/link/bridge.c b/lib/route/link/bridge.c
index 4ca5f6e..2d95faf 100644
--- a/lib/route/link/bridge.c
+++ b/lib/route/link/bridge.c
@@ -25,11 +25,16 @@
 #include <netlink-private/route/link/api.h>
 #include <linux/if_bridge.h>
 
+#define VLAN_VID_MASK           0x0fff /* VLAN Identifier */
+
 /** @cond SKIP */
 #define BRIDGE_ATTR_PORT_STATE		(1 << 0)
 #define BRIDGE_ATTR_PRIORITY		(1 << 1)
 #define BRIDGE_ATTR_COST		(1 << 2)
 #define BRIDGE_ATTR_FLAGS		(1 << 3)
+#define BRIDGE_ATTR_PORT_VLAN           (1 << 4)
+#define BRIDGE_ATTR_HWMODE		(1 << 5)
+#define BRIDGE_ATTR_SELF		(1 << 6)
 
 #define PRIV_FLAG_NEW_ATTRS		(1 << 0)
 
@@ -37,13 +42,38 @@
 {
 	uint8_t			b_port_state;
 	uint8_t			b_priv_flags; /* internal flags */
+	uint16_t		b_hwmode;
 	uint16_t		b_priority;
+	uint16_t		b_self; /* here for comparison reasons */
 	uint32_t		b_cost;
 	uint32_t		b_flags;
 	uint32_t		b_flags_mask;
 	uint32_t                ce_mask; /* HACK to support attr macros */
+	struct rtnl_link_bridge_vlan vlan_info;
 };
 
+static void set_bit(unsigned nr, uint32_t *addr)
+{
+	if (nr < RTNL_LINK_BRIDGE_VLAN_BITMAP_MAX)
+		addr[nr / 32] |= (((uint32_t) 1) << (nr % 32));
+}
+
+static int find_next_bit(int i, uint32_t x)
+{
+	int j;
+
+	if (i >= 32)
+		return -1;
+
+	/* find first bit */
+	if (i < 0)
+		return __builtin_ffs(x);
+
+	/* mask off prior finds to get next */
+	j = __builtin_ffs(x >> i);
+	return j ? j + i : 0;
+}
+
 static struct rtnl_link_af_ops bridge_ops;
 
 #define IS_BRIDGE_LINK_ASSERT(link) \
@@ -85,6 +115,9 @@
 	[IFLA_BRPORT_GUARD]		= { .type = NLA_U8 },
 	[IFLA_BRPORT_PROTECT]		= { .type = NLA_U8 },
 	[IFLA_BRPORT_FAST_LEAVE]	= { .type = NLA_U8 },
+	[IFLA_BRPORT_LEARNING]		= { .type = NLA_U8 },
+	[IFLA_BRPORT_LEARNING_SYNC]	= { .type = NLA_U8 },
+	[IFLA_BRPORT_UNICAST_FLOOD]	= { .type = NLA_U8 },
 };
 
 static void check_flag(struct rtnl_link *link, struct nlattr *attrs[],
@@ -137,10 +170,228 @@
 	check_flag(link, br_attrs, IFLA_BRPORT_GUARD, RTNL_BRIDGE_BPDU_GUARD);
 	check_flag(link, br_attrs, IFLA_BRPORT_PROTECT, RTNL_BRIDGE_ROOT_BLOCK);
 	check_flag(link, br_attrs, IFLA_BRPORT_FAST_LEAVE, RTNL_BRIDGE_FAST_LEAVE);
+	check_flag(link, br_attrs, IFLA_BRPORT_UNICAST_FLOOD,
+	           RTNL_BRIDGE_UNICAST_FLOOD);
+	check_flag(link, br_attrs, IFLA_BRPORT_LEARNING, RTNL_BRIDGE_LEARNING);
+	check_flag(link, br_attrs, IFLA_BRPORT_LEARNING_SYNC,
+	           RTNL_BRIDGE_LEARNING_SYNC);
 
 	return 0;
 }
 
+static int bridge_parse_af_full(struct rtnl_link *link, struct nlattr *attr_full,
+                                void *data)
+{
+	struct bridge_data *bd = data;
+	struct bridge_vlan_info *vinfo = NULL;
+	uint16_t vid_range_start = 0;
+	uint16_t vid_range_flags = -1;
+
+	struct nlattr *attr;
+	int remaining;
+
+	nla_for_each_nested(attr, attr_full, remaining) {
+
+		if (nla_type(attr) == IFLA_BRIDGE_MODE) {
+			bd->b_hwmode = nla_get_u16(attr);
+			bd->ce_mask |= BRIDGE_ATTR_HWMODE;
+		} else if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO)
+			continue;
+
+		if (nla_len(attr) != sizeof(struct bridge_vlan_info))
+			return -EINVAL;
+
+		vinfo = nla_data(attr);
+		if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK)
+			return -EINVAL;
+
+
+		if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
+			vid_range_start = vinfo->vid;
+			vid_range_flags = (vinfo->flags ^ BRIDGE_VLAN_INFO_RANGE_BEGIN);
+			continue;
+		}
+
+		if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) {
+			/* sanity check the range flags */
+			if (vid_range_flags != (vinfo->flags ^ BRIDGE_VLAN_INFO_RANGE_END)) {
+				NL_DBG(1, "VLAN range flags differ; can not handle it.\n");
+				return -EINVAL;
+			}
+		} else {
+			vid_range_start = vinfo->vid;
+		}
+
+		for (; vid_range_start <= vinfo->vid; vid_range_start++) {
+			if (vinfo->flags & BRIDGE_VLAN_INFO_PVID)
+				bd->vlan_info.pvid = vinfo->vid;
+
+			if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED)
+				set_bit(vid_range_start, bd->vlan_info.untagged_bitmap);
+
+			set_bit(vid_range_start, bd->vlan_info.vlan_bitmap);
+			bd->ce_mask |= BRIDGE_ATTR_PORT_VLAN;
+		}
+
+		vid_range_flags = -1;
+	}
+
+	return 0;
+}
+
+static int bridge_fill_af(struct rtnl_link *link, struct nl_msg *msg,
+		   void *data)
+{
+	struct bridge_data *bd = data;
+
+	if ((bd->ce_mask & BRIDGE_ATTR_SELF)||(bd->ce_mask & BRIDGE_ATTR_HWMODE))
+		NLA_PUT_U16(msg, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF);
+
+	if (bd->ce_mask & BRIDGE_ATTR_HWMODE)
+		NLA_PUT_U16(msg, IFLA_BRIDGE_MODE, bd->b_hwmode);
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_MSGSIZE;
+}
+
+static int bridge_fill_pi(struct rtnl_link *link, struct nl_msg *msg,
+		   void *data)
+{
+	struct bridge_data *bd = data;
+
+	if (bd->ce_mask & BRIDGE_ATTR_FLAGS) {
+		if (bd->b_flags_mask & RTNL_BRIDGE_BPDU_GUARD) {
+			NLA_PUT_U8(msg, IFLA_BRPORT_GUARD,
+						bd->b_flags & RTNL_BRIDGE_BPDU_GUARD);
+		}
+		if (bd->b_flags_mask & RTNL_BRIDGE_HAIRPIN_MODE) {
+			NLA_PUT_U8(msg, IFLA_BRPORT_MODE,
+			           bd->b_flags & RTNL_BRIDGE_HAIRPIN_MODE);
+		}
+		if (bd->b_flags_mask & RTNL_BRIDGE_FAST_LEAVE) {
+			NLA_PUT_U8(msg, IFLA_BRPORT_FAST_LEAVE,
+			           bd->b_flags & RTNL_BRIDGE_FAST_LEAVE);
+		}
+		if (bd->b_flags_mask & RTNL_BRIDGE_ROOT_BLOCK) {
+			NLA_PUT_U8(msg, IFLA_BRPORT_PROTECT,
+			           bd->b_flags & RTNL_BRIDGE_ROOT_BLOCK);
+		}
+		if (bd->b_flags_mask & RTNL_BRIDGE_UNICAST_FLOOD) {
+			NLA_PUT_U8(msg, IFLA_BRPORT_UNICAST_FLOOD,
+			           bd->b_flags & RTNL_BRIDGE_UNICAST_FLOOD);
+		}
+		if (bd->b_flags_mask & RTNL_BRIDGE_LEARNING) {
+			NLA_PUT_U8(msg, IFLA_BRPORT_LEARNING,
+			           bd->b_flags & RTNL_BRIDGE_LEARNING);
+		}
+		if (bd->b_flags_mask & RTNL_BRIDGE_LEARNING_SYNC) {
+			NLA_PUT_U8(msg, IFLA_BRPORT_LEARNING_SYNC,
+			           bd->b_flags & RTNL_BRIDGE_LEARNING_SYNC);
+		}
+	}
+
+	if (bd->ce_mask & BRIDGE_ATTR_COST)
+		NLA_PUT_U32(msg, IFLA_BRPORT_COST, bd->b_cost);
+
+	if (bd->ce_mask & BRIDGE_ATTR_PRIORITY)
+		NLA_PUT_U16(msg, IFLA_BRPORT_PRIORITY, bd->b_priority);
+
+	if (bd->ce_mask & BRIDGE_ATTR_PORT_STATE)
+		NLA_PUT_U8(msg, IFLA_BRPORT_STATE, bd->b_port_state);
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_MSGSIZE;
+}
+
+static int bridge_override_rtm(struct rtnl_link *link) {
+        struct bridge_data *bd;
+
+        if (!rtnl_link_is_bridge(link))
+                return 0;
+
+        bd = bridge_data(link);
+
+        if (bd->ce_mask & BRIDGE_ATTR_FLAGS)
+                return 1;
+
+        return 0;
+}
+
+static int bridge_get_af(struct nl_msg *msg, uint32_t *ext_filter_mask)
+{
+	*ext_filter_mask |= RTEXT_FILTER_BRVLAN;
+	return 0;
+}
+
+static void dump_bitmap(struct nl_dump_params *p, const uint32_t *b)
+{
+	int i = -1, j, k;
+	int start = -1, prev = -1;
+	int done, found = 0;
+
+	for (k = 0; k < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN; k++) {
+		int base_bit;
+		uint32_t a = b[k];
+
+		base_bit = k * 32;
+		i = -1;
+		done = 0;
+		while (!done) {
+			j = find_next_bit(i, a);
+			if (j > 0) {
+				/* first hit of any bit */
+				if (start < 0 && prev < 0) {
+					start = prev = j - 1 + base_bit;
+					goto next;
+				}
+				/* this bit is a continuation of prior bits */
+				if (j - 2 + base_bit == prev) {
+					prev++;
+					goto next;
+				}
+			} else
+				done = 1;
+
+			if (start >= 0) {
+				found++;
+				if (done && k < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN - 1)
+					break;
+
+				nl_dump(p, " %d", start);
+				if (start != prev)
+					nl_dump(p, "-%d", prev);
+
+				if (done)
+					break;
+			}
+			if (j > 0)
+				start = prev = j - 1 + base_bit;
+next:
+			i = j;
+		}
+	}
+	if (!found)
+		nl_dump(p, " <none>");
+
+	return;
+}
+
+static void rtnl_link_bridge_dump_vlans(struct nl_dump_params *p,
+					struct bridge_data *bd)
+{
+	nl_dump(p, "pvid %u", bd->vlan_info.pvid);
+
+	nl_dump(p, "   all vlans:");
+	dump_bitmap(p, bd->vlan_info.vlan_bitmap);
+
+	nl_dump(p, "   untagged vlans:");
+	dump_bitmap(p, bd->vlan_info.untagged_bitmap);
+}
+
 static void bridge_dump_details(struct rtnl_link *link,
 				struct nl_dump_params *p, void *data)
 {
@@ -157,6 +408,24 @@
 	if (bd->ce_mask & BRIDGE_ATTR_COST)
 		nl_dump(p, "cost %u ", bd->b_cost);
 
+	if (bd->ce_mask & BRIDGE_ATTR_HWMODE) {
+		char hbuf[32];
+
+		rtnl_link_bridge_hwmode2str(bd->b_hwmode, hbuf, sizeof(hbuf));
+		nl_dump(p, "hwmode %s", hbuf);
+	}
+
+	if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN)
+		rtnl_link_bridge_dump_vlans(p, bd);
+
+	if (bd->ce_mask & BRIDGE_ATTR_FLAGS) {
+		char buf[256];
+
+		rtnl_link_bridge_flags2str(bd->b_flags & bd->b_flags_mask,
+					   buf, sizeof(buf));
+		nl_dump(p, "%s", buf);
+	}
+
 	nl_dump(p, "\n");
 }
 
@@ -171,6 +440,10 @@
 	diff |= BRIDGE_DIFF(PORT_STATE,	a->b_port_state != b->b_port_state);
 	diff |= BRIDGE_DIFF(PRIORITY, a->b_priority != b->b_priority);
 	diff |= BRIDGE_DIFF(COST, a->b_cost != b->b_cost);
+	diff |= BRIDGE_DIFF(PORT_VLAN, memcmp(&a->vlan_info, &b->vlan_info,
+					      sizeof(struct rtnl_link_bridge_vlan)));
+	diff |= BRIDGE_DIFF(HWMODE, a->b_hwmode != b->b_hwmode);
+	diff |= BRIDGE_DIFF(SELF, a->b_self != b->b_self);
 
 	if (flags & LOOSE_COMPARISON)
 		diff |= BRIDGE_DIFF(FLAGS,
@@ -203,8 +476,8 @@
 
 	return link;
 }
-		
-/** 
+
+/**
  * Create a new kernel bridge device
  * @arg sk              netlink socket
  * @arg name            name of the bridge device or NULL
@@ -440,6 +713,9 @@
  *   - RTNL_BRIDGE_BPDU_GUARD
  *   - RTNL_BRIDGE_ROOT_BLOCK
  *   - RTNL_BRIDGE_FAST_LEAVE
+ *   - RTNL_BRIDGE_UNICAST_FLOOD
+ *   - RTNL_BRIDGE_LEARNING
+ *   - RTNL_BRIDGE_LEARNING_SYNC
  *
  * @see rtnl_link_bridge_unset_flags()
  * @see rtnl_link_bridge_get_flags()
@@ -479,11 +755,99 @@
 	return bd->b_flags;
 }
 
+/**
+ * Set link change type to self
+ * @arg link		Link Object of type bridge
+ *
+ * This will set the bridge change flag to self, meaning that changes to
+ * be applied with this link object will be applied directly to the physical
+ * device in a bridge instead of the virtual device.
+ *
+ * @return 0 on success or negative error code
+ * @return -NLE_OPNOTSUP Link is not a bridge
+ */
+int rtnl_link_bridge_set_self(struct rtnl_link *link)
+{
+	struct bridge_data *bd = bridge_data(link);
+
+	IS_BRIDGE_LINK_ASSERT(link);
+
+	bd->b_self |= 1;
+	bd->ce_mask |= BRIDGE_ATTR_SELF;
+
+	return 0;
+}
+
+/**
+ * Get hardware mode
+ * @arg link            Link object of type bridge
+ * @arg hwmode          Output argument.
+ *
+ * @see rtnl_link_bridge_set_hwmode()
+ *
+ * @return 0 if hardware mode is present and returned in hwmode
+ * @return -NLE_NOATTR if hardware mode is not present
+ * @return -NLE_OPNOTSUP Link is not a bridge
+ */
+int rtnl_link_bridge_get_hwmode(struct rtnl_link *link, uint16_t *hwmode)
+{
+	struct bridge_data *bd = bridge_data(link);
+
+	IS_BRIDGE_LINK_ASSERT(link);
+
+	if (!(bd->ce_mask & BRIDGE_ATTR_HWMODE))
+		return -NLE_NOATTR;
+
+	*hwmode = bd->b_hwmode;
+	return 0;
+}
+
+/**
+ * Set hardware mode
+ * @arg link		Link object of type bridge
+ * @arg hwmode		Hardware mode to set on link
+ *
+ * This will set the hardware mode of a link when it supports hardware
+ * offloads for bridging.
+ * @see rtnl_link_bridge_get_hwmode()
+ *
+ * Valid modes are:
+ *   - RTNL_BRIDGE_HWMODE_VEB
+ *   - RTNL_BRIDGE_HWMODE_VEPA
+ *
+ * When setting hardware mode, the change type will be set to self.
+ * @see rtnl_link_bridge_set_self()
+ *
+ * @return 0 on success or negative error code
+ * @return -NLE_OPNOTSUP Link is not a bridge
+ * @return -NLE_INVAL when specified hwmode is unsupported.
+ */
+int rtnl_link_bridge_set_hwmode(struct rtnl_link *link, uint16_t hwmode)
+{
+	int err;
+	struct bridge_data *bd = bridge_data(link);
+
+	if (hwmode > RTNL_BRIDGE_HWMODE_MAX)
+		return -NLE_INVAL;
+
+	if ((err = rtnl_link_bridge_set_self(link)) < 0)
+		return err;
+
+	bd->b_hwmode = hwmode;
+	bd->ce_mask |= BRIDGE_ATTR_HWMODE;
+
+	return 0;
+}
+
+
 static const struct trans_tbl bridge_flags[] = {
-	__ADD(RTNL_BRIDGE_HAIRPIN_MODE, hairpin_mode)
-	__ADD(RTNL_BRIDGE_BPDU_GUARD, 	bpdu_guard)
-	__ADD(RTNL_BRIDGE_ROOT_BLOCK,	root_block)
-	__ADD(RTNL_BRIDGE_FAST_LEAVE,	fast_leave)
+	__ADD(RTNL_BRIDGE_HAIRPIN_MODE, hairpin_mode),
+	__ADD(RTNL_BRIDGE_BPDU_GUARD, 	bpdu_guard),
+	__ADD(RTNL_BRIDGE_ROOT_BLOCK,	root_block),
+	__ADD(RTNL_BRIDGE_FAST_LEAVE,	fast_leave),
+	__ADD(RTNL_BRIDGE_UNICAST_FLOOD,	flood),
+	__ADD(RTNL_BRIDGE_LEARNING,			learning),
+	__ADD(RTNL_BRIDGE_LEARNING_SYNC,	learning_sync),
 };
 
 /**
@@ -503,6 +867,101 @@
 
 /** @} */
 
+static const struct trans_tbl port_states[] = {
+	__ADD(BR_STATE_DISABLED, disabled),
+	__ADD(BR_STATE_LISTENING, listening),
+	__ADD(BR_STATE_LEARNING, learning),
+	__ADD(BR_STATE_FORWARDING, forwarding),
+	__ADD(BR_STATE_BLOCKING, blocking),
+};
+
+/**
+ * @name Port State Translation
+ * @{
+ */
+
+char *rtnl_link_bridge_portstate2str(int st, char *buf, size_t len)
+{
+	return __type2str(st, buf, len, port_states, ARRAY_SIZE(port_states));
+}
+
+int rtnl_link_bridge_str2portstate(const char *name)
+{
+	return __str2type(name, port_states, ARRAY_SIZE(port_states));
+}
+
+/** @} */
+
+static const struct trans_tbl hw_modes[] = {
+	__ADD(RTNL_BRIDGE_HWMODE_VEB, veb),
+	__ADD(RTNL_BRIDGE_HWMODE_VEPA, vepa),
+	__ADD(RTNL_BRIDGE_HWMODE_UNDEF, undef),
+};
+
+/**
+ * @name Hardware Mode Translation
+ * @{
+ */
+
+char *rtnl_link_bridge_hwmode2str(uint16_t st, char *buf, size_t len) {
+	return __type2str(st, buf, len, hw_modes, ARRAY_SIZE(hw_modes));
+}
+
+uint16_t rtnl_link_bridge_str2hwmode(const char *name)
+{
+	return __str2type(name, hw_modes, ARRAY_SIZE(hw_modes));
+}
+
+/** @} */
+
+int rtnl_link_bridge_pvid(struct rtnl_link *link)
+{
+	struct bridge_data *bd;
+
+	IS_BRIDGE_LINK_ASSERT(link);
+
+	bd = link->l_af_data[AF_BRIDGE];
+	if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN)
+		return (int) bd->vlan_info.pvid;
+
+	return -EINVAL;
+}
+
+int rtnl_link_bridge_has_vlan(struct rtnl_link *link)
+{
+	struct bridge_data *bd;
+	int i;
+
+	IS_BRIDGE_LINK_ASSERT(link);
+
+	bd = link->l_af_data[AF_BRIDGE];
+	if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN) {
+		if (bd->vlan_info.pvid)
+			return 1;
+
+		for (i = 0; i < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN; ++i) {
+			if (bd->vlan_info.vlan_bitmap[i] ||
+			    bd->vlan_info.untagged_bitmap[i])
+				return 1;
+		}
+	}
+	return 0;
+}
+
+struct rtnl_link_bridge_vlan *rtnl_link_bridge_get_port_vlan(struct rtnl_link *link)
+{
+	struct bridge_data *data;
+
+	if (!rtnl_link_is_bridge(link))
+		return NULL;
+
+	data = link->l_af_data[AF_BRIDGE];
+	if (data && (data->ce_mask & BRIDGE_ATTR_PORT_VLAN))
+		return &data->vlan_info;
+
+	return NULL;
+}
+
 static struct rtnl_link_af_ops bridge_ops = {
 	.ao_family			= AF_BRIDGE,
 	.ao_alloc			= &bridge_alloc,
@@ -511,6 +970,13 @@
 	.ao_parse_protinfo		= &bridge_parse_protinfo,
 	.ao_dump[NL_DUMP_DETAILS]	= &bridge_dump_details,
 	.ao_compare			= &bridge_compare,
+	.ao_parse_af_full		= &bridge_parse_af_full,
+	.ao_get_af			= &bridge_get_af,
+	.ao_fill_af			= &bridge_fill_af,
+	.ao_fill_pi			= &bridge_fill_pi,
+	.ao_fill_pi_flags	= NLA_F_NESTED,
+	.ao_override_rtm		= &bridge_override_rtm,
+	.ao_fill_af_no_nest	= 1,
 };
 
 static void __init bridge_init(void)
diff --git a/lib/route/link/can.c b/lib/route/link/can.c
index 60da42d..884121f 100644
--- a/lib/route/link/can.c
+++ b/lib/route/link/can.c
@@ -73,11 +73,15 @@
 {
 	struct can_info *ci;
 
-	ci = calloc(1, sizeof(*ci));
-	if (!ci)
-		return -NLE_NOMEM;
+	if (link->l_info)
+		memset(link->l_info, 0, sizeof(*ci));
+	else {
+		ci = calloc(1, sizeof(*ci));
+		if (!ci)
+			return -NLE_NOMEM;
 
-	link->l_info = ci;
+		link->l_info = ci;
+	}
 
 	return 0;
 }
@@ -89,7 +93,7 @@
 	struct can_info *ci;
 	int err;
 
-	NL_DBG(3, "Parsing CAN link info");
+	NL_DBG(3, "Parsing CAN link info\n");
 
 	if ((err = nla_parse_nested(tb, IFLA_CAN_MAX, data, can_policy)) < 0)
 		goto errout;
@@ -751,11 +755,11 @@
  */
 
 static const struct trans_tbl can_ctrlmode[] = {
-	__ADD(CAN_CTRLMODE_LOOPBACK, loopback)
-	__ADD(CAN_CTRLMODE_LISTENONLY, listen-only)
-	__ADD(CAN_CTRLMODE_3_SAMPLES, triple-sampling)
-	__ADD(CAN_CTRLMODE_ONE_SHOT, one-shot)
-	__ADD(CAN_CTRLMODE_BERR_REPORTING, berr-reporting)
+	__ADD(CAN_CTRLMODE_LOOPBACK, loopback),
+	__ADD(CAN_CTRLMODE_LISTENONLY, listen-only),
+	__ADD(CAN_CTRLMODE_3_SAMPLES, triple-sampling),
+	__ADD(CAN_CTRLMODE_ONE_SHOT, one-shot),
+	__ADD(CAN_CTRLMODE_BERR_REPORTING, berr-reporting),
 };
 
 char *rtnl_link_can_ctrlmode2str(int ctrlmode, char *buf, size_t len)
diff --git a/lib/route/link/geneve.c b/lib/route/link/geneve.c
new file mode 100644
index 0000000..7232b07
--- /dev/null
+++ b/lib/route/link/geneve.c
@@ -0,0 +1,810 @@
+/*
+ * lib/route/link/geneve.c      Geneve Link Info
+ *         This library is free software; you can redistribute it and/or
+ *         modify it under the terms of the GNU Lesser General Public
+ *         License as published by the Free Software Foundation version 2.1
+ *         of the License.
+ *
+ * Copyright (c) 2018 Wang Jian <jianjian.wang1@gmail.com>
+ */
+/**
+ * @ingroup link
+ * @defgroup geneve Geneve
+ * Generic Network Virtualization Encapsulation
+ *
+ * @details
+ * \b Link Type Name: "geneve"
+ *
+ * @route_doc{link_geneve, Geneve Documentation}
+ *
+ * @{
+ */
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+#include <netlink/route/link/geneve.h>
+
+
+/** @cond SKIP */
+#define GENEVE_ATTR_ID          (1<<0)
+#define GENEVE_ATTR_REMOTE      (1<<1)
+#define GENEVE_ATTR_REMOTE6     (1<<2)
+#define GENEVE_ATTR_TTL         (1<<3)
+#define GENEVE_ATTR_TOS         (1<<4)
+#define GENEVE_ATTR_LABEL       (1<<5)
+#define GENEVE_ATTR_PORT        (1<<6)
+#define GENEVE_ATTR_FLAGS       (1<<7)
+#define GENEVE_ATTR_UDP_CSUM    (1<<8)
+#define GENEVE_ATTR_UDP_ZERO_CSUM6_TX   (1<<9)
+#define GENEVE_ATTR_UDP_ZERO_CSUM6_RX   (1<<10)
+
+struct geneve_info
+{
+        uint32_t        id;
+        uint32_t        remote;
+        struct in6_addr remote6;
+        uint8_t         ttl;
+        uint8_t         tos;
+        uint32_t        label;
+        uint16_t        port;
+        uint8_t         flags;
+        uint8_t         udp_csum;
+        uint8_t         udp_zero_csum6_tx;
+        uint8_t         udp_zero_csum6_rx;
+        uint32_t        mask;
+};
+
+/** @endcond */
+
+static struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
+        [IFLA_GENEVE_ID] = { .type = NLA_U32 },
+        [IFLA_GENEVE_REMOTE]    = { .minlen = sizeof(uint32_t) },
+        [IFLA_GENEVE_REMOTE6]   = { .minlen = sizeof(struct in6_addr) },
+        [IFLA_GENEVE_TTL]       = { .type = NLA_U8 },
+        [IFLA_GENEVE_TOS]       = { .type = NLA_U8 },
+        [IFLA_GENEVE_LABEL]     = { .type = NLA_U32 },
+        [IFLA_GENEVE_PORT]      = { .type = NLA_U16 },
+        [IFLA_GENEVE_COLLECT_METADATA]  = { .type = NLA_FLAG },
+        [IFLA_GENEVE_UDP_CSUM]  = { .type = NLA_U8 },
+        [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 },
+        [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 },
+};
+
+static int geneve_alloc(struct rtnl_link *link)
+{
+        struct geneve_info *geneve;
+
+        if (link->l_info)
+                memset(link->l_info, 0, sizeof(*geneve));
+        else {
+                if ((geneve = calloc(1, sizeof(*geneve))) == NULL)
+                                return -NLE_NOMEM;
+                link->l_info = geneve;
+        }
+
+        return 0;
+}
+
+static int geneve_parse(struct rtnl_link *link, struct nlattr *data,
+                        struct nlattr *xstats)
+{
+        struct nlattr *tb[IFLA_GENEVE_MAX + 1];
+        struct geneve_info *geneve;
+        int err = 0;
+
+        NL_DBG(3, "Parsing Geneve link info\n");
+
+        err = nla_parse_nested(tb, IFLA_GENEVE_MAX, data, geneve_policy);
+        if (err < 0)
+                return err;
+
+        err = geneve_alloc(link);
+        if (err < 0)
+                return err;
+
+        geneve = link->l_info;
+
+        if (tb[IFLA_GENEVE_ID]) {
+                geneve->id = nla_get_u32(tb[IFLA_GENEVE_ID]);
+                geneve->mask |= GENEVE_ATTR_ID;
+        }
+
+        if (tb[IFLA_GENEVE_REMOTE]) {
+                nla_memcpy(&geneve->remote, tb[IFLA_GENEVE_REMOTE],
+                                sizeof(geneve->remote));
+                geneve->mask |= GENEVE_ATTR_REMOTE;
+                geneve->mask &= ~GENEVE_ATTR_REMOTE6;
+        }
+        if (tb[IFLA_GENEVE_REMOTE6]) {
+                nla_memcpy(&geneve->remote6, tb[IFLA_GENEVE_REMOTE6],
+                                sizeof(geneve->remote6));
+                geneve->mask |= GENEVE_ATTR_REMOTE6;
+                geneve->mask &= ~GENEVE_ATTR_REMOTE;
+        }
+
+        if (tb[IFLA_GENEVE_TTL]) {
+                geneve->ttl = nla_get_u8(tb[IFLA_GENEVE_TTL]);
+                geneve->mask |= GENEVE_ATTR_TTL;
+        }
+
+        if (tb[IFLA_GENEVE_TOS]) {
+                geneve->tos = nla_get_u8(tb[IFLA_GENEVE_TOS]);
+                geneve->mask |= GENEVE_ATTR_TOS;
+        }
+
+        if (tb[IFLA_GENEVE_LABEL]) {
+                geneve->label = nla_get_u32(tb[IFLA_GENEVE_LABEL]);
+                geneve->mask |= GENEVE_ATTR_LABEL;
+        }
+
+        if (tb[IFLA_GENEVE_PORT]) {
+                geneve->port  = nla_get_u16(tb[IFLA_GENEVE_PORT]);
+                geneve->mask |= GENEVE_ATTR_PORT;
+        }
+
+        if (tb[IFLA_GENEVE_COLLECT_METADATA])
+                geneve->flags |= RTNL_LINK_GENEVE_F_COLLECT_METADATA;
+
+        if (tb[IFLA_GENEVE_UDP_CSUM]) {
+                geneve->udp_csum = nla_get_u8(tb[IFLA_GENEVE_UDP_CSUM]);
+                geneve->mask |= GENEVE_ATTR_UDP_CSUM;
+        }
+
+        if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) {
+                geneve->udp_zero_csum6_tx = nla_get_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]);
+                geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_TX;
+        }
+
+        if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) {
+                geneve->udp_zero_csum6_rx = nla_get_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]);
+                geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_RX;
+        }
+
+        return err;
+}
+
+static void geneve_free(struct rtnl_link *link)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        free(geneve);
+        link->l_info = NULL;
+}
+
+static void geneve_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        nl_dump(p, "geneve-id %u", geneve->id);
+}
+
+static void geneve_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
+{
+        struct geneve_info *geneve = link->l_info;
+        char addr[INET6_ADDRSTRLEN];
+
+        nl_dump_line(p, "    geneve-id %u\n", geneve->id);
+
+        if (geneve->mask & GENEVE_ATTR_REMOTE) {
+                nl_dump(p, "     remote ");
+                if (inet_ntop(AF_INET, &geneve->remote, addr, sizeof(addr)))
+                        nl_dump_line(p, "%s\n", addr);
+                else
+                        nl_dump_line(p, "%#x\n", ntohs(geneve->remote));
+        } else if (geneve->mask & GENEVE_ATTR_REMOTE6) {
+                nl_dump(p, "      remote ");
+                if (inet_ntop(AF_INET6, &geneve->remote6, addr, sizeof(addr)))
+                        nl_dump_line(p, "%s\n", addr);
+                else
+                        nl_dump_line(p, "%#x\n", geneve->remote6);
+        }
+
+        if (geneve->mask & GENEVE_ATTR_TTL) {
+                nl_dump(p, "      ttl ");
+                nl_dump_line(p, "%u\n", geneve->ttl);
+        }
+
+        if (geneve->mask & GENEVE_ATTR_TOS) {
+                nl_dump(p, "      tos ");
+                nl_dump_line(p, "%u\n", geneve->tos);
+        }
+
+        if (geneve->mask & GENEVE_ATTR_PORT) {
+                nl_dump(p, "      port ");
+                nl_dump_line(p, "%u\n", ntohs(geneve->port));
+        }
+
+        if (geneve->mask & GENEVE_ATTR_LABEL) {
+                nl_dump(p, "      label ");
+                nl_dump_line(p, "%u\n", ntohl(geneve->label));
+        }
+
+        if (geneve->mask & GENEVE_ATTR_UDP_CSUM) {
+                nl_dump(p, "      UDP checksum ");
+                if (geneve->udp_csum)
+                        nl_dump_line(p, "enabled (%#x)\n", geneve->udp_csum);
+                else
+                        nl_dump_line(p, "disabled\n");
+        }
+
+        if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_TX) {
+                nl_dump(p, "      udp-zero-csum6-tx ");
+                if (geneve->udp_zero_csum6_tx)
+                        nl_dump_line(p, "enabled (%#x)\n", geneve->udp_zero_csum6_tx);
+                else
+                        nl_dump_line(p, "disabled\n");
+        }
+
+        if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_RX) {
+                nl_dump(p, "      udp-zero-csum6-rx ");
+                if (geneve->udp_zero_csum6_tx)
+                        nl_dump_line(p, "enabled (%#x)\n", geneve->udp_zero_csum6_rx);
+                else
+                        nl_dump_line(p, "disabled\n");
+        }
+
+        if (geneve->flags & RTNL_LINK_GENEVE_F_COLLECT_METADATA)
+                nl_dump(p, "      collect-metadata\n");
+}
+
+static int geneve_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+        struct geneve_info *gdst, *gsrc;
+        int err;
+
+        gsrc = src->l_info;
+        dst->l_info = NULL;
+        err = rtnl_link_set_type(dst, "geneve");
+        if (err < 0)
+                return err;
+
+        gdst = dst->l_info;
+
+        if (!gsrc || !gdst)
+                return -NLE_NOMEM;
+
+        memcpy(gdst, gsrc, sizeof(struct geneve_info));
+
+        return 0;
+}
+
+static int geneve_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+        struct geneve_info  *geneve = link->l_info;
+        struct nlattr *data;
+
+        if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
+                return -NLE_MSGSIZE;
+
+        if (geneve->mask & GENEVE_ATTR_ID)
+                NLA_PUT_U32(msg, IFLA_GENEVE_ID, geneve->id);
+
+        if (geneve->mask & GENEVE_ATTR_REMOTE)
+                NLA_PUT(msg, IFLA_GENEVE_REMOTE,
+                                sizeof(geneve->remote), &geneve->remote);
+
+        if (geneve->mask & GENEVE_ATTR_REMOTE6)
+                NLA_PUT(msg, IFLA_GENEVE_REMOTE6,
+                                sizeof(geneve->remote6), &geneve->remote6);
+
+        if (geneve->mask & GENEVE_ATTR_TTL)
+                NLA_PUT_U8(msg, IFLA_GENEVE_TTL, geneve->ttl);
+
+        if (geneve->mask & GENEVE_ATTR_TOS)
+                NLA_PUT_U8(msg, IFLA_GENEVE_TOS, geneve->tos);
+
+        if (geneve->mask & GENEVE_ATTR_LABEL)
+                NLA_PUT_U32(msg, IFLA_GENEVE_LABEL, geneve->label);
+
+        if (geneve->mask & GENEVE_ATTR_PORT)
+                NLA_PUT_U32(msg, IFLA_GENEVE_PORT, geneve->port);
+
+        if (geneve->mask & GENEVE_ATTR_UDP_CSUM)
+                NLA_PUT_U8(msg, IFLA_GENEVE_UDP_CSUM, geneve->udp_csum);
+
+        if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_TX)
+                NLA_PUT_U8(msg, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, geneve->udp_zero_csum6_tx);
+
+        if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_RX)
+                NLA_PUT_U8(msg, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, geneve->udp_zero_csum6_rx);
+
+        if (geneve->flags & RTNL_LINK_GENEVE_F_COLLECT_METADATA)
+                NLA_PUT_FLAG(msg, IFLA_GENEVE_COLLECT_METADATA);
+
+        nla_nest_end(msg, data);
+
+nla_put_failure:
+
+        return 0;
+}
+
+static struct rtnl_link_info_ops geneve_info_ops = {
+        .io_name        = "geneve",
+        .io_alloc       = geneve_alloc,
+        .io_parse       = geneve_parse,
+        .io_dump        = {
+                [NL_DUMP_LINE]          = geneve_dump_line,
+                [NL_DUMP_DETAILS]       = geneve_dump_details,
+        },
+        .io_clone       = geneve_clone,
+        .io_put_attrs   = geneve_put_attrs,
+        .io_free        = geneve_free,
+};
+
+
+/** @cond SKIP */
+#define IS_GENEVE_LINK_ASSERT(link) \
+        if ((link)->l_info_ops != &geneve_info_ops) { \
+                APPBUG("Link is not a geneve link. set type \"geneve\" first."); \
+                return -NLE_OPNOTSUPP; \
+        }
+/** @endcond */
+
+/**
+ * @name Geneve Object
+ * @{
+ */
+
+/**
+ * Allocate link object of type Geneve
+ *
+ * @return Allocated link object or NULL.
+ */
+struct rtnl_link *rtnl_link_geneve_alloc(void)
+{
+        struct rtnl_link *link;
+        int err;
+
+        if (!(link = rtnl_link_alloc()))
+                return NULL;
+
+        if ((err = rtnl_link_set_type(link, "geneve")) < 0) {
+                rtnl_link_put(link);
+                return NULL;
+        }
+
+        return link;
+}
+
+/**
+ * Check if link is a Geneve link
+ * @arg link    Link object
+ *
+ * @return True if link is a Geneve link, otherwisee false is returned.
+ */
+int rtnl_link_is_geneve(struct rtnl_link *link)
+{
+        return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "geneve");
+}
+
+/**
+ * Set Geneve Network Indentifier
+ * @arg link    Link object
+ * @arg id      Geneve network identifier
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_id(struct rtnl_link *link, uint32_t id)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        if (id > RTNL_GENEVE_ID_MAX)
+                return -NLE_INVAL;
+
+        geneve->id = id;
+        geneve->mask |= GENEVE_ATTR_ID;
+
+        return 0;
+}
+
+/**
+ * Get Geneve Network Identifier
+ * @arg link    Link object
+ * @arg id      Pointer to store network identifier
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_get_id(struct rtnl_link *link, uint32_t *id)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        if (!id)
+                return -NLE_INVAL;
+
+        if (geneve->mask & GENEVE_ATTR_ID)
+                *id = geneve->id;
+        else
+                return -NLE_AGAIN;
+
+        return 0;
+}
+
+/**
+ * Set Geneve unicast destination IP address
+ * @arg link    Link object
+ * @arg addr    The unicast destination IP address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_remote(struct rtnl_link *link, struct nl_addr *addr)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        if ((nl_addr_get_family(addr) == AF_INET) &&
+                (nl_addr_get_len(addr) == sizeof(geneve->remote))) {
+                memcpy(&geneve->remote, nl_addr_get_binary_addr(addr),
+                                sizeof(geneve->remote));
+                geneve->mask |= GENEVE_ATTR_REMOTE;
+                geneve->mask &= ~GENEVE_ATTR_REMOTE6;
+        } else if ((nl_addr_get_family(addr) == AF_INET6) &&
+                        (nl_addr_get_len(addr) == sizeof(geneve->remote6))) {
+                memcpy(&geneve->remote6, nl_addr_get_binary_addr(addr),
+                                sizeof(geneve->remote6));
+                geneve->mask |= GENEVE_ATTR_REMOTE6;
+                geneve->mask &= ~GENEVE_ATTR_REMOTE;
+        } else
+                return -NLE_INVAL;
+
+        return 0;
+}
+
+/**
+ * Get Geneve unicast destination IP address
+ * @arg link    Link object
+ * @arg addr    Pointer to store unicast destination IP addree
+ *
+ * @return 0 on success or a a negative error code
+ */
+int rtnl_link_geneve_get_remote(struct rtnl_link *link, struct nl_addr **addr)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        if (!addr)
+                return -NLE_INVAL;
+
+        if (geneve->mask & GENEVE_ATTR_REMOTE)
+                *addr = nl_addr_build(AF_INET, &geneve->remote, sizeof(geneve->remote));
+        else if (geneve->mask & GENEVE_ATTR_REMOTE6)
+                *addr = nl_addr_build(AF_INET6, &geneve->remote6, sizeof(geneve->remote6));
+        else
+                return -NLE_AGAIN;
+
+        return 0;
+}
+
+/**
+ * Set IP TTL value to use for Geneve
+ * @arg link    Link object
+ * @arg ttl     TTL value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_ttl(struct rtnl_link *link, uint8_t ttl)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        geneve->ttl = ttl;
+        geneve->mask |= GENEVE_ATTR_TTL;
+
+        return 0;
+}
+
+/**
+ * Get IP TTL value to use for Geneve
+ * @arg link    Link object
+ *
+ * @return TTL value on success or a negative error code
+ */
+int rtnl_link_geneve_get_ttl(struct rtnl_link *link)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        if (!(geneve->mask & GENEVE_ATTR_TTL))
+                return -NLE_AGAIN;
+
+        return geneve->ttl;
+}
+
+/**
+ * Set IP ToS value to use for Geneve
+ * @arg link    Link object
+ * @arg tos     ToS value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_tos(struct rtnl_link *link, uint8_t tos)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        geneve->tos = tos;
+        geneve->mask |= GENEVE_ATTR_TOS;
+
+        return 0;
+}
+
+/**
+ * Get IP ToS value to use for Geneve
+ * @arg link    Link object
+ *
+ * @return ToS value on success or a negative error code
+ */
+int rtnl_link_geneve_get_tos(struct rtnl_link *link)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        if (!(geneve->mask & GENEVE_ATTR_TOS))
+                return -NLE_AGAIN;
+
+        return geneve->tos;
+}
+
+/**
+ * Set UDP destination port to use for Geneve
+ * @arg link    Link object
+ * @arg port    Destination port
+ *
+ * @return 0 on success or a negative error code
+ */
+
+int rtnl_link_geneve_set_port(struct rtnl_link *link,  uint32_t port)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        geneve->port = htons(port);
+        geneve->mask |= GENEVE_ATTR_PORT;
+
+        return 0;
+}
+
+/**
+ * Get UDP destination port to use for Geneve
+ * @arg link    Link object
+ * @arg port    Pointer to store destination port
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_get_port(struct rtnl_link *link, uint32_t *port)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        if (!port)
+                return -NLE_INVAL;
+
+        if (!(geneve->mask & GENEVE_ATTR_PORT))
+                return -NLE_NOATTR;
+
+        *port = ntohs(geneve->port);
+
+        return 0;
+}
+
+/**
+ * Set flow label to use for Geneve
+ * @arg link    Link object
+ * @arg label   Destination label
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_label(struct rtnl_link *link, uint32_t label)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        geneve->label = htonl(label);
+        geneve->mask |= GENEVE_ATTR_LABEL;
+
+        return 0;
+}
+
+/**
+ * Get flow label to use for Geneve
+ * @arg link    Link object
+ * @arg label   Pointer to store destination label
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_get_label(struct rtnl_link *link, uint32_t *label)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        if (!label)
+                return -NLE_INVAL;
+        if (!(geneve->mask & GENEVE_ATTR_LABEL))
+                return -NLE_NOATTR;
+
+        *label = ntohl(geneve->label);
+
+        return 0;
+}
+
+/**
+ * Set UDP checksum status to use for Geneve
+ * @arg link    Link object
+ * @arg csum    Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_udp_csum(struct rtnl_link *link, uint8_t csum)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        geneve->udp_csum = csum;
+        geneve->mask |= GENEVE_ATTR_UDP_CSUM;
+
+        return 0;
+}
+
+/**
+ * Get UDP checksum status to use for Geneve
+ * @arg link    Link object
+ *
+ * @return status value on success or a negative error code
+ */
+int rtnl_link_geneve_get_udp_csum(struct rtnl_link *link)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        if (!(geneve->mask & GENEVE_ATTR_UDP_CSUM))
+                return -NLE_NOATTR;
+
+        return geneve->udp_csum;
+}
+
+/**
+ * Set skip UDP checksum transmitted over IPv6 status to use for Geneve
+ * @arg link    Link object
+ * @arg csum    Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_udp_zero_csum6_tx(struct rtnl_link *link, uint8_t csum)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        geneve->udp_zero_csum6_tx = csum;
+        geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_TX;
+
+        return 0;
+}
+
+/**
+ * Get skip UDP checksum transmitted over IPv6 status to use for Geneve
+ * @arg link    Link object
+ *
+ * @return Status value on success or a negative error code
+ */
+int rtnl_link_geneve_get_udp_zero_csum6_tx(struct rtnl_link *link)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        if (!(geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_TX))
+                return -NLE_NOATTR;
+
+        return geneve->udp_zero_csum6_tx;
+}
+
+/**
+ * Set skip UDP checksum received over IPv6 status to use for Geneve
+ * @arg link    Link object
+ * @arg csum    Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_udp_zero_csum6_rx(struct rtnl_link *link, uint8_t csum)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        geneve->udp_zero_csum6_rx = csum;
+        geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_RX;
+
+        return 0;
+}
+
+/**
+ * Get skip UDP checksum received over IPv6  status to use for Geneve
+ * @arg link    Link object
+ *
+ * @return Status value on success or a negative error code
+ */
+int rtnl_link_geneve_get_udp_zero_csum6_rx(struct rtnl_link *link)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        if (!(geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_RX))
+                return -NLE_NOATTR;
+
+        return geneve->udp_zero_csum6_rx;
+}
+
+/**
+ * Set Geneve flags
+ * @arg link    Link object
+ * @arg flags   Which flags to set
+ * @arg enable  Boolean enabling or disabling flag
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_set_flags(struct rtnl_link *link, uint8_t flags, int enable)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        if (flags & ~RTNL_LINK_GENEVE_F_COLLECT_METADATA)
+                return -NLE_INVAL;
+
+        if (enable)
+                geneve->flags = flags;
+        else
+                geneve->flags &= ~flags;
+
+        return 0;
+}
+
+/**
+ * Get Geneve flags
+ * @arg link    Link object
+ * @arg flags   Pointer to store flags
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_geneve_get_flags(struct rtnl_link *link, uint8_t *flags)
+{
+        struct geneve_info *geneve = link->l_info;
+
+        IS_GENEVE_LINK_ASSERT(link);
+
+        *flags = geneve->flags;
+        return 0;
+}
+
+/** @} */
+static void __init geneve_init(void)
+{
+        rtnl_link_register_info(&geneve_info_ops);
+}
+
+static void __exit geneve_exit(void)
+{
+        rtnl_link_unregister_info(&geneve_info_ops);
+}
+
+/** @} */
diff --git a/lib/route/link/ifb.c b/lib/route/link/ifb.c
new file mode 100644
index 0000000..524f5c6
--- /dev/null
+++ b/lib/route/link/ifb.c
@@ -0,0 +1,40 @@
+/*
+ * lib/route/link/ifb.c	IFB Interfaces
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2014 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup ifb Intermediate Functional Block
+ *
+ * @details
+ * \b Link Type Name: "ifb"
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink-private/route/link/api.h>
+
+static struct rtnl_link_info_ops ifb_info_ops = {
+	.io_name		= "ifb",
+};
+
+static void __init ifb_init(void)
+{
+	rtnl_link_register_info(&ifb_info_ops);
+}
+
+static void __exit ifb_exit(void)
+{
+	rtnl_link_unregister_info(&ifb_info_ops);
+}
+
+/** @} */
diff --git a/lib/route/link/inet.c b/lib/route/link/inet.c
index e94342f..6651bc3 100644
--- a/lib/route/link/inet.c
+++ b/lib/route/link/inet.c
@@ -61,6 +61,7 @@
 #include <netlink/netlink.h>
 #include <netlink/attr.h>
 #include <netlink/route/rtnl.h>
+#include <netlink/route/link/inet.h>
 #include <netlink-private/route/link/api.h>
 
 /** @cond SKIP */
@@ -91,7 +92,7 @@
 	free(data);
 }
 
-static struct nla_policy inet_policy[IFLA_INET6_MAX+1] = {
+static struct nla_policy inet_policy[IFLA_INET_MAX+1] = {
 	[IFLA_INET_CONF]	= { .minlen = 4 },
 };
 
@@ -141,34 +142,34 @@
 }
 
 static const struct trans_tbl inet_devconf[] = {
-	__ADD(IPV4_DEVCONF_FORWARDING, forwarding)
-	__ADD(IPV4_DEVCONF_MC_FORWARDING, mc_forwarding)
-	__ADD(IPV4_DEVCONF_PROXY_ARP, proxy_arp)
-	__ADD(IPV4_DEVCONF_ACCEPT_REDIRECTS, accept_redirects)
-	__ADD(IPV4_DEVCONF_SECURE_REDIRECTS, secure_redirects)
-	__ADD(IPV4_DEVCONF_SEND_REDIRECTS, send_redirects)
-	__ADD(IPV4_DEVCONF_SHARED_MEDIA, shared_media)
-	__ADD(IPV4_DEVCONF_RP_FILTER, rp_filter)
-	__ADD(IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE, accept_source_route)
-	__ADD(IPV4_DEVCONF_BOOTP_RELAY, bootp_relay)
-	__ADD(IPV4_DEVCONF_LOG_MARTIANS, log_martians)
-	__ADD(IPV4_DEVCONF_TAG, tag)
-	__ADD(IPV4_DEVCONF_ARPFILTER, arpfilter)
-	__ADD(IPV4_DEVCONF_MEDIUM_ID, medium_id)
-	__ADD(IPV4_DEVCONF_NOXFRM, noxfrm)
-	__ADD(IPV4_DEVCONF_NOPOLICY, nopolicy)
-	__ADD(IPV4_DEVCONF_FORCE_IGMP_VERSION, force_igmp_version)
-	__ADD(IPV4_DEVCONF_ARP_ANNOUNCE, arp_announce)
-	__ADD(IPV4_DEVCONF_ARP_IGNORE, arp_ignore)
-	__ADD(IPV4_DEVCONF_PROMOTE_SECONDARIES, promote_secondaries)
-	__ADD(IPV4_DEVCONF_ARP_ACCEPT, arp_accept)
-	__ADD(IPV4_DEVCONF_ARP_NOTIFY, arp_notify)
-	__ADD(IPV4_DEVCONF_ACCEPT_LOCAL, accept_local)
-	__ADD(IPV4_DEVCONF_SRC_VMARK, src_vmark)
-	__ADD(IPV4_DEVCONF_PROXY_ARP_PVLAN, proxy_arp_pvlan)
-	__ADD(IPV4_DEVCONF_ROUTE_LOCALNET, route_localnet)
-	__ADD(IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL, igmpv2_unsolicited_report_interval)
-	__ADD(IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL, igmpv3_unsolicited_report_interval)
+	__ADD(IPV4_DEVCONF_FORWARDING, forwarding),
+	__ADD(IPV4_DEVCONF_MC_FORWARDING, mc_forwarding),
+	__ADD(IPV4_DEVCONF_PROXY_ARP, proxy_arp),
+	__ADD(IPV4_DEVCONF_ACCEPT_REDIRECTS, accept_redirects),
+	__ADD(IPV4_DEVCONF_SECURE_REDIRECTS, secure_redirects),
+	__ADD(IPV4_DEVCONF_SEND_REDIRECTS, send_redirects),
+	__ADD(IPV4_DEVCONF_SHARED_MEDIA, shared_media),
+	__ADD(IPV4_DEVCONF_RP_FILTER, rp_filter),
+	__ADD(IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE, accept_source_route),
+	__ADD(IPV4_DEVCONF_BOOTP_RELAY, bootp_relay),
+	__ADD(IPV4_DEVCONF_LOG_MARTIANS, log_martians),
+	__ADD(IPV4_DEVCONF_TAG, tag),
+	__ADD(IPV4_DEVCONF_ARPFILTER, arpfilter),
+	__ADD(IPV4_DEVCONF_MEDIUM_ID, medium_id),
+	__ADD(IPV4_DEVCONF_NOXFRM, noxfrm),
+	__ADD(IPV4_DEVCONF_NOPOLICY, nopolicy),
+	__ADD(IPV4_DEVCONF_FORCE_IGMP_VERSION, force_igmp_version),
+	__ADD(IPV4_DEVCONF_ARP_ANNOUNCE, arp_announce),
+	__ADD(IPV4_DEVCONF_ARP_IGNORE, arp_ignore),
+	__ADD(IPV4_DEVCONF_PROMOTE_SECONDARIES, promote_secondaries),
+	__ADD(IPV4_DEVCONF_ARP_ACCEPT, arp_accept),
+	__ADD(IPV4_DEVCONF_ARP_NOTIFY, arp_notify),
+	__ADD(IPV4_DEVCONF_ACCEPT_LOCAL, accept_local),
+	__ADD(IPV4_DEVCONF_SRC_VMARK, src_vmark),
+	__ADD(IPV4_DEVCONF_PROXY_ARP_PVLAN, proxy_arp_pvlan),
+	__ADD(IPV4_DEVCONF_ROUTE_LOCALNET, route_localnet),
+	__ADD(IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL, igmpv2_unsolicited_report_interval),
+	__ADD(IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL, igmpv3_unsolicited_report_interval),
 };
 
 const char *rtnl_link_inet_devconf2str(int type, char *buf, size_t len)
@@ -242,7 +243,7 @@
 	if (cfgid == 0 || cfgid > IPV4_DEVCONF_MAX)
 		return -NLE_RANGE;
 
-	if (!(id = rtnl_link_af_alloc(link, &inet_ops)))
+	if (!(id = rtnl_link_af_data(link, &inet_ops)))
 		return -NLE_NOATTR;
 
 	if (!id->i_confset[cfgid - 1])
diff --git a/lib/route/link/inet6.c b/lib/route/link/inet6.c
index 6fa2741..f02792c 100644
--- a/lib/route/link/inet6.c
+++ b/lib/route/link/inet6.c
@@ -13,18 +13,31 @@
 #include <netlink/netlink.h>
 #include <netlink/attr.h>
 #include <netlink/route/rtnl.h>
+#include <netlink/route/link/inet6.h>
 #include <netlink-private/route/link/api.h>
 
+#include "netlink-private/utils.h"
+
+#define I6_ADDR_GEN_MODE_UNKNOWN	UINT8_MAX
+
 struct inet6_data
 {
 	uint32_t		i6_flags;
 	struct ifla_cacheinfo	i6_cacheinfo;
 	uint32_t		i6_conf[DEVCONF_MAX];
+	struct in6_addr		i6_token;
+	uint8_t			i6_addr_gen_mode;
 };
 
 static void *inet6_alloc(struct rtnl_link *link)
 {
-	return calloc(1, sizeof(struct inet6_data));
+	struct inet6_data *i6;
+
+	i6 = calloc(1, sizeof(struct inet6_data));
+	if (i6)
+		i6->i6_addr_gen_mode = I6_ADDR_GEN_MODE_UNKNOWN;
+
+	return i6;
 }
 
 static void *inet6_clone(struct rtnl_link *link, void *data)
@@ -43,11 +56,13 @@
 }
 
 static struct nla_policy inet6_policy[IFLA_INET6_MAX+1] = {
-	[IFLA_INET6_FLAGS]	= { .type = NLA_U32 },
-	[IFLA_INET6_CACHEINFO]	= { .minlen = sizeof(struct ifla_cacheinfo) },
-	[IFLA_INET6_CONF]	= { .minlen = 4 },
-	[IFLA_INET6_STATS]	= { .minlen = 8 },
-	[IFLA_INET6_ICMP6STATS]	= { .minlen = 8 },
+	[IFLA_INET6_FLAGS]		= { .type = NLA_U32 },
+	[IFLA_INET6_CACHEINFO]		= { .minlen = sizeof(struct ifla_cacheinfo) },
+	[IFLA_INET6_CONF]		= { .minlen = 4 },
+	[IFLA_INET6_STATS]		= { .minlen = 8 },
+	[IFLA_INET6_ICMP6STATS]		= { .minlen = 8 },
+	[IFLA_INET6_TOKEN]		= { .minlen = sizeof(struct in6_addr) },
+	[IFLA_INET6_ADDR_GEN_MODE]	= { .type = NLA_U8 },
 };
 
 static const uint8_t map_stat_id_from_IPSTATS_MIB_v1[__IPSTATS_MIB_MAX] = {
@@ -155,7 +170,14 @@
 	if (tb[IFLA_INET6_CONF])
 		nla_memcpy(&i6->i6_conf, tb[IFLA_INET6_CONF],
 			   sizeof(i6->i6_conf));
- 
+
+	if (tb[IFLA_INET6_TOKEN])
+		nla_memcpy(&i6->i6_token, tb[IFLA_INET6_TOKEN],
+		           sizeof(struct in6_addr));
+
+	if (tb[IFLA_INET6_ADDR_GEN_MODE])
+		i6->i6_addr_gen_mode = nla_get_u8 (tb[IFLA_INET6_ADDR_GEN_MODE]);
+
 	/*
 	 * Due to 32bit data alignment, these addresses must be copied to an
 	 * aligned location prior to access.
@@ -200,6 +222,19 @@
 	return 0;
 }
 
+static int inet6_fill_af(struct rtnl_link *link, struct nl_msg *msg, void *data)
+{
+	struct inet6_data *id = data;
+
+	if (id->i6_addr_gen_mode != I6_ADDR_GEN_MODE_UNKNOWN)
+		NLA_PUT_U8(msg, IFLA_INET6_ADDR_GEN_MODE, id->i6_addr_gen_mode);
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_MSGSIZE;
+}
+
 /* These live in include/net/if_inet6.h and should be moved to include/linux */
 #define IF_RA_OTHERCONF	0x80
 #define IF_RA_MANAGED	0x40
@@ -208,49 +243,54 @@
 #define IF_READY	0x80000000
 
 static const struct trans_tbl inet6_flags[] = {
-	__ADD(IF_RA_OTHERCONF, ra_otherconf)
-	__ADD(IF_RA_MANAGED, ra_managed)
-	__ADD(IF_RA_RCVD, ra_rcvd)
-	__ADD(IF_RS_SENT, rs_sent)
-	__ADD(IF_READY, ready)
+	__ADD(IF_RA_OTHERCONF, ra_otherconf),
+	__ADD(IF_RA_MANAGED, ra_managed),
+	__ADD(IF_RA_RCVD, ra_rcvd),
+	__ADD(IF_RS_SENT, rs_sent),
+	__ADD(IF_READY, ready),
 };
 
-static char *inet6_flags2str(int flags, char *buf, size_t len)
+char *rtnl_link_inet6_flags2str(int flags, char *buf, size_t len)
 {
 	return __flags2str(flags, buf, len, inet6_flags,
 			   ARRAY_SIZE(inet6_flags));
 }
 
+int rtnl_link_inet6_str2flags(const char *name)
+{
+	return __str2flags(name, inet6_flags, ARRAY_SIZE(inet6_flags));
+}
+
 static const struct trans_tbl inet6_devconf[] = {
-	__ADD(DEVCONF_FORWARDING, forwarding)
-	__ADD(DEVCONF_HOPLIMIT, hoplimit)
-	__ADD(DEVCONF_MTU6, mtu6)
-	__ADD(DEVCONF_ACCEPT_RA, accept_ra)
-	__ADD(DEVCONF_ACCEPT_REDIRECTS, accept_redirects)
-	__ADD(DEVCONF_AUTOCONF, autoconf)
-	__ADD(DEVCONF_DAD_TRANSMITS, dad_transmits)
-	__ADD(DEVCONF_RTR_SOLICITS, rtr_solicits)
-	__ADD(DEVCONF_RTR_SOLICIT_INTERVAL, rtr_solicit_interval)
-	__ADD(DEVCONF_RTR_SOLICIT_DELAY, rtr_solicit_delay)
-	__ADD(DEVCONF_USE_TEMPADDR, use_tempaddr)
-	__ADD(DEVCONF_TEMP_VALID_LFT, temp_valid_lft)
-	__ADD(DEVCONF_TEMP_PREFERED_LFT, temp_prefered_lft)
-	__ADD(DEVCONF_REGEN_MAX_RETRY, regen_max_retry)
-	__ADD(DEVCONF_MAX_DESYNC_FACTOR, max_desync_factor)
-	__ADD(DEVCONF_MAX_ADDRESSES, max_addresses)
-	__ADD(DEVCONF_FORCE_MLD_VERSION, force_mld_version)
-	__ADD(DEVCONF_ACCEPT_RA_DEFRTR, accept_ra_defrtr)
-	__ADD(DEVCONF_ACCEPT_RA_PINFO, accept_ra_pinfo)
-	__ADD(DEVCONF_ACCEPT_RA_RTR_PREF, accept_ra_rtr_pref)
-	__ADD(DEVCONF_RTR_PROBE_INTERVAL, rtr_probe_interval)
-	__ADD(DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN, accept_ra_rt_info)
-	__ADD(DEVCONF_PROXY_NDP, proxy_ndp)
-	__ADD(DEVCONF_OPTIMISTIC_DAD, optimistic_dad)
-	__ADD(DEVCONF_ACCEPT_SOURCE_ROUTE, accept_source_route)
-	__ADD(DEVCONF_MC_FORWARDING, mc_forwarding)
-	__ADD(DEVCONF_DISABLE_IPV6, disable_ipv6)
-	__ADD(DEVCONF_ACCEPT_DAD, accept_dad)
-	__ADD(DEVCONF_FORCE_TLLAO, force_tllao)
+	__ADD(DEVCONF_FORWARDING, forwarding),
+	__ADD(DEVCONF_HOPLIMIT, hoplimit),
+	__ADD(DEVCONF_MTU6, mtu6),
+	__ADD(DEVCONF_ACCEPT_RA, accept_ra),
+	__ADD(DEVCONF_ACCEPT_REDIRECTS, accept_redirects),
+	__ADD(DEVCONF_AUTOCONF, autoconf),
+	__ADD(DEVCONF_DAD_TRANSMITS, dad_transmits),
+	__ADD(DEVCONF_RTR_SOLICITS, rtr_solicits),
+	__ADD(DEVCONF_RTR_SOLICIT_INTERVAL, rtr_solicit_interval),
+	__ADD(DEVCONF_RTR_SOLICIT_DELAY, rtr_solicit_delay),
+	__ADD(DEVCONF_USE_TEMPADDR, use_tempaddr),
+	__ADD(DEVCONF_TEMP_VALID_LFT, temp_valid_lft),
+	__ADD(DEVCONF_TEMP_PREFERED_LFT, temp_prefered_lft),
+	__ADD(DEVCONF_REGEN_MAX_RETRY, regen_max_retry),
+	__ADD(DEVCONF_MAX_DESYNC_FACTOR, max_desync_factor),
+	__ADD(DEVCONF_MAX_ADDRESSES, max_addresses),
+	__ADD(DEVCONF_FORCE_MLD_VERSION, force_mld_version),
+	__ADD(DEVCONF_ACCEPT_RA_DEFRTR, accept_ra_defrtr),
+	__ADD(DEVCONF_ACCEPT_RA_PINFO, accept_ra_pinfo),
+	__ADD(DEVCONF_ACCEPT_RA_RTR_PREF, accept_ra_rtr_pref),
+	__ADD(DEVCONF_RTR_PROBE_INTERVAL, rtr_probe_interval),
+	__ADD(DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN, accept_ra_rt_info),
+	__ADD(DEVCONF_PROXY_NDP, proxy_ndp),
+	__ADD(DEVCONF_OPTIMISTIC_DAD, optimistic_dad),
+	__ADD(DEVCONF_ACCEPT_SOURCE_ROUTE, accept_source_route),
+	__ADD(DEVCONF_MC_FORWARDING, mc_forwarding),
+	__ADD(DEVCONF_DISABLE_IPV6, disable_ipv6),
+	__ADD(DEVCONF_ACCEPT_DAD, accept_dad),
+	__ADD(DEVCONF_FORCE_TLLAO, force_tllao),
 };
 
 static char *inet6_devconf2str(int type, char *buf, size_t len)
@@ -259,32 +299,59 @@
 			  ARRAY_SIZE(inet6_devconf));
 }
 
+static const struct trans_tbl inet6_addr_gen_mode[] = {
+	__ADD(IN6_ADDR_GEN_MODE_EUI64, eui64),
+	__ADD(IN6_ADDR_GEN_MODE_NONE, none),
+	__ADD(IN6_ADDR_GEN_MODE_STABLE_PRIVACY, stable_privacy),
+};
+
+const char *rtnl_link_inet6_addrgenmode2str(uint8_t mode, char *buf, size_t len)
+{
+	return __type2str(mode, buf, len, inet6_addr_gen_mode,
+			  ARRAY_SIZE(inet6_addr_gen_mode));
+}
+
+uint8_t rtnl_link_inet6_str2addrgenmode(const char *mode)
+{
+	return (uint8_t) __str2type(mode, inet6_addr_gen_mode,
+			            ARRAY_SIZE(inet6_addr_gen_mode));
+}
 
 static void inet6_dump_details(struct rtnl_link *link,
 				struct nl_dump_params *p, void *data)
 {
 	struct inet6_data *i6 = data;
-	char buf[64], buf2[64];
+	struct nl_addr *addr;
 	int i, n = 0;
+	char buf[64];
 
 	nl_dump_line(p, "    ipv6 max-reasm-len %s",
-		nl_size2str(i6->i6_cacheinfo.max_reasm_len, buf, sizeof(buf)));
+	             nl_size2str(i6->i6_cacheinfo.max_reasm_len, buf, sizeof(buf)));
 
 	nl_dump(p, " <%s>\n",
-		inet6_flags2str(i6->i6_flags, buf, sizeof(buf)));
-
+	        rtnl_link_inet6_flags2str(i6->i6_flags, buf, sizeof(buf)));
 
 	nl_dump_line(p, "      create-stamp %.2fs reachable-time %s",
-		(double) i6->i6_cacheinfo.tstamp / 100.,
-		nl_msec2str(i6->i6_cacheinfo.reachable_time, buf, sizeof(buf)));
+	             (double) i6->i6_cacheinfo.tstamp / 100.,
+	             nl_msec2str(i6->i6_cacheinfo.reachable_time, buf, sizeof(buf)));
 
 	nl_dump(p, " retrans-time %s\n",
-		nl_msec2str(i6->i6_cacheinfo.retrans_time, buf, sizeof(buf)));
+	        nl_msec2str(i6->i6_cacheinfo.retrans_time, buf, sizeof(buf)));
+
+	addr = nl_addr_build(AF_INET6, &i6->i6_token, sizeof(i6->i6_token));
+	nl_dump(p, "      token %s\n",
+	        nl_addr2str(addr, buf, sizeof(buf)));
+	nl_addr_put(addr);
+
+	nl_dump(p, "      link-local address mode %s\n",
+	        rtnl_link_inet6_addrgenmode2str(i6->i6_addr_gen_mode,
+	                                        buf, sizeof(buf)));
 
 	nl_dump_line(p, "      devconf:\n");
 	nl_dump_line(p, "      ");
 
 	for (i = 0; i < DEVCONF_MAX; i++) {
+		char buf2[64];
 		uint32_t value = i6->i6_conf[i];
 		int x, offset;
 
@@ -303,7 +370,6 @@
 		default:
 			snprintf(buf2, sizeof(buf2), "%u", value);
 			break;
-			
 		}
 
 		inet6_devconf2str(i, buf, sizeof(buf));
@@ -315,7 +381,7 @@
 		for (x = strlen(buf); x < offset; x++)
 			buf[x] = ' ';
 
-		strncpy(&buf[offset], buf2, strlen(buf2));
+		_nl_strncpy_trunc(&buf[offset], buf2, sizeof(buf) - offset);
 
 		nl_dump_line(p, "%s", buf);
 
@@ -468,11 +534,160 @@
 	.ao_free			= &inet6_free,
 	.ao_parse_protinfo		= &inet6_parse_protinfo,
 	.ao_parse_af			= &inet6_parse_protinfo,
+	.ao_fill_af			= &inet6_fill_af,
 	.ao_dump[NL_DUMP_DETAILS]	= &inet6_dump_details,
 	.ao_dump[NL_DUMP_STATS]		= &inet6_dump_stats,
 	.ao_protinfo_policy		= &protinfo_policy,
 };
 
+/**
+ * Return IPv6 specific flags
+ * @arg link		Link object
+ * @arg out_flags	Flags on success
+ *
+ * Returns the link's IPv6 flags.
+ *
+ * @return 0 on success
+ * @return -NLE_NOATTR configuration setting not available
+ */
+int rtnl_link_inet6_get_flags(struct rtnl_link *link, uint32_t* out_flags)
+{
+	struct inet6_data *id = NULL;
+
+	if (!(id = rtnl_link_af_data(link, &inet6_ops)))
+		return -NLE_NOATTR;
+
+	*out_flags = id->i6_flags;
+	return 0;
+}
+
+/**
+ * Set IPv6 specific flags
+ * @arg link		Link object
+ * @arg flags		Flags to set
+ *
+ * Sets the link's IPv6 specific flags. Overwrites currently set flags.
+ *
+ * @return 0 on success
+ * @return -NLE_NOMEM could not allocate inet6 data
+ */
+int rtnl_link_inet6_set_flags(struct rtnl_link *link, uint32_t flags)
+{
+	struct inet6_data *id;
+
+	if (!(id = rtnl_link_af_alloc(link, &inet6_ops)))
+		return -NLE_NOMEM;
+
+	id->i6_flags = flags;
+	return 0;
+}
+
+/**
+ * Get IPv6 tokenized interface identifier
+ * @arg link		Link object
+ * @arg token		Tokenized interface identifier on success
+ *
+ * Returns the link's IPv6 tokenized interface identifier.
+ *
+ * @return 0 on success
+ * @return -NLE_NOMEM  failure to allocate struct nl_addr result
+ * @return -NLE_NOATTR configuration setting not available
+ * @return -NLE_NOADDR tokenized interface identifier is not set
+ */
+int rtnl_link_inet6_get_token(struct rtnl_link *link, struct nl_addr **addr)
+{
+	struct inet6_data *id;
+
+	if (!(id = rtnl_link_af_data(link, &inet6_ops)))
+		return -NLE_NOATTR;
+
+	*addr = nl_addr_build(AF_INET6, &id->i6_token, sizeof(id->i6_token));
+	if (!*addr)
+		return -NLE_NOMEM;
+	if (nl_addr_iszero(*addr)) {
+		nl_addr_put(*addr);
+		*addr = NULL;
+		return -NLE_NOADDR;
+	}
+
+	return 0;
+}
+
+/**
+ * Set IPv6 tokenized interface identifier
+ * @arg link		Link object
+ * @arg token		Tokenized interface identifier
+ *
+ * Sets the link's IPv6 tokenized interface identifier.
+ *
+ * @return 0 on success
+ * @return -NLE_NOMEM could not allocate inet6 data
+ * @return -NLE_INVAL addr is not a valid inet6 address
+ */
+int rtnl_link_inet6_set_token(struct rtnl_link *link, struct nl_addr *addr)
+{
+	struct inet6_data *id;
+
+	if ((nl_addr_get_family(addr) != AF_INET6) ||
+	    (nl_addr_get_len(addr) != sizeof(id->i6_token)))
+		return -NLE_INVAL;
+
+	if (!(id = rtnl_link_af_alloc(link, &inet6_ops)))
+		return -NLE_NOMEM;
+
+	memcpy(&id->i6_token, nl_addr_get_binary_addr(addr),
+	       sizeof(id->i6_token));
+	return 0;
+}
+
+/**
+ * Get IPv6 link-local address generation mode
+ * @arg link		Link object
+ * @arg mode		Generation mode on success
+ *
+ * Returns the link's IPv6 link-local address generation mode.
+ *
+ * @return 0 on success
+ * @return -NLE_NOATTR configuration setting not available
+ * @return -NLE_INVAL generation mode unknown. If the link was received via
+ *                    netlink, it means that address generation mode is not
+ *                    supported by the kernel.
+ */
+int rtnl_link_inet6_get_addr_gen_mode(struct rtnl_link *link, uint8_t *mode)
+{
+	struct inet6_data *id;
+
+	if (!(id = rtnl_link_af_data(link, &inet6_ops)))
+		return -NLE_NOATTR;
+
+	if (id->i6_addr_gen_mode == I6_ADDR_GEN_MODE_UNKNOWN)
+		return -NLE_INVAL;
+
+	*mode = id->i6_addr_gen_mode;
+	return 0;
+}
+
+/**
+ * Set IPv6 link-local address generation mode
+ * @arg link		Link object
+ * @arg mode		Generation mode
+ *
+ * Sets the link's IPv6 link-local address generation mode.
+ *
+ * @return 0 on success
+ * @return -NLE_NOMEM could not allocate inet6 data
+ */
+int rtnl_link_inet6_set_addr_gen_mode(struct rtnl_link *link, uint8_t mode)
+{
+	struct inet6_data *id;
+
+	if (!(id = rtnl_link_af_alloc(link, &inet6_ops)))
+		return -NLE_NOMEM;
+
+	id->i6_addr_gen_mode = mode;
+	return 0;
+}
+
 static void __init inet6_init(void)
 {
 	rtnl_link_af_register(&inet6_ops);
diff --git a/lib/route/link/ip6tnl.c b/lib/route/link/ip6tnl.c
index 9fe1367..085bf66 100644
--- a/lib/route/link/ip6tnl.c
+++ b/lib/route/link/ip6tnl.c
@@ -28,6 +28,7 @@
 #include <netlink/utils.h>
 #include <netlink/object.h>
 #include <netlink/route/rtnl.h>
+#include <netlink/route/link/ip6tnl.h>
 #include <netlink-private/route/link/api.h>
 #include <linux/if_tunnel.h>
 #include <netinet/in.h>
@@ -72,11 +73,15 @@
 {
 	struct ip6_tnl_info *ip6_tnl;
 
-	ip6_tnl = calloc(1, sizeof(*ip6_tnl));
-	if (!ip6_tnl)
-		return -NLE_NOMEM;
+	if (link->l_info)
+		memset(link->l_info, 0, sizeof(*ip6_tnl));
+	else {
+		ip6_tnl = calloc(1, sizeof(*ip6_tnl));
+		if (!ip6_tnl)
+			return -NLE_NOMEM;
 
-	link->l_info = ip6_tnl;
+		link->l_info = ip6_tnl;
+	}
 
 	return 0;
 }
@@ -88,7 +93,7 @@
 	struct ip6_tnl_info *ip6_tnl;
 	int err;
 
-	NL_DBG(3, "Parsing IP6_TNL link info");
+	NL_DBG(3, "Parsing IP6_TNL link info\n");
 
 	err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, ip6_tnl_policy);
 	if (err < 0)
@@ -213,10 +218,16 @@
 {
 	struct ip6_tnl_info *ip6_tnl = link->l_info;
 	char *name, addr[INET6_ADDRSTRLEN];
+	struct rtnl_link *parent;
 
 	if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LINK) {
 		nl_dump(p, "      link ");
-		name = rtnl_link_get_name(link);
+
+		name = NULL;
+		parent = link_lookup(link->ce_cache, ip6_tnl->link);
+		if (parent)
+			name = rtnl_link_get_name(parent);
+
 		if (name)
 			nl_dump_line(p, "%s\n", name);
 		else
diff --git a/lib/route/link/ipgre.c b/lib/route/link/ipgre.c
index 74dbb9d..a7665fe 100644
--- a/lib/route/link/ipgre.c
+++ b/lib/route/link/ipgre.c
@@ -28,6 +28,7 @@
 #include <netlink/utils.h>
 #include <netlink/object.h>
 #include <netlink/route/rtnl.h>
+#include <netlink/route/link/ipgre.h>
 #include <netlink-private/route/link/api.h>
 #include <linux/if_tunnel.h>
 
@@ -74,11 +75,15 @@
 {
 	struct ipgre_info *ipgre;
 
-	ipgre = calloc(1, sizeof(*ipgre));
-	if (!ipgre)
-		return -NLE_NOMEM;
+	if (link->l_info)
+		memset(link->l_info, 0, sizeof(*ipgre));
+	else {
+		ipgre = calloc(1, sizeof(*ipgre));
+		if (!ipgre)
+			return -NLE_NOMEM;
 
-	link->l_info = ipgre;
+		link->l_info = ipgre;
+	}
 
 	return 0;
 }
@@ -86,11 +91,11 @@
 static int ipgre_parse(struct rtnl_link *link, struct nlattr *data,
 		       struct nlattr *xstats)
 {
-	struct nlattr *tb[IFLA_IPTUN_MAX + 1];
+	struct nlattr *tb[IFLA_GRE_MAX + 1];
 	struct ipgre_info *ipgre;
 	int err;
 
-	NL_DBG(3, "Parsing IPGRE link info");
+	NL_DBG(3, "Parsing IPGRE link info\n");
 
 	err = nla_parse_nested(tb, IFLA_GRE_MAX, data, ipgre_policy);
 	if (err < 0)
@@ -132,8 +137,8 @@
 		ipgre->ipgre_mask |= IPGRE_ATTR_LOCAL;
 	}
 
-	if (tb[IFLA_GRE_LOCAL]) {
-		ipgre->remote = nla_get_u32(tb[IFLA_GRE_LOCAL]);
+	if (tb[IFLA_GRE_REMOTE]) {
+		ipgre->remote = nla_get_u32(tb[IFLA_GRE_REMOTE]);
 		ipgre->ipgre_mask |= IPGRE_ATTR_REMOTE;
 	}
 
@@ -154,7 +159,7 @@
 
 	err = 0;
 
- errout:
+errout:
 	return err;
 }
 
@@ -199,7 +204,7 @@
 
 	nla_nest_end(msg, data);
 
- nla_put_failure:
+nla_put_failure:
 
 	return 0;
 }
@@ -221,10 +226,16 @@
 {
 	struct ipgre_info *ipgre = link->l_info;
 	char *name, addr[INET_ADDRSTRLEN];
+	struct rtnl_link *parent;
 
 	if (ipgre->ipgre_mask & IPGRE_ATTR_LINK) {
 		nl_dump(p, "      link ");
-		name = rtnl_link_get_name(link);
+
+		name = NULL;
+		parent = link_lookup(link->ce_cache, ipgre->link);
+		if (parent)
+			name = rtnl_link_get_name(parent);
+
 		if (name)
 			nl_dump_line(p, "%s\n", name);
 		else
@@ -304,6 +315,27 @@
 	return 0;
 }
 
+static int ipgretap_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+	struct ipgre_info *ipgre_dst, *ipgre_src = src->l_info;
+	int err;
+
+	dst->l_info = NULL;
+
+	err = rtnl_link_set_type(dst, "gretap");
+	if (err < 0)
+		return err;
+
+	ipgre_dst = dst->l_info;
+
+	if (!ipgre_dst || !ipgre_src)
+		BUG();
+
+	memcpy(ipgre_dst, ipgre_src, sizeof(struct ipgre_info));
+
+	return 0;
+}
+
 static struct rtnl_link_info_ops ipgre_info_ops = {
 	.io_name                = "gre",
 	.io_alloc               = ipgre_alloc,
@@ -317,10 +349,24 @@
 	.io_free                = ipgre_free,
 };
 
-#define IS_IPGRE_LINK_ASSERT(link)                                          \
-        if ((link)->l_info_ops != &ipgre_info_ops) {                        \
-                APPBUG("Link is not a ipgre link. set type \"gre\" first.");\
-                return -NLE_OPNOTSUPP;                                      \
+static struct rtnl_link_info_ops ipgretap_info_ops = {
+	.io_name                = "gretap",
+	.io_alloc               = ipgre_alloc,
+	.io_parse               = ipgre_parse,
+	.io_dump = {
+		[NL_DUMP_LINE]  = ipgre_dump_line,
+		[NL_DUMP_DETAILS] = ipgre_dump_details,
+	},
+	.io_clone               = ipgretap_clone,
+	.io_put_attrs           = ipgre_put_attrs,
+	.io_free                = ipgre_free,
+};
+
+#define IS_IPGRE_LINK_ASSERT(link)                                                 \
+        if ((link)->l_info_ops != &ipgre_info_ops &&                               \
+            (link)->l_info_ops != &ipgretap_info_ops) {                            \
+                APPBUG("Link is not a ipgre link. set type \"gre/gretap\" first.");\
+                return -NLE_OPNOTSUPP;                                             \
         }
 
 struct rtnl_link *rtnl_link_ipgre_alloc(void)
@@ -351,8 +397,9 @@
 {
 	return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "gre");
 }
+
 /**
- * Create a new ipip tunnel device
+ * Create a new IPGRE tunnel device
  * @arg sock            netlink socket
  * @arg name            name of the tunnel deviceL
  *
@@ -376,6 +423,61 @@
 
 	return err;
 }
+
+struct rtnl_link *rtnl_link_ipgretap_alloc(void)
+{
+	struct rtnl_link *link;
+	int err;
+
+	link = rtnl_link_alloc();
+	if (!link)
+		return NULL;
+
+	err = rtnl_link_set_type(link, "gretap");
+	if (err < 0) {
+		rtnl_link_put(link);
+		return NULL;
+	}
+
+	return link;
+}
+
+/**
+ * Check if link is a IPGRETAP link
+ * @arg link            Link object
+ *
+ * @return True if link is a IPGRETAP link, otherwise 0 is returned.
+ */
+int rtnl_link_is_ipgretap(struct rtnl_link *link)
+{
+	return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "gretap");
+}
+/**
+ * Create a new IPGRETAP tunnel device
+ * @arg sock            netlink socket
+ * @arg name            name of the tunnel deviceL
+ *
+ * Creates a new IPGRETAP tunnel device in the kernel
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipgretap_add(struct nl_sock *sk, const char *name)
+{
+	struct rtnl_link *link;
+	int err;
+
+	link = rtnl_link_ipgretap_alloc();
+	if (!link)
+		return -NLE_NOMEM;
+
+	if(name)
+		rtnl_link_set_name(link, name);
+
+	err = rtnl_link_add(sk, link, NLM_F_CREATE);
+	rtnl_link_put(link);
+
+	return err;
+}
+
 /**
  * Set IPGRE tunnel interface index
  * @arg link            Link object
@@ -707,7 +809,7 @@
  *
  * @return pmtudisc value
  */
-uint8_t rtnl_link_get_pmtudisc(struct rtnl_link *link)
+uint8_t rtnl_link_ipgre_get_pmtudisc(struct rtnl_link *link)
 {
 	struct ipgre_info *ipgre = link->l_info;
 
@@ -716,12 +818,25 @@
 	return ipgre->pmtudisc;
 }
 
+/* Function prototype for ABI-preserving wrapper (not in public header) to avoid
+ * GCC warning about missing prototype. */
+uint8_t rtnl_link_get_pmtudisc(struct rtnl_link *link);
+
+uint8_t rtnl_link_get_pmtudisc(struct rtnl_link *link)
+{
+	/* rtnl_link_ipgre_get_pmtudisc() was wrongly named. Keep this
+	 * to preserve ABI. */
+	return rtnl_link_ipgre_get_pmtudisc (link);
+}
+
 static void __init ipgre_init(void)
 {
 	rtnl_link_register_info(&ipgre_info_ops);
+	rtnl_link_register_info(&ipgretap_info_ops);
 }
 
 static void __exit ipgre_exit(void)
 {
 	rtnl_link_unregister_info(&ipgre_info_ops);
+	rtnl_link_unregister_info(&ipgretap_info_ops);
 }
diff --git a/lib/route/link/ipip.c b/lib/route/link/ipip.c
index ecf86ad..3243b56 100644
--- a/lib/route/link/ipip.c
+++ b/lib/route/link/ipip.c
@@ -28,6 +28,7 @@
 #include <netlink/utils.h>
 #include <netlink/object.h>
 #include <netlink/route/rtnl.h>
+#include <netlink/route/link/ipip.h>
 #include <netlink-private/route/link/api.h>
 #include <linux/if_tunnel.h>
 
@@ -62,11 +63,15 @@
 {
 	struct ipip_info *ipip;
 
-	ipip = calloc(1, sizeof(*ipip));
-	if (!ipip)
-		return -NLE_NOMEM;
+	if (link->l_info)
+		memset(link->l_info, 0, sizeof(*ipip));
+	else {
+		ipip = calloc(1, sizeof(*ipip));
+		if (!ipip)
+			return -NLE_NOMEM;
 
-	link->l_info = ipip;
+		link->l_info = ipip;
+	}
 
 	return 0;
 }
@@ -78,7 +83,7 @@
 	struct ipip_info *ipip;
 	int err;
 
-	NL_DBG(3, "Parsing IPIP link info");
+	NL_DBG(3, "Parsing IPIP link info\n");
 
 	err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, ipip_policy);
 	if (err < 0)
@@ -176,10 +181,16 @@
 {
 	struct ipip_info *ipip = link->l_info;
 	char *name, addr[INET_ADDRSTRLEN];
+	struct rtnl_link *parent;
 
 	if (ipip->ipip_mask & IPIP_ATTR_LINK) {
 		nl_dump(p, "      link ");
-		name = rtnl_link_get_name(link);
+
+		name = NULL;
+		parent = link_lookup(link->ce_cache, ipip->link);
+		if (parent)
+			name = rtnl_link_get_name(parent);
+
 		if (name)
 			nl_dump_line(p, "%s\n", name);
 		else
diff --git a/lib/route/link/ipvlan.c b/lib/route/link/ipvlan.c
new file mode 100644
index 0000000..84ace43
--- /dev/null
+++ b/lib/route/link/ipvlan.c
@@ -0,0 +1,277 @@
+/*
+ * lib/route/link/ipvlan.c	IPVLAN Link Info
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2015 Cong Wang <cwang@twopensource.com>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup ipvlan IPVLAN
+ * IP-based Virtual LAN link module
+ *
+ * @details
+ * \b Link Type Name: "ipvlan"
+ *
+ * @route_doc{link_ipvlan, IPVLAN Documentation}
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+#include <netlink/route/link/ipvlan.h>
+
+#include <linux/if_link.h>
+
+/** @cond SKIP */
+#define IPVLAN_HAS_MODE	(1<<0)
+
+struct ipvlan_info
+{
+	uint16_t		ipi_mode;
+	uint32_t		ipi_mask;
+};
+
+/** @endcond */
+
+static struct nla_policy ipvlan_policy[IFLA_IPVLAN_MAX+1] = {
+	[IFLA_IPVLAN_MODE]	= { .type = NLA_U16 },
+};
+
+static int ipvlan_alloc(struct rtnl_link *link)
+{
+	struct ipvlan_info *ipi;
+
+	if (link->l_info)
+		memset(link->l_info, 0, sizeof(*ipi));
+	else {
+		if ((ipi = calloc(1, sizeof(*ipi))) == NULL)
+			return -NLE_NOMEM;
+
+		link->l_info = ipi;
+	}
+
+	return 0;
+}
+
+static int ipvlan_parse(struct rtnl_link *link, struct nlattr *data,
+                         struct nlattr *xstats)
+{
+	struct nlattr *tb[IFLA_IPVLAN_MAX+1];
+	struct ipvlan_info *ipi;
+	int err;
+
+	NL_DBG(3, "Parsing IPVLAN link info\n");
+
+	if ((err = nla_parse_nested(tb, IFLA_IPVLAN_MAX, data, ipvlan_policy)) < 0)
+		goto errout;
+
+	if ((err = ipvlan_alloc(link)) < 0)
+		goto errout;
+
+	ipi = link->l_info;
+
+	if (tb[IFLA_IPVLAN_MODE]) {
+		ipi->ipi_mode = nla_get_u16(tb[IFLA_IPVLAN_MODE]);
+		ipi->ipi_mask |= IPVLAN_HAS_MODE;
+	}
+
+	err = 0;
+errout:
+	return err;
+}
+
+static void ipvlan_free(struct rtnl_link *link)
+{
+	free(link->l_info);
+	link->l_info = NULL;
+}
+
+static void ipvlan_dump(struct rtnl_link *link, struct nl_dump_params *p)
+{
+	char buf[64];
+	struct ipvlan_info *ipi = link->l_info;
+
+	if (ipi->ipi_mask & IPVLAN_HAS_MODE) {
+		rtnl_link_ipvlan_mode2str(ipi->ipi_mode, buf, sizeof(buf));
+		nl_dump(p, "ipvlan-mode %s", buf);
+	}
+}
+
+static int ipvlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+	struct ipvlan_info *vdst, *vsrc = src->l_info;
+	int err;
+
+	dst->l_info = NULL;
+	if ((err = rtnl_link_set_type(dst, "ipvlan")) < 0)
+		return err;
+	vdst = dst->l_info;
+
+	if (!vdst || !vsrc)
+		return -NLE_NOMEM;
+
+	memcpy(vdst, vsrc, sizeof(struct ipvlan_info));
+
+	return 0;
+}
+
+static int ipvlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+	struct ipvlan_info *ipi = link->l_info;
+	struct nlattr *data;
+
+	if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
+		return -NLE_MSGSIZE;
+
+	if (ipi->ipi_mask & IPVLAN_HAS_MODE)
+		NLA_PUT_U16(msg, IFLA_IPVLAN_MODE, ipi->ipi_mode);
+
+	nla_nest_end(msg, data);
+
+nla_put_failure:
+
+	return 0;
+}
+
+static struct rtnl_link_info_ops ipvlan_info_ops = {
+	.io_name		= "ipvlan",
+	.io_alloc		= ipvlan_alloc,
+	.io_parse		= ipvlan_parse,
+	.io_dump = {
+	    [NL_DUMP_LINE]	= ipvlan_dump,
+	    [NL_DUMP_DETAILS]	= ipvlan_dump,
+	},
+	.io_clone		= ipvlan_clone,
+	.io_put_attrs		= ipvlan_put_attrs,
+	.io_free		= ipvlan_free,
+};
+
+/** @cond SKIP */
+#define IS_IPVLAN_LINK_ASSERT(link) \
+	if ((link)->l_info_ops != &ipvlan_info_ops) { \
+		APPBUG("Link is not a ipvlan link. set type \"ipvlan\" first."); \
+		return -NLE_OPNOTSUPP; \
+	}
+/** @endcond */
+
+/**
+ * @name IPVLAN Object
+ * @{
+ */
+
+/**
+ * Allocate link object of type IPVLAN
+ *
+ * @return Allocated link object or NULL.
+ */
+struct rtnl_link *rtnl_link_ipvlan_alloc(void)
+{
+	struct rtnl_link *link;
+	int err;
+
+	if (!(link = rtnl_link_alloc()))
+		return NULL;
+
+	if ((err = rtnl_link_set_type(link, "ipvlan")) < 0) {
+		rtnl_link_put(link);
+		return NULL;
+	}
+
+	return link;
+}
+
+/**
+ * Check if link is a IPVLAN link
+ * @arg link		Link object
+ *
+ * @return True if link is a IPVLAN link, otherwise false is returned.
+ */
+int rtnl_link_is_ipvlan(struct rtnl_link *link)
+{
+	return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "ipvlan");
+}
+
+/**
+ * Set IPVLAN MODE
+ * @arg link		Link object
+ * @arg mode		IPVLAN mode
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipvlan_set_mode(struct rtnl_link *link, uint16_t mode)
+{
+	struct ipvlan_info *ipi = link->l_info;
+
+	IS_IPVLAN_LINK_ASSERT(link);
+
+	ipi->ipi_mode = mode;
+	ipi->ipi_mask |= IPVLAN_HAS_MODE;
+
+	return 0;
+}
+
+/**
+ * Get IPVLAN Mode
+ * @arg link		Link object
+ * @arg out_mode        on success, return the mode
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_ipvlan_get_mode(struct rtnl_link *link, uint16_t *out_mode)
+{
+	struct ipvlan_info *ipi = link->l_info;
+
+	IS_IPVLAN_LINK_ASSERT(link);
+
+	if (!(ipi->ipi_mask & IPVLAN_HAS_MODE))
+		return -NLE_INVAL;
+	*out_mode = ipi->ipi_mode;
+	return 0;
+}
+
+/** @} */
+
+static const struct trans_tbl ipvlan_modes[] = {
+	__ADD(IPVLAN_MODE_L2, l2),
+	__ADD(IPVLAN_MODE_L3, l3),
+};
+
+/**
+ * @name Mode Translation
+ * @{
+ */
+
+char *rtnl_link_ipvlan_mode2str(int mode, char *buf, size_t len)
+{
+	return __type2str(mode, buf, len, ipvlan_modes, ARRAY_SIZE(ipvlan_modes));
+}
+
+int rtnl_link_ipvlan_str2mode(const char *name)
+{
+	return __str2type(name, ipvlan_modes, ARRAY_SIZE(ipvlan_modes));
+}
+
+/** @} */
+
+static void __init ipvlan_init(void)
+{
+	rtnl_link_register_info(&ipvlan_info_ops);
+}
+
+static void __exit ipvlan_exit(void)
+{
+	rtnl_link_unregister_info(&ipvlan_info_ops);
+}
+
+/** @} */
diff --git a/lib/route/link/ipvti.c b/lib/route/link/ipvti.c
index 71f61c3..851d566 100644
--- a/lib/route/link/ipvti.c
+++ b/lib/route/link/ipvti.c
@@ -1,4 +1,4 @@
- /*
+/*
  * lib/route/link/ipvti.c	 IPVTI Link Info
  *
  *	This library is free software; you can redistribute it and/or
@@ -28,6 +28,7 @@
 #include <netlink/utils.h>
 #include <netlink/object.h>
 #include <netlink/route/rtnl.h>
+#include <netlink/route/link/ipvti.h>
 #include <netlink-private/route/link/api.h>
 #include <linux/if_tunnel.h>
 
@@ -47,7 +48,7 @@
 	uint32_t   ipvti_mask;
 };
 
-static	struct nla_policy ipvti_policy[IFLA_GRE_MAX + 1] = {
+static	struct nla_policy ipvti_policy[IFLA_VTI_MAX + 1] = {
 	[IFLA_VTI_LINK]     = { .type = NLA_U32 },
 	[IFLA_VTI_IKEY]     = { .type = NLA_U32 },
 	[IFLA_VTI_OKEY]     = { .type = NLA_U32 },
@@ -59,11 +60,15 @@
 {
 	struct ipvti_info *ipvti;
 
-	ipvti = calloc(1, sizeof(*ipvti));
-	if (!ipvti)
-		return -NLE_NOMEM;
+	if (link->l_info)
+		memset(link->l_info, 0, sizeof(*ipvti));
+	else {
+		ipvti = calloc(1, sizeof(*ipvti));
+		if (!ipvti)
+			return -NLE_NOMEM;
 
-	link->l_info = ipvti;
+		link->l_info = ipvti;
+	}
 
 	return 0;
 }
@@ -71,13 +76,13 @@
 static int ipvti_parse(struct rtnl_link *link, struct nlattr *data,
 		       struct nlattr *xstats)
 {
-	struct nlattr *tb[IFLA_IPTUN_MAX + 1];
+	struct nlattr *tb[IFLA_VTI_MAX + 1];
 	struct ipvti_info *ipvti;
 	int err;
 
-	NL_DBG(3, "Parsing IPVTI link info");
+	NL_DBG(3, "Parsing IPVTI link info\n");
 
-	err = nla_parse_nested(tb, IFLA_GRE_MAX, data, ipvti_policy);
+	err = nla_parse_nested(tb, IFLA_VTI_MAX, data, ipvti_policy);
 	if (err < 0)
 		goto errout;
 
@@ -114,7 +119,7 @@
 
 	err = 0;
 
- errout:
+errout:
 	return err;
 }
 
@@ -166,10 +171,16 @@
 {
 	struct ipvti_info *ipvti = link->l_info;
 	char *name, addr[INET_ADDRSTRLEN];
+	struct rtnl_link *parent;
 
 	if (ipvti->ipvti_mask & IPVTI_ATTR_LINK) {
 		nl_dump(p, "      link ");
-		name = rtnl_link_get_name(link);
+
+		name = NULL;
+		parent = link_lookup(link->ce_cache, ipvti->link);
+		if (parent)
+			name = rtnl_link_get_name(parent);
+
 		if (name)
 			nl_dump_line(p, "%s\n", name);
 		else
diff --git a/lib/route/link/macsec.c b/lib/route/link/macsec.c
new file mode 100644
index 0000000..fa115e2
--- /dev/null
+++ b/lib/route/link/macsec.c
@@ -0,0 +1,850 @@
+/*
+ * lib/route/link/macsec.c	MACsec Link Info
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2016 Sabrina Dubroca <sd@queasysnail.net>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup macsec MACsec
+ * MACsec link module
+ *
+ * @details
+ * \b Link Type Name: "macsec"
+ *
+ * @route_doc{link_macsec, MACsec Documentation}
+ *
+ * @{
+ */
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink/route/link/macsec.h>
+#include <netlink-private/route/link/api.h>
+#include <netlink-private/utils.h>
+
+#include <linux/if_macsec.h>
+
+/** @cond SKIP */
+#define MACSEC_ATTR_SCI			(1 << 0)
+#define MACSEC_ATTR_ICV_LEN		(1 << 1)
+#define MACSEC_ATTR_CIPHER_SUITE	(1 << 2)
+#define MACSEC_ATTR_WINDOW		(1 << 3)
+#define MACSEC_ATTR_ENCODING_SA		(1 << 4)
+#define MACSEC_ATTR_ENCRYPT		(1 << 5)
+#define MACSEC_ATTR_PROTECT		(1 << 6)
+#define MACSEC_ATTR_INC_SCI		(1 << 7)
+#define MACSEC_ATTR_ES			(1 << 8)
+#define MACSEC_ATTR_SCB			(1 << 9)
+#define MACSEC_ATTR_REPLAY_PROTECT	(1 << 10)
+#define MACSEC_ATTR_VALIDATION		(1 << 11)
+#define MACSEC_ATTR_PORT		(1 << 12)
+
+struct macsec_info {
+	int ifindex;
+	uint64_t sci;
+	uint16_t port;
+	uint64_t cipher_suite;
+	uint16_t icv_len;
+	uint32_t window;
+	enum macsec_validation_type validate;
+	uint8_t encoding_sa;
+
+	uint8_t send_sci, end_station, scb, replay_protect, protect, encrypt;
+
+	uint32_t ce_mask;
+};
+
+#define DEFAULT_ICV_LEN 16
+
+/** @endcond */
+
+static struct nla_policy macsec_policy[IFLA_MACSEC_MAX+1] = {
+	[IFLA_MACSEC_SCI] = { .type = NLA_U64 },
+	[IFLA_MACSEC_ICV_LEN] = { .type = NLA_U8 },
+	[IFLA_MACSEC_CIPHER_SUITE] = { .type = NLA_U64 },
+	[IFLA_MACSEC_WINDOW] = { .type = NLA_U32 },
+	[IFLA_MACSEC_ENCODING_SA] = { .type = NLA_U8 },
+	[IFLA_MACSEC_ENCRYPT] = { .type = NLA_U8 },
+	[IFLA_MACSEC_PROTECT] = { .type = NLA_U8 },
+	[IFLA_MACSEC_INC_SCI] = { .type = NLA_U8 },
+	[IFLA_MACSEC_ES] = { .type = NLA_U8 },
+	[IFLA_MACSEC_SCB] = { .type = NLA_U8 },
+	[IFLA_MACSEC_REPLAY_PROTECT] = { .type = NLA_U8 },
+	[IFLA_MACSEC_VALIDATION] = { .type = NLA_U8 },
+};
+
+/**
+ * @name MACsec Object
+ * @{
+ */
+
+/**
+ * Allocate link object of type MACsec
+ *
+ * @return Allocated link object or NULL.
+ */
+static int macsec_alloc(struct rtnl_link *link)
+{
+	struct macsec_info *info;
+
+	if (!link->l_info) {
+		link->l_info = malloc(sizeof(struct macsec_info));
+		if (!link->l_info)
+			return -NLE_NOMEM;
+	}
+
+	memset(link->l_info, 0, sizeof(struct macsec_info));
+	info = link->l_info;
+
+	info->cipher_suite = MACSEC_DEFAULT_CIPHER_ID;
+	info->icv_len = DEFAULT_ICV_LEN;
+	info->ce_mask = MACSEC_ATTR_CIPHER_SUITE | MACSEC_ATTR_ICV_LEN;
+
+	return 0;
+}
+
+static int macsec_parse(struct rtnl_link *link, struct nlattr *data,
+		      struct nlattr *xstats)
+{
+	struct nlattr *tb[IFLA_MACSEC_MAX+1];
+	struct macsec_info *info;
+	int err;
+
+	NL_DBG(3, "Parsing MACsec link info\n");
+
+	if ((err = nla_parse_nested(tb, IFLA_MACSEC_MAX, data, macsec_policy)) < 0)
+		goto errout;
+
+	if ((err = macsec_alloc(link)) < 0)
+		goto errout;
+
+	info = link->l_info;
+
+	if (tb[IFLA_MACSEC_SCI]) {
+		info->sci = nla_get_u64(tb[IFLA_MACSEC_SCI]);
+		info->ce_mask |= MACSEC_ATTR_SCI;
+	}
+
+	if (tb[IFLA_MACSEC_PROTECT]) {
+		info->protect = nla_get_u8(tb[IFLA_MACSEC_PROTECT]);
+		info->ce_mask |= MACSEC_ATTR_PROTECT;
+	}
+
+	if (tb[IFLA_MACSEC_CIPHER_SUITE]) {
+		info->cipher_suite = nla_get_u64(tb[IFLA_MACSEC_CIPHER_SUITE]);
+		info->ce_mask |= MACSEC_ATTR_CIPHER_SUITE;
+	}
+
+	if (tb[IFLA_MACSEC_ICV_LEN]) {
+		info->icv_len = nla_get_u8(tb[IFLA_MACSEC_ICV_LEN]);
+		info->ce_mask |= MACSEC_ATTR_ICV_LEN;
+	}
+
+	if (tb[IFLA_MACSEC_ENCODING_SA]) {
+		info->encoding_sa = nla_get_u8(tb[IFLA_MACSEC_ENCODING_SA]);
+		info->ce_mask |= MACSEC_ATTR_ENCODING_SA;
+	}
+
+	if (tb[IFLA_MACSEC_VALIDATION]) {
+		info->validate = nla_get_u8(tb[IFLA_MACSEC_VALIDATION]);
+		info->ce_mask |= MACSEC_ATTR_VALIDATION;
+	}
+
+	if (tb[IFLA_MACSEC_ENCRYPT]) {
+		info->encrypt = nla_get_u8(tb[IFLA_MACSEC_ENCRYPT]);
+		info->ce_mask |= MACSEC_ATTR_ENCRYPT;
+	}
+
+	if (tb[IFLA_MACSEC_INC_SCI]) {
+		info->send_sci = nla_get_u8(tb[IFLA_MACSEC_INC_SCI]);
+		info->ce_mask |= MACSEC_ATTR_INC_SCI;
+	}
+
+	if (tb[IFLA_MACSEC_ES]) {
+		info->end_station = nla_get_u8(tb[IFLA_MACSEC_ES]);
+		info->ce_mask |= MACSEC_ATTR_ES;
+	}
+
+	if (tb[IFLA_MACSEC_SCB]) {
+		info->scb = nla_get_u8(tb[IFLA_MACSEC_SCB]);
+		info->ce_mask |= MACSEC_ATTR_SCB;
+	}
+
+	if (tb[IFLA_MACSEC_REPLAY_PROTECT]) {
+		info->replay_protect = nla_get_u8(tb[IFLA_MACSEC_REPLAY_PROTECT]);
+		info->ce_mask |= MACSEC_ATTR_REPLAY_PROTECT;
+	}
+
+	if (tb[IFLA_MACSEC_WINDOW]) {
+		info->window = nla_get_u32(tb[IFLA_MACSEC_WINDOW]);
+		info->ce_mask |= MACSEC_ATTR_WINDOW;
+	}
+
+	err = 0;
+errout:
+	return err;
+}
+
+static void macsec_free(struct rtnl_link *link)
+{
+	free(link->l_info);
+	link->l_info = NULL;
+}
+
+static const char *values_on_off[] = {	"off", "on" };
+
+static const char *VALIDATE_STR[] = {
+	[MACSEC_VALIDATE_DISABLED] = "disabled",
+	[MACSEC_VALIDATE_CHECK] = "check",
+	[MACSEC_VALIDATE_STRICT] = "strict",
+};
+
+static char *replay_protect_str(char *buf, uint8_t replay_protect, uint8_t window)
+{
+	if (replay_protect == 1) {
+		sprintf(buf, "replay_protect on window %d", window);
+	} else if (replay_protect == 0) {
+		sprintf(buf, "replay_protect off");
+	} else {
+		buf[0] = '\0';
+	}
+
+	return buf;
+}
+
+/** @cond SKIP */
+#define PRINT_FLAG(buf, i, field, c) ({ if (i->field == 1) *buf++ = c; })
+/** @endcond */
+static char *flags_str(char *buf, unsigned char len, struct macsec_info *info)
+{
+	char *tmp = buf;
+	memset(tmp, 0, len);
+
+	PRINT_FLAG(tmp, info, protect, 'P');
+	PRINT_FLAG(tmp, info, encrypt, 'E');
+	PRINT_FLAG(tmp, info, send_sci, 'S');
+	PRINT_FLAG(tmp, info, end_station, 'e');
+	PRINT_FLAG(tmp, info, scb, 's');
+	PRINT_FLAG(tmp, info, replay_protect, 'R');
+
+	*tmp++ = ' ';
+	*tmp++ = 'v';
+	switch (info->validate) {
+	case MACSEC_VALIDATE_DISABLED:
+		*tmp++ = 'd';
+		break;
+	case MACSEC_VALIDATE_CHECK:
+		*tmp++ = 'c';
+		break;
+	case MACSEC_VALIDATE_STRICT:
+		*tmp++ = 's';
+		break;
+	default:
+		break;
+	}
+
+	sprintf(tmp, " %d", info->encoding_sa);
+
+	return buf;
+}
+
+static void macsec_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
+{
+	struct macsec_info *info = link->l_info;
+	char tmp[128];
+
+	nl_dump(p, "sci %016llx <%s>", ntohll(info->sci), flags_str(tmp, sizeof(tmp), info));
+}
+
+static void macsec_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
+{
+	struct macsec_info *info = link->l_info;
+	char tmp[128];
+
+	nl_dump(p, "    sci %016llx protect %s encoding_sa %d encrypt %s send_sci %s validate %s %s\n",
+		ntohll(info->sci), values_on_off[info->protect], info->encoding_sa, values_on_off[info->encrypt], values_on_off[info->send_sci],
+		VALIDATE_STR[info->validate],
+		replay_protect_str(tmp, info->replay_protect, info->window));
+	nl_dump(p, "    cipher suite: %016llx, icv_len %d\n",
+		info->cipher_suite, info->icv_len);
+}
+
+static int macsec_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+	struct macsec_info *copy, *info = src->l_info;
+	int err;
+
+	dst->l_info = NULL;
+	if ((err = rtnl_link_set_type(dst, "macsec")) < 0)
+		return err;
+	copy = dst->l_info;
+
+	if (!info || !copy)
+		return -NLE_NOMEM;
+
+	memcpy(copy, info, sizeof(struct macsec_info));
+
+	return 0;
+}
+
+static int macsec_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+	struct macsec_info *info = link->l_info;
+	struct nlattr *data;
+
+	if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
+		return -NLE_MSGSIZE;
+
+	if (info->ce_mask & MACSEC_ATTR_SCI)
+		NLA_PUT_U64(msg, IFLA_MACSEC_SCI, info->sci);
+	else if (info->ce_mask & MACSEC_ATTR_PORT)
+		NLA_PUT_U16(msg, IFLA_MACSEC_PORT, htons(info->port));
+
+	if ((info->ce_mask & MACSEC_ATTR_ENCRYPT))
+		NLA_PUT_U8(msg, IFLA_MACSEC_ENCRYPT, info->encrypt);
+
+	if (info->cipher_suite != MACSEC_DEFAULT_CIPHER_ID || info->icv_len != DEFAULT_ICV_LEN) {
+		NLA_PUT_U64(msg, IFLA_MACSEC_CIPHER_SUITE, info->cipher_suite);
+		NLA_PUT_U8(msg, IFLA_MACSEC_ICV_LEN, info->icv_len);
+	}
+
+	if ((info->ce_mask & MACSEC_ATTR_INC_SCI))
+		NLA_PUT_U8(msg, IFLA_MACSEC_INC_SCI, info->send_sci);
+
+	if ((info->ce_mask & MACSEC_ATTR_ES))
+		NLA_PUT_U8(msg, IFLA_MACSEC_ES, info->end_station);
+
+	if ((info->ce_mask & MACSEC_ATTR_SCB))
+		NLA_PUT_U8(msg, IFLA_MACSEC_SCB, info->scb);
+
+	if ((info->ce_mask & MACSEC_ATTR_PROTECT))
+		NLA_PUT_U8(msg, IFLA_MACSEC_PROTECT, info->protect);
+
+	if ((info->ce_mask & MACSEC_ATTR_REPLAY_PROTECT)) {
+		if (info->replay_protect && !(info->ce_mask & MACSEC_ATTR_WINDOW))
+			return -NLE_INVAL;
+
+		NLA_PUT_U8(msg, IFLA_MACSEC_REPLAY_PROTECT, info->replay_protect);
+		NLA_PUT_U32(msg, IFLA_MACSEC_WINDOW, info->window);
+	}
+
+	if ((info->ce_mask & MACSEC_ATTR_VALIDATION))
+		NLA_PUT_U8(msg, IFLA_MACSEC_VALIDATION, info->validate);
+
+	if ((info->ce_mask & MACSEC_ATTR_ENCODING_SA))
+		NLA_PUT_U8(msg, IFLA_MACSEC_ENCODING_SA, info->encoding_sa);
+
+	nla_nest_end(msg, data);
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_MSGSIZE;
+}
+
+static int macsec_compare(struct rtnl_link *link_a, struct rtnl_link *link_b,
+			  int flags)
+{
+	struct macsec_info *a = link_a->l_info;
+	struct macsec_info *b = link_b->l_info;
+	int diff = 0;
+	uint32_t attrs = flags & LOOSE_COMPARISON ? b->ce_mask : ~0;
+
+#define MACSEC_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, MACSEC_ATTR_##ATTR, a, b, EXPR)
+
+	if (a->ce_mask & MACSEC_ATTR_SCI && b->ce_mask & MACSEC_ATTR_SCI)
+		diff |= MACSEC_DIFF(SCI, a->sci != b->sci);
+	else if (a->ce_mask & MACSEC_ATTR_PORT && b->ce_mask & MACSEC_ATTR_PORT)
+		diff |= MACSEC_DIFF(PORT, a->port != b->port);
+
+	if (a->ce_mask & MACSEC_ATTR_CIPHER_SUITE && b->ce_mask & MACSEC_ATTR_CIPHER_SUITE) {
+		diff |= MACSEC_DIFF(ICV_LEN, a->icv_len != b->icv_len);
+		diff |= MACSEC_DIFF(CIPHER_SUITE, a->cipher_suite != b->cipher_suite);
+	}
+
+	if (a->ce_mask & MACSEC_ATTR_REPLAY_PROTECT && b->ce_mask & MACSEC_ATTR_REPLAY_PROTECT) {
+		int d = MACSEC_DIFF(REPLAY_PROTECT, a->replay_protect != b->replay_protect);
+		if (a->replay_protect && b->replay_protect)
+			d |= MACSEC_DIFF(WINDOW, a->window != b->window);
+		diff |= d;
+	}
+
+	diff |= MACSEC_DIFF(ENCODING_SA, a->encoding_sa != b->encoding_sa);
+	diff |= MACSEC_DIFF(ENCRYPT, a->encrypt != b->encrypt);
+	diff |= MACSEC_DIFF(PROTECT, a->protect != b->protect);
+	diff |= MACSEC_DIFF(INC_SCI, a->send_sci != b->send_sci);
+	diff |= MACSEC_DIFF(ES, a->end_station != b->end_station);
+	diff |= MACSEC_DIFF(SCB, a->scb != b->scb);
+	diff |= MACSEC_DIFF(VALIDATION, a->validate != b->validate);
+#undef MACSEC_DIFF
+
+	return diff;
+}
+
+
+static struct rtnl_link_info_ops macsec_info_ops = {
+	.io_name		= "macsec",
+	.io_alloc		= macsec_alloc,
+	.io_parse		= macsec_parse,
+	.io_dump = {
+		[NL_DUMP_LINE] = macsec_dump_line,
+		[NL_DUMP_DETAILS]	= macsec_dump_details,
+	},
+	.io_clone		= macsec_clone,
+	.io_put_attrs		= macsec_put_attrs,
+	.io_free		= macsec_free,
+	.io_compare		= macsec_compare,
+};
+
+static void __init macsec_init(void)
+{
+	rtnl_link_register_info(&macsec_info_ops);
+}
+
+static void __exit macsec_exit(void)
+{
+	rtnl_link_unregister_info(&macsec_info_ops);
+}
+
+/** @cond SKIP */
+#define IS_MACSEC_LINK_ASSERT(link) \
+	if ((link)->l_info_ops != &macsec_info_ops) { \
+		APPBUG("Link is not a MACsec link. set type \"macsec\" first."); \
+		return -NLE_OPNOTSUPP; \
+	}
+/** @endcond */
+
+struct rtnl_link *rtnl_link_macsec_alloc(void)
+{
+	struct rtnl_link *link = rtnl_link_alloc();
+
+	if (!link)
+		return NULL;
+
+	if (rtnl_link_set_type(link, "macsec") < 0) {
+		rtnl_link_put(link);
+		return NULL;
+	}
+
+	return link;
+}
+
+/**
+ * Set SCI
+ * @arg link		Link object
+ * @arg sci		Secure Channel Identifier in network byte order
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_macsec_set_sci(struct rtnl_link *link, uint64_t sci)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	info->sci = sci;
+	info->ce_mask |= MACSEC_ATTR_SCI;
+
+	return 0;
+}
+
+/**
+ * Get SCI
+ * @arg link		Link object
+ * @arg sci		On return points to the Secure Channel Identifier
+ *			in network byte order
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_macsec_get_sci(struct rtnl_link *link, uint64_t *sci)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (!(info->ce_mask & MACSEC_ATTR_SCI))
+		return -NLE_NOATTR;
+
+	if (sci)
+		*sci = info->sci;
+
+	return 0;
+}
+
+/**
+ * Set port identifier
+ * @arg link		Link object
+ * @arg port		Port identifier in host byte order
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_macsec_set_port(struct rtnl_link *link, uint16_t port)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	info->port = port;
+	info->ce_mask |= MACSEC_ATTR_PORT;
+
+	return 0;
+}
+
+/**
+ * Get port identifier
+ * @arg link		Link object
+ * @arg port		On return points to the port identifier in host byte order
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_macsec_get_port(struct rtnl_link *link, uint16_t *port)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (!(info->ce_mask & MACSEC_ATTR_PORT))
+		return -NLE_NOATTR;
+
+	if (port)
+		*port = info->port;
+
+	return 0;
+}
+
+int rtnl_link_macsec_set_cipher_suite(struct rtnl_link *link, uint64_t cipher_suite)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	info->cipher_suite = cipher_suite;
+	info->ce_mask |= MACSEC_ATTR_CIPHER_SUITE;
+
+	return 0;
+}
+
+int rtnl_link_macsec_get_cipher_suite(struct rtnl_link *link, uint64_t *cs)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (!(info->ce_mask & MACSEC_ATTR_CIPHER_SUITE))
+		return -NLE_NOATTR;
+
+	if (cs)
+		*cs = info->cipher_suite;
+
+	return 0;
+}
+
+int rtnl_link_macsec_set_icv_len(struct rtnl_link *link, uint16_t icv_len)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (icv_len > MACSEC_STD_ICV_LEN)
+		return -NLE_INVAL;
+
+	info->icv_len = icv_len;
+	info->ce_mask |= MACSEC_ATTR_ICV_LEN;
+
+	return 0;
+}
+
+int rtnl_link_macsec_get_icv_len(struct rtnl_link *link, uint16_t *icv_len)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (!(info->ce_mask & MACSEC_ATTR_ICV_LEN))
+		return -NLE_NOATTR;
+
+	if (icv_len)
+		*icv_len = info->icv_len;
+
+	return 0;
+}
+
+int rtnl_link_macsec_set_protect(struct rtnl_link *link, uint8_t protect)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (protect > 1)
+		return -NLE_INVAL;
+
+	info->protect = protect;
+	info->ce_mask |= MACSEC_ATTR_PROTECT;
+
+	return 0;
+}
+
+int rtnl_link_macsec_get_protect(struct rtnl_link *link, uint8_t *protect)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (!(info->ce_mask & MACSEC_ATTR_PROTECT))
+		return -NLE_NOATTR;
+
+	if (protect)
+		*protect = info->protect;
+
+	return 0;
+}
+
+int rtnl_link_macsec_set_encrypt(struct rtnl_link *link, uint8_t encrypt)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (encrypt > 1)
+		return -NLE_INVAL;
+
+	info->encrypt = encrypt;
+	info->ce_mask |= MACSEC_ATTR_ENCRYPT;
+
+	return 0;
+}
+
+int rtnl_link_macsec_get_encrypt(struct rtnl_link *link, uint8_t *encrypt)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (!(info->ce_mask & MACSEC_ATTR_ENCRYPT))
+		return -NLE_NOATTR;
+
+	if (encrypt)
+		*encrypt = info->encrypt;
+
+	return 0;
+}
+
+int rtnl_link_macsec_set_encoding_sa(struct rtnl_link *link, uint8_t encoding_sa)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (encoding_sa > 3)
+		return -NLE_INVAL;
+
+	info->encoding_sa = encoding_sa;
+	info->ce_mask |= MACSEC_ATTR_ENCODING_SA;
+
+	return 0;
+}
+
+int rtnl_link_macsec_get_encoding_sa(struct rtnl_link *link, uint8_t *encoding_sa)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (!(info->ce_mask & MACSEC_ATTR_ENCODING_SA))
+		return -NLE_NOATTR;
+
+	if (encoding_sa)
+		*encoding_sa = info->encoding_sa;
+
+	return 0;
+}
+
+int rtnl_link_macsec_set_validation_type(struct rtnl_link *link, enum macsec_validation_type validate)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (validate > 1)
+		return -NLE_INVAL;
+
+	info->validate = validate;
+	info->ce_mask |= MACSEC_ATTR_VALIDATION;
+
+	return 0;
+}
+
+int rtnl_link_macsec_get_validation_type(struct rtnl_link *link, enum macsec_validation_type *validate)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (!(info->ce_mask & MACSEC_ATTR_VALIDATION))
+		return -NLE_NOATTR;
+
+	if (validate)
+		*validate = info->validate;
+
+	return 0;
+}
+
+int rtnl_link_macsec_set_replay_protect(struct rtnl_link *link, uint8_t replay_protect)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (replay_protect > 1)
+		return -NLE_INVAL;
+
+	info->replay_protect = replay_protect;
+	info->ce_mask |= MACSEC_ATTR_REPLAY_PROTECT;
+
+	return 0;
+}
+
+int rtnl_link_macsec_get_replay_protect(struct rtnl_link *link, uint8_t *replay_protect)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (!(info->ce_mask & MACSEC_ATTR_REPLAY_PROTECT))
+		return -NLE_NOATTR;
+
+	if (replay_protect)
+		*replay_protect = info->replay_protect;
+
+	return 0;
+}
+
+int rtnl_link_macsec_set_window(struct rtnl_link *link, uint32_t window)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	info->window = window;
+	info->ce_mask |= MACSEC_ATTR_WINDOW;
+
+	return 0;
+}
+
+int rtnl_link_macsec_get_window(struct rtnl_link *link, uint32_t *window)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (!(info->ce_mask & MACSEC_ATTR_WINDOW))
+		return -NLE_NOATTR;
+
+	if (window)
+		*window = info->window;
+
+	return 0;
+}
+
+int rtnl_link_macsec_set_send_sci(struct rtnl_link *link, uint8_t send_sci)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (send_sci > 1)
+		return -NLE_INVAL;
+
+	info->send_sci = send_sci;
+	info->ce_mask |= MACSEC_ATTR_INC_SCI;
+
+	return 0;
+}
+
+int rtnl_link_macsec_get_send_sci(struct rtnl_link *link, uint8_t *send_sci)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (!(info->ce_mask & MACSEC_ATTR_INC_SCI))
+		return -NLE_NOATTR;
+
+	if (send_sci)
+		*send_sci = info->send_sci;
+
+	return 0;
+}
+
+int rtnl_link_macsec_set_end_station(struct rtnl_link *link, uint8_t end_station)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (end_station > 1)
+		return -NLE_INVAL;
+
+	info->end_station = end_station;
+	info->ce_mask |= MACSEC_ATTR_ES;
+
+	return 0;
+}
+
+int rtnl_link_macsec_get_end_station(struct rtnl_link *link, uint8_t *es)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (!(info->ce_mask & MACSEC_ATTR_ES))
+		return -NLE_NOATTR;
+
+	if (es)
+		*es = info->end_station;
+
+	return 0;
+}
+
+int rtnl_link_macsec_set_scb(struct rtnl_link *link, uint8_t scb)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (scb > 1)
+		return -NLE_INVAL;
+
+	info->scb = scb;
+	info->ce_mask |= MACSEC_ATTR_SCB;
+
+	return 0;
+}
+
+int rtnl_link_macsec_get_scb(struct rtnl_link *link, uint8_t *scb)
+{
+	struct macsec_info *info = link->l_info;
+
+	IS_MACSEC_LINK_ASSERT(link);
+
+	if (!(info->ce_mask & MACSEC_ATTR_SCB))
+		return -NLE_NOATTR;
+
+	if (scb)
+		*scb = info->scb;
+
+	return 0;
+}
+
+/** @} */
+
+/** @} */
diff --git a/lib/route/link/macvlan.c b/lib/route/link/macvlan.c
index 2340903..a23fe6d 100644
--- a/lib/route/link/macvlan.c
+++ b/lib/route/link/macvlan.c
@@ -1,23 +1,24 @@
 /*
- * lib/route/link/macvlan.c	MACVLAN Link Info
+ * lib/route/link/macvlan.c     MACVLAN Link Info
  *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
  *
  * Copyright (c) 2013 Michael Braun <michael-dev@fami-braun.de>
  */
 
 /**
  * @ingroup link
- * @defgroup macvlan MACVLAN
+ * @defgroup macvlan MACVLAN/MACVTAP
  * MAC-based Virtual LAN link module
  *
  * @details
  * \b Link Type Name: "macvlan"
  *
  * @route_doc{link_macvlan, MACVLAN Documentation}
+ * @route_doc{link_macvtap, MACVTAP Documentation}
  *
  * @{
  */
@@ -30,35 +31,54 @@
 #include <netlink/route/rtnl.h>
 #include <netlink-private/route/link/api.h>
 #include <netlink/route/link/macvlan.h>
+#include <netlink/route/link/macvtap.h>
 
 #include <linux/if_link.h>
 
 /** @cond SKIP */
-#define MACVLAN_HAS_MODE	(1<<0)
-#define MACVLAN_HAS_FLAGS	(1<<1)
+#define MACVLAN_HAS_MODE        (1<<0)
+#define MACVLAN_HAS_FLAGS       (1<<1)
+#define MACVLAN_HAS_MACADDR     (1<<2)
 
 struct macvlan_info
 {
-	uint32_t		mvi_mode;
-	uint16_t		mvi_flags; // there currently is only one flag and kernel has no flags_mask yet
-	uint32_t		mvi_mask;
+	uint32_t                mvi_mode;
+	uint16_t                mvi_flags; // there currently is only one flag and kernel has no flags_mask yet
+	uint32_t                mvi_mask;
+	uint32_t                mvi_maccount;
+	uint32_t                mvi_macmode;
+	struct nl_addr          **mvi_macaddr;
 };
 
 /** @endcond */
 
 static struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX+1] = {
-	[IFLA_MACVLAN_MODE]	= { .type = NLA_U32 },
-	[IFLA_MACVLAN_FLAGS]	= { .type = NLA_U16 },
+	[IFLA_MACVLAN_MODE]             = { .type = NLA_U32 },
+	[IFLA_MACVLAN_FLAGS]            = { .type = NLA_U16 },
+	[IFLA_MACVLAN_MACADDR_MODE]     = { .type = NLA_U32 },
+	[IFLA_MACVLAN_MACADDR]          = { .type = NLA_UNSPEC },
+	[IFLA_MACVLAN_MACADDR_DATA]     = { .type = NLA_NESTED },
+	[IFLA_MACVLAN_MACADDR_COUNT]    = { .type = NLA_U32 },
 };
 
 static int macvlan_alloc(struct rtnl_link *link)
 {
 	struct macvlan_info *mvi;
+	uint32_t i;
 
-	if ((mvi = calloc(1, sizeof(*mvi))) == NULL)
-		return -NLE_NOMEM;
+	if (link->l_info) {
+		mvi = link->l_info;
+		for (i = 0; i < mvi->mvi_maccount; i++)
+			nl_addr_put(mvi->mvi_macaddr[i]);
+		free(mvi->mvi_macaddr);
+		memset(mvi, 0, sizeof(*mvi));
+	} else {
+		if ((mvi = calloc(1, sizeof(*mvi))) == NULL)
+			return -NLE_NOMEM;
 
-	link->l_info = mvi;
+		link->l_info = mvi;
+	}
+	mvi->mvi_macmode = MACVLAN_MACADDR_SET;
 
 	return 0;
 }
@@ -68,9 +88,11 @@
 {
 	struct nlattr *tb[IFLA_MACVLAN_MAX+1];
 	struct macvlan_info *mvi;
+	struct nlattr *nla;
+	int len;
 	int err;
 
-	NL_DBG(3, "Parsing MACVLAN link info");
+	NL_DBG(3, "Parsing %s link info", link->l_info_ops->io_name);
 
 	if ((err = nla_parse_nested(tb, IFLA_MACVLAN_MAX, data, macvlan_policy)) < 0)
 		goto errout;
@@ -86,10 +108,36 @@
 	}
 
 	if (tb[IFLA_MACVLAN_FLAGS]) {
-		mvi->mvi_mode = nla_get_u16(tb[IFLA_MACVLAN_FLAGS]);
+		mvi->mvi_flags = nla_get_u16(tb[IFLA_MACVLAN_FLAGS]);
 		mvi->mvi_mask |= MACVLAN_HAS_FLAGS;
 	}
 
+	if (   tb[IFLA_MACVLAN_MACADDR_COUNT]
+	    && tb[IFLA_MACVLAN_MACADDR_DATA]) {
+		mvi->mvi_maccount = nla_get_u32(tb[IFLA_MACVLAN_MACADDR_COUNT]);
+		if (mvi->mvi_maccount > 0) {
+			uint32_t i;
+
+			nla = nla_data(tb[IFLA_MACVLAN_MACADDR_DATA]);
+			len = nla_len(tb[IFLA_MACVLAN_MACADDR_DATA]);
+
+			mvi->mvi_macaddr = calloc(mvi->mvi_maccount,
+			                          sizeof(*(mvi->mvi_macaddr)));
+
+			i = 0;
+			for (; nla_ok(nla, len); nla = nla_next(nla, &len)) {
+				if (i >= mvi->mvi_maccount)
+					break;
+				if (nla_type(nla) != IFLA_MACVLAN_MACADDR ||
+				    nla_len(nla) < ETH_ALEN)
+					continue;
+				mvi->mvi_macaddr[i] = nl_addr_alloc_attr(nla, AF_LLC);
+				i++;
+			}
+		}
+		mvi->mvi_mask |= MACVLAN_HAS_MACADDR;
+	}
+
 	err = 0;
 errout:
 	return err;
@@ -97,30 +145,54 @@
 
 static void macvlan_free(struct rtnl_link *link)
 {
-	free(link->l_info);
+	struct macvlan_info *mvi;
+	uint32_t i;
+
+	mvi = link->l_info;
+
+	for (i = 0; i < mvi->mvi_maccount; i++)
+		nl_addr_put(mvi->mvi_macaddr[i]);
+	free(mvi->mvi_macaddr);
+	free(mvi);
+
 	link->l_info = NULL;
 }
 
-static void macvlan_dump(struct rtnl_link *link, struct nl_dump_params *p)
+static void macvlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
 {
 	char buf[64];
+	uint32_t i;
 	struct macvlan_info *mvi = link->l_info;
 
 	if (mvi->mvi_mask & MACVLAN_HAS_MODE) {
 		rtnl_link_macvlan_mode2str(mvi->mvi_mode, buf, sizeof(buf));
-		nl_dump(p, "macvlan-mode %s", buf);
+		nl_dump(p, "    %s-mode %s", link->l_info_ops->io_name, buf);
 	}
 
 	if (mvi->mvi_mask & MACVLAN_HAS_FLAGS) {
 		rtnl_link_macvlan_flags2str(mvi->mvi_flags, buf, sizeof(buf));
-		nl_dump(p, "macvlan-flags %s", buf);
+		nl_dump(p, " %s-flags %s", link->l_info_ops->io_name, buf);
 	}
+
+	if (mvi->mvi_mask & MACVLAN_HAS_MACADDR) {
+		nl_dump(p, " macvlan-count %u", (unsigned) mvi->mvi_maccount);
+
+		if (mvi->mvi_maccount)
+			nl_dump(p, " macvlan-sourcemac");
+
+		for (i = 0; i < mvi->mvi_maccount; i++) {
+			nl_dump(p, " %s", nl_addr2str(mvi->mvi_macaddr[i], buf,
+			        sizeof(buf)));
+		}
+	}
+	nl_dump(p, "\n");
 }
 
 static int macvlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
 {
 	struct macvlan_info *vdst, *vsrc = src->l_info;
 	int err;
+	uint32_t i;
 
 	dst->l_info = NULL;
 	if ((err = rtnl_link_set_type(dst, "macvlan")) < 0)
@@ -132,41 +204,80 @@
 
 	memcpy(vdst, vsrc, sizeof(struct macvlan_info));
 
+	if (   vsrc->mvi_mask & MACVLAN_HAS_MACADDR
+	    && vsrc->mvi_maccount > 0) {
+		vdst->mvi_macaddr = calloc(vdst->mvi_maccount,
+		                           sizeof(*(vdst->mvi_macaddr)));
+		for (i = 0; i < vdst->mvi_maccount; i++)
+			vdst->mvi_macaddr[i] = nl_addr_clone(vsrc->mvi_macaddr[i]);
+	} else
+		vdst->mvi_macaddr = NULL;
+
 	return 0;
 }
 
 static int macvlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
 {
 	struct macvlan_info *mvi = link->l_info;
-	struct nlattr *data;
+	struct nlattr *data, *datamac = NULL;
+	int i, ret;
 
 	if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
 		return -NLE_MSGSIZE;
 
+	ret = -NLE_NOMEM;
+
 	if (mvi->mvi_mask & MACVLAN_HAS_MODE)
 		NLA_PUT_U32(msg, IFLA_MACVLAN_MODE, mvi->mvi_mode);
 
 	if (mvi->mvi_mask & MACVLAN_HAS_FLAGS)
 		NLA_PUT_U16(msg, IFLA_MACVLAN_FLAGS, mvi->mvi_flags);
 
-	nla_nest_end(msg, data);
+	if (mvi->mvi_mask & MACVLAN_HAS_MACADDR) {
+		NLA_PUT_U32(msg, IFLA_MACVLAN_MACADDR_MODE, mvi->mvi_macmode);
+		datamac = nla_nest_start(msg, IFLA_MACVLAN_MACADDR_DATA);
+		if (!datamac)
+			goto nla_put_failure;
+
+		for (i = 0; i < mvi->mvi_maccount; i++) {
+			NLA_PUT_ADDR(msg, IFLA_MACVLAN_MACADDR,
+			             mvi->mvi_macaddr[i]);
+		}
+	}
+
+	ret = 0;
 
 nla_put_failure:
+	if (datamac)
+		nla_nest_end(msg, datamac);
 
-	return 0;
+	nla_nest_end(msg, data);
+
+	return ret;
 }
 
 static struct rtnl_link_info_ops macvlan_info_ops = {
-	.io_name		= "macvlan",
-	.io_alloc		= macvlan_alloc,
-	.io_parse		= macvlan_parse,
+	.io_name                = "macvlan",
+	.io_alloc               = macvlan_alloc,
+	.io_parse               = macvlan_parse,
 	.io_dump = {
-	    [NL_DUMP_LINE]	= macvlan_dump,
-	    [NL_DUMP_DETAILS]	= macvlan_dump,
+		[NL_DUMP_DETAILS] = macvlan_dump_details,
 	},
-	.io_clone		= macvlan_clone,
-	.io_put_attrs		= macvlan_put_attrs,
-	.io_free		= macvlan_free,
+	.io_clone               = macvlan_clone,
+	.io_put_attrs           = macvlan_put_attrs,
+	.io_free                = macvlan_free,
+};
+
+static struct rtnl_link_info_ops macvtap_info_ops = {
+	.io_name                = "macvtap",
+	.io_alloc               = macvlan_alloc,
+	.io_parse               = macvlan_parse,
+	.io_dump = {
+		[NL_DUMP_DETAILS] = macvlan_dump_details,
+	},
+	.io_clone               = macvlan_clone,
+	.io_put_attrs           = macvlan_put_attrs,
+	.io_free                = macvlan_free,
 };
 
 /** @cond SKIP */
@@ -175,6 +286,12 @@
 		APPBUG("Link is not a macvlan link. set type \"macvlan\" first."); \
 		return -NLE_OPNOTSUPP; \
 	}
+
+#define IS_MACVTAP_LINK_ASSERT(link) \
+	if ((link)->l_info_ops != &macvtap_info_ops) { \
+		APPBUG("Link is not a macvtap link. set type \"macvtap\" first."); \
+		return -NLE_OPNOTSUPP; \
+	}
 /** @endcond */
 
 /**
@@ -205,7 +322,7 @@
 
 /**
  * Check if link is a MACVLAN link
- * @arg link		Link object
+ * @arg link            Link object
  *
  * @return True if link is a MACVLAN link, otherwise false is returned.
  */
@@ -216,26 +333,37 @@
 
 /**
  * Set MACVLAN MODE
- * @arg link		Link object
- * @arg mode		MACVLAN mode
+ * @arg link            Link object
+ * @arg mode            MACVLAN mode
  *
  * @return 0 on success or a negative error code
  */
 int rtnl_link_macvlan_set_mode(struct rtnl_link *link, uint32_t mode)
 {
 	struct macvlan_info *mvi = link->l_info;
+	int i;
 
 	IS_MACVLAN_LINK_ASSERT(link);
 
 	mvi->mvi_mode = mode;
 	mvi->mvi_mask |= MACVLAN_HAS_MODE;
 
+	if (mode != MACVLAN_MODE_SOURCE) {
+		for (i = 0; i < mvi->mvi_maccount; i++)
+			nl_addr_put(mvi->mvi_macaddr[i]);
+		free(mvi->mvi_macaddr);
+		mvi->mvi_maccount = 0;
+		mvi->mvi_macaddr = NULL;
+		mvi->mvi_macmode = MACVLAN_MACADDR_SET;
+		mvi->mvi_mask &= ~MACVLAN_HAS_MACADDR;
+	}
+
 	return 0;
 }
 
 /**
  * Get MACVLAN Mode
- * @arg link		Link object
+ * @arg link            Link object
  *
  * @return MACVLAN mode, 0 if not set or a negative error code.
  */
@@ -252,9 +380,61 @@
 }
 
 /**
+ * Set MACVLAN MACMODE
+ * @arg link            Link object
+ * @arg mode            MACVLAN mac list modification mode
+ *
+ * Only for macvlan SOURCE mode.
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_macvlan_set_macmode(struct rtnl_link *link, uint32_t macmode)
+{
+	struct macvlan_info *mvi = link->l_info;
+
+	IS_MACVLAN_LINK_ASSERT(link);
+
+	if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) ||
+	    (mvi->mvi_mode != MACVLAN_MODE_SOURCE))
+		return -NLE_INVAL;
+
+	mvi->mvi_macmode = macmode;
+	mvi->mvi_mask |= MACVLAN_HAS_MACADDR;
+
+	return 0;
+}
+
+/**
+ * Get MACVLAN MACMODE
+ * @arg link            Link object
+ * @arg out_macmode     mac list modification mode
+ *
+ * Only for SOURCE mode.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_macvlan_get_macmode(struct rtnl_link *link, uint32_t *out_macmode)
+{
+	struct macvlan_info *mvi = link->l_info;
+
+	IS_MACVLAN_LINK_ASSERT(link);
+
+	if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) ||
+	    (mvi->mvi_mode != MACVLAN_MODE_SOURCE))
+		return -NLE_INVAL;
+
+	if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR))
+		return -NLE_INVAL;
+
+	*out_macmode = mvi->mvi_macmode;
+
+	return 0;
+}
+
+/**
  * Set MACVLAN flags
- * @arg link		Link object
- * @arg flags		MACVLAN flags
+ * @arg link            Link object
+ * @arg flags           MACVLAN flags
  *
  * @return 0 on success or a negative error code.
  */
@@ -272,8 +452,8 @@
 
 /**
  * Unset MACVLAN flags
- * @arg link		Link object
- * @arg flags		MACVLAN flags
+ * @arg link            Link object
+ * @arg flags           MACVLAN flags
  *
  * Note: kernel currently only has a single flag and lacks flags_mask to
  * indicate which flags shall be changed (it always all).
@@ -294,7 +474,7 @@
 
 /**
  * Get MACVLAN flags
- * @arg link		Link object
+ * @arg link            Link object
  *
  * @return MACVLAN flags, 0 if none set, or a negative error code.
  */
@@ -307,17 +487,309 @@
 	return mvi->mvi_flags;
 }
 
+/**
+ * Get number of MAC-Addr for MACVLAN device in source mode
+ * @arg link            Link object
+ * @arg out_count       number of mac addresses
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_macvlan_count_macaddr(struct rtnl_link *link, uint32_t *out_count)
+{
+	struct macvlan_info *mvi = link->l_info;
+
+	IS_MACVLAN_LINK_ASSERT(link);
+
+	if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) ||
+	    (mvi->mvi_mode != MACVLAN_MODE_SOURCE))
+		return -NLE_INVAL;
+
+	if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR))
+		return -NLE_INVAL;
+
+	*out_count = mvi->mvi_maccount;
+
+	return 0;
+}
+
+/**
+ * Get configured remote MAC-Addr from MACVLAN device in source mode
+ * @arg link            Link object
+ * @arg out_addr        address object
+ *
+ * The returned nl_addr struct needs NOT to be released using nl_addr_put.
+ * It is only valid until the address is not removed from this link object
+ * or its mode is changed to non-source.
+ *
+ * @return 0 on success or negative error code
+ */
+int rtnl_link_macvlan_get_macaddr(struct rtnl_link *link, uint32_t idx,
+                                  const struct nl_addr **out_addr)
+{
+	struct macvlan_info *mvi = link->l_info;
+
+	IS_MACVLAN_LINK_ASSERT(link);
+
+	if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) ||
+	    (mvi->mvi_mode != MACVLAN_MODE_SOURCE))
+		return -NLE_INVAL;
+
+	if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR))
+		return -NLE_INVAL;
+
+	if (idx >= mvi->mvi_maccount)
+		return -NLE_INVAL;
+
+	*out_addr = mvi->mvi_macaddr[idx];
+	return 0;
+}
+
+/**
+ * Add MAC-Addr to MACVLAN device in source mode
+ * @arg link            Link object
+ * @arg addr            MAC-Addr
+ *
+ * addr is not release but cloned by this method.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_macvlan_add_macaddr(struct rtnl_link *link, struct nl_addr *addr)
+{
+	struct macvlan_info *mvi = link->l_info;
+	struct nl_addr **mvi_macaddr;
+	size_t newsize;
+
+	IS_MACVLAN_LINK_ASSERT(link);
+
+	if (nl_addr_get_family(addr) != AF_LLC)
+		return -NLE_INVAL;
+
+	if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) ||
+	    (mvi->mvi_mode != MACVLAN_MODE_SOURCE))
+		return -NLE_INVAL;
+
+	if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR))
+		return -NLE_INVAL;
+
+	if (mvi->mvi_maccount >= UINT32_MAX)
+		return -NLE_INVAL;
+
+	newsize = (mvi->mvi_maccount + 1) * sizeof(*(mvi->mvi_macaddr));
+	mvi_macaddr = realloc(mvi->mvi_macaddr, newsize);
+	if (!mvi_macaddr)
+		return -NLE_NOMEM;
+
+	mvi->mvi_macaddr = mvi_macaddr;
+	mvi->mvi_macaddr[mvi->mvi_maccount] = nl_addr_clone(addr);
+	mvi->mvi_maccount++;
+
+	mvi->mvi_mask |= MACVLAN_HAS_MACADDR;
+
+	return 0;
+}
+
+/**
+ * Remove MAC-Addr from MACVLAN device in source mode
+ * @arg link            Link object
+ * @arg addr            MAC-Addr
+ *
+ * addr is not release by this method.
+ *
+ * @return a negative error code on failure, or the number
+ *   of deleted addresses on success.
+ */
+int rtnl_link_macvlan_del_macaddr(struct rtnl_link *link, struct nl_addr *addr)
+{
+	struct macvlan_info *mvi = link->l_info;
+	uint32_t found, i;
+
+	IS_MACVLAN_LINK_ASSERT(link);
+
+	if (nl_addr_get_family(addr) != AF_LLC)
+		return -NLE_INVAL;
+
+	if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) ||
+	    (mvi->mvi_mode != MACVLAN_MODE_SOURCE))
+		return -NLE_INVAL;
+
+	if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR))
+		return -NLE_INVAL;
+
+	nl_addr_get(addr);
+
+	found = 0; i = 0;
+	while (i + found < mvi->mvi_maccount) {
+		mvi->mvi_macaddr[i] = mvi->mvi_macaddr[i + found];
+		if (found > 0)
+			mvi->mvi_macaddr[i + found] = NULL;
+		if (nl_addr_cmp(addr, mvi->mvi_macaddr[i]) == 0) {
+			nl_addr_put(mvi->mvi_macaddr[i]);
+			mvi->mvi_macaddr[i] = NULL;
+			found++;
+		} else
+			i++;
+	}
+
+	nl_addr_put(addr);
+
+	mvi->mvi_maccount -= found;
+
+	return found > INT_MAX ? INT_MAX : (int) found;
+}
+
 /** @} */
 
+
+/**
+ * @name MACVTAP Object
+ * @{
+ */
+
+/**
+ * Allocate link object of type MACVTAP
+ *
+ * @return Allocated link object or NULL.
+ */
+struct rtnl_link *rtnl_link_macvtap_alloc(void)
+{
+	struct rtnl_link *link;
+	int err;
+
+	if (!(link = rtnl_link_alloc()))
+		return NULL;
+
+	if ((err = rtnl_link_set_type(link, "macvtap")) < 0) {
+		rtnl_link_put(link);
+		return NULL;
+	}
+
+	return link;
+}
+
+/**
+ * Check if link is a MACVTAP link
+ * @arg link            Link object
+ *
+ * @return True if link is a MACVTAP link, otherwise false is returned.
+ */
+int rtnl_link_is_macvtap(struct rtnl_link *link)
+{
+	return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "macvtap");
+}
+
+/**
+ * Set MACVTAP MODE
+ * @arg link            Link object
+ * @arg mode            MACVTAP mode
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_macvtap_set_mode(struct rtnl_link *link, uint32_t mode)
+{
+	struct macvlan_info *mvi = link->l_info;
+
+	IS_MACVTAP_LINK_ASSERT(link);
+
+	mvi->mvi_mode = mode;
+	mvi->mvi_mask |= MACVLAN_HAS_MODE;
+
+	return 0;
+}
+
+/**
+ * Get MACVTAP Mode
+ * @arg link            Link object
+ *
+ * @return MACVTAP mode, 0 if not set or a negative error code.
+ */
+uint32_t rtnl_link_macvtap_get_mode(struct rtnl_link *link)
+{
+	struct macvlan_info *mvi = link->l_info;
+
+	IS_MACVTAP_LINK_ASSERT(link);
+
+	if (mvi->mvi_mask & MACVLAN_HAS_MODE)
+		return mvi->mvi_mode;
+	else
+		return 0;
+}
+
+/**
+ * Set MACVTAP flags
+ * @arg link            Link object
+ * @arg flags           MACVTAP flags
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_macvtap_set_flags(struct rtnl_link *link, uint16_t flags)
+{
+	struct macvlan_info *mvi = link->l_info;
+
+	IS_MACVTAP_LINK_ASSERT(link);
+
+	mvi->mvi_flags |= flags;
+	mvi->mvi_mask |= MACVLAN_HAS_FLAGS;
+
+	return 0;
+}
+
+/**
+ * Unset MACVTAP flags
+ * @arg link            Link object
+ * @arg flags           MACVTAP flags
+ *
+ * Note: kernel currently only has a single flag and lacks flags_mask to
+ * indicate which flags shall be changed (it always all).
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_macvtap_unset_flags(struct rtnl_link *link, uint16_t flags)
+{
+	struct macvlan_info *mvi = link->l_info;
+
+	IS_MACVTAP_LINK_ASSERT(link);
+
+	mvi->mvi_flags &= ~flags;
+	mvi->mvi_mask |= MACVLAN_HAS_FLAGS;
+
+	return 0;
+}
+
+/**
+ * Get MACVTAP flags
+ * @arg link            Link object
+ *
+ * @return MACVTAP flags, 0 if none set, or a negative error code.
+ */
+uint16_t rtnl_link_macvtap_get_flags(struct rtnl_link *link)
+{
+	struct macvlan_info *mvi = link->l_info;
+
+	IS_MACVTAP_LINK_ASSERT(link);
+
+	return mvi->mvi_flags;
+}
+
+/** @} */
+
+
 static const struct trans_tbl macvlan_flags[] = {
-	__ADD(MACVLAN_FLAG_NOPROMISC, nopromisc)
+	__ADD(MACVLAN_FLAG_NOPROMISC, nopromisc),
 };
 
 static const struct trans_tbl macvlan_modes[] = {
-	__ADD(MACVLAN_MODE_PRIVATE, private)
-	__ADD(MACVLAN_MODE_VEPA, vepa)
-	__ADD(MACVLAN_MODE_BRIDGE, bridge)
-	__ADD(MACVLAN_MODE_PASSTHRU, passthru)
+	__ADD(MACVLAN_MODE_PRIVATE, private),
+	__ADD(MACVLAN_MODE_VEPA, vepa),
+	__ADD(MACVLAN_MODE_BRIDGE, bridge),
+	__ADD(MACVLAN_MODE_PASSTHRU, passthru),
+	__ADD(MACVLAN_MODE_SOURCE, source),
+};
+
+static const struct trans_tbl macvlan_macmodes[] = {
+	__ADD(MACVLAN_MACADDR_ADD, "add"),
+	__ADD(MACVLAN_MACADDR_DEL, "del"),
+	__ADD(MACVLAN_MACADDR_SET, "set"),
+	__ADD(MACVLAN_MACADDR_FLUSH, "flush"),
 };
 
 /**
@@ -335,6 +807,16 @@
 	return __str2flags(name, macvlan_flags, ARRAY_SIZE(macvlan_flags));
 }
 
+char *rtnl_link_macvtap_flags2str(int flags, char *buf, size_t len)
+{
+	return __flags2str(flags, buf, len, macvlan_flags, ARRAY_SIZE(macvlan_flags));
+}
+
+int rtnl_link_macvtap_str2flags(const char *name)
+{
+	return __str2flags(name, macvlan_flags, ARRAY_SIZE(macvlan_flags));
+}
+
 /** @} */
 
 /**
@@ -352,16 +834,39 @@
 	return __str2type(name, macvlan_modes, ARRAY_SIZE(macvlan_modes));
 }
 
+char *rtnl_link_macvlan_macmode2str(int mode, char *buf, size_t len)
+{
+	return __type2str(mode, buf, len, macvlan_macmodes,
+	                  ARRAY_SIZE(macvlan_macmodes));
+}
+
+int rtnl_link_macvlan_str2macmode(const char *name)
+{
+	return __str2type(name, macvlan_macmodes, ARRAY_SIZE(macvlan_macmodes));
+}
+
+char *rtnl_link_macvtap_mode2str(int mode, char *buf, size_t len)
+{
+	return __type2str(mode, buf, len, macvlan_modes, ARRAY_SIZE(macvlan_modes));
+}
+
+int rtnl_link_macvtap_str2mode(const char *name)
+{
+	return __str2type(name, macvlan_modes, ARRAY_SIZE(macvlan_modes));
+}
+
 /** @} */
 
 static void __init macvlan_init(void)
 {
 	rtnl_link_register_info(&macvlan_info_ops);
+	rtnl_link_register_info(&macvtap_info_ops);
 }
 
 static void __exit macvlan_exit(void)
 {
 	rtnl_link_unregister_info(&macvlan_info_ops);
+	rtnl_link_unregister_info(&macvtap_info_ops);
 }
 
 /** @} */
diff --git a/lib/route/link/ppp.c b/lib/route/link/ppp.c
new file mode 100644
index 0000000..b05e7f3
--- /dev/null
+++ b/lib/route/link/ppp.c
@@ -0,0 +1,224 @@
+/*
+ * lib/route/link/ppp.c		PPP Link Module
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2016 Jonas Johansson <jonasj76@gmail.com>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup ppp PPP
+ *
+ * @details
+ * \b Link Type Name: "ppp"
+ *
+ * @route_doc{link_ppp, PPP Documentation}
+ * @{
+ */
+
+#include <netlink/route/link/ppp.h>
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink-private/route/link/api.h>
+
+/** @cond SKIP */
+#define PPP_ATTR_FD		(1<<0)
+
+struct ppp_info
+{
+	int32_t			pi_fd;
+	uint32_t		ce_mask;
+};
+
+/** @endcond */
+
+static struct nla_policy ppp_nl_policy[IFLA_PPP_MAX+1] = {
+	[IFLA_PPP_DEV_FD]	= { .type = NLA_S32 },
+};
+
+static int ppp_alloc(struct rtnl_link *link)
+{
+	struct ppp_info *info;
+
+	if (link->l_info)
+		memset(link->l_info, 0, sizeof(*info));
+	else {
+		if ((info = calloc(1, sizeof(*info))) == NULL)
+			return -NLE_NOMEM;
+
+		link->l_info = info;
+	}
+
+	return 0;
+}
+
+static int ppp_parse(struct rtnl_link *link, struct nlattr *data,
+                         struct nlattr *xstats)
+{
+	struct nlattr *tb[IFLA_PPP_MAX+1];
+	struct ppp_info *info;
+	int err;
+
+	NL_DBG(3, "Parsing PPP link info\n");
+
+	if ((err = nla_parse_nested(tb, IFLA_PPP_MAX, data, ppp_nl_policy)) < 0)
+		goto errout;
+
+	if ((err = ppp_alloc(link)) < 0)
+		goto errout;
+
+	info = link->l_info;
+
+	if (tb[IFLA_PPP_DEV_FD]) {
+		info->pi_fd = nla_get_s32(tb[IFLA_PPP_DEV_FD]);
+		info->ce_mask |= PPP_ATTR_FD;
+	}
+
+	err = 0;
+errout:
+	return err;
+}
+
+static void ppp_free(struct rtnl_link *link)
+{
+	free(link->l_info);
+	link->l_info = NULL;
+}
+
+static int ppp_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+	struct ppp_info *vdst, *vsrc = src->l_info;
+	int err;
+
+	dst->l_info = NULL;
+	if ((err = rtnl_link_set_type(dst, "ppp")) < 0)
+		return err;
+	vdst = dst->l_info;
+
+	if (!vdst || !vsrc)
+		return -NLE_NOMEM;
+
+	memcpy(vdst, vsrc, sizeof(struct ppp_info));
+
+	return 0;
+}
+
+static int ppp_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+	struct ppp_info *info = link->l_info;
+	struct nlattr *data;
+
+	if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
+		return -NLE_MSGSIZE;
+
+	if (info->ce_mask & PPP_ATTR_FD)
+		NLA_PUT_S32(msg, IFLA_PPP_DEV_FD, info->pi_fd);
+
+	nla_nest_end(msg, data);
+
+nla_put_failure:
+
+	return 0;
+}
+
+static struct rtnl_link_info_ops ppp_info_ops = {
+	.io_name		= "ppp",
+	.io_alloc		= ppp_alloc,
+	.io_parse		= ppp_parse,
+	.io_clone		= ppp_clone,
+	.io_put_attrs		= ppp_put_attrs,
+	.io_free		= ppp_free,
+};
+
+/** @cond SKIP */
+#define IS_PPP_LINK_ASSERT(link) \
+	if ((link)->l_info_ops != &ppp_info_ops) { \
+		APPBUG("Link is not a PPP link. set type \"ppp\" first."); \
+		return -NLE_OPNOTSUPP; \
+	}
+/** @endcond */
+
+/**
+ * @name PPP Object
+ * @{
+ */
+
+/**
+ * Allocate link object of type PPP
+ *
+ * @return Allocated link object or NULL.
+ */
+struct rtnl_link *rtnl_link_ppp_alloc(void)
+{
+	struct rtnl_link *link;
+	int err;
+
+	if (!(link = rtnl_link_alloc()))
+		return NULL;
+
+	if ((err = rtnl_link_set_type(link, "ppp")) < 0) {
+		rtnl_link_put(link);
+		return NULL;
+	}
+
+	return link;
+}
+
+/**
+ * Set PPP file descriptor
+ * @arg link		Link object
+ * @arg flags		PPP file descriptor
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_ppp_set_fd(struct rtnl_link *link, int32_t fd)
+{
+	struct ppp_info *info = link->l_info;
+
+	IS_PPP_LINK_ASSERT(link);
+
+	info->pi_fd |= fd;
+	info->ce_mask |= PPP_ATTR_FD;
+
+	return 0;
+}
+
+/**
+ * Get PPP file descriptor
+ * @arg link		Link object
+ *
+ * @return PPP file descriptor, 0 if not set or a negative error code.
+ */
+int rtnl_link_ppp_get_fd(struct rtnl_link *link, int32_t *fd)
+{
+	struct ppp_info *info = link->l_info;
+
+	IS_PPP_LINK_ASSERT(link);
+
+	if (!(info->ce_mask & PPP_ATTR_FD))
+		return -NLE_NOATTR;
+
+	if (fd)
+		*fd = info->pi_fd;
+
+	return 0;
+}
+
+/** @} */
+
+static void __init ppp_init(void)
+{
+	rtnl_link_register_info(&ppp_info_ops);
+}
+
+static void __exit ppp_exit(void)
+{
+	rtnl_link_unregister_info(&ppp_info_ops);
+}
+
+/** @} */
diff --git a/lib/route/link/sit.c b/lib/route/link/sit.c
index 694c177..8856513 100644
--- a/lib/route/link/sit.c
+++ b/lib/route/link/sit.c
@@ -28,6 +28,7 @@
 #include <netlink/utils.h>
 #include <netlink/object.h>
 #include <netlink/route/rtnl.h>
+#include <netlink/route/link/sit.h>
 #include <netlink-private/route/link/api.h>
 #include <linux/if_tunnel.h>
 
@@ -39,6 +40,10 @@
 #define SIT_ATTR_PMTUDISC      (1 << 5)
 #define SIT_ATTR_FLAGS         (1 << 6)
 #define SIT_ATTR_PROTO         (1 << 7)
+#define SIT_ATTR_6RD_PREFIX          (1 << 8)
+#define SIT_ATTR_6RD_RELAY_PREFIX    (1 << 9)
+#define SIT_ATTR_6RD_PREFIXLEN       (1 << 10)
+#define SIT_ATTR_6RD_RELAY_PREFIXLEN (1 << 11)
 
 struct sit_info
 {
@@ -50,6 +55,10 @@
 	uint32_t   link;
 	uint32_t   local;
 	uint32_t   remote;
+	struct in6_addr ip6rd_prefix;
+	uint32_t   ip6rd_relay_prefix;
+	uint16_t   ip6rd_prefixlen;
+	uint16_t   ip6rd_relay_prefixlen;
 	uint32_t   sit_mask;
 };
 
@@ -62,17 +71,25 @@
 	[IFLA_IPTUN_PMTUDISC]   = { .type = NLA_U8 },
 	[IFLA_IPTUN_FLAGS]      = { .type = NLA_U16 },
 	[IFLA_IPTUN_PROTO]      = { .type = NLA_U8 },
+	[IFLA_IPTUN_6RD_PREFIX]          = { .minlen = sizeof(struct in6_addr) },
+	[IFLA_IPTUN_6RD_RELAY_PREFIX]    = { .type = NLA_U32 },
+	[IFLA_IPTUN_6RD_PREFIXLEN]       = { .type = NLA_U16 },
+	[IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NLA_U16 },
 };
 
 static int sit_alloc(struct rtnl_link *link)
 {
 	struct sit_info *sit;
 
-	sit = calloc(1, sizeof(*sit));
-	if (!sit)
-		return -NLE_NOMEM;
+	if (link->l_info)
+		memset(link->l_info, 0, sizeof(*sit));
+	else {
+		sit = calloc(1, sizeof(*sit));
+		if (!sit)
+			return -NLE_NOMEM;
 
-	link->l_info = sit;
+		link->l_info = sit;
+	}
 
 	return 0;
 }
@@ -84,7 +101,7 @@
 	struct sit_info *sit;
 	int err;
 
-	NL_DBG(3, "Parsing SIT link info");
+	NL_DBG(3, "Parsing SIT link info\n");
 
 	err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, sit_policy);
 	if (err < 0)
@@ -136,9 +153,30 @@
 		sit->sit_mask |= SIT_ATTR_PROTO;
 	}
 
+	if (tb[IFLA_IPTUN_6RD_PREFIX]) {
+		nla_memcpy(&sit->ip6rd_prefix, tb[IFLA_IPTUN_6RD_PREFIX],
+			   sizeof(struct in6_addr));
+		sit->sit_mask |= SIT_ATTR_6RD_PREFIX;
+	}
+
+	if (tb[IFLA_IPTUN_6RD_RELAY_PREFIX]) {
+		sit->ip6rd_relay_prefix = nla_get_u32(tb[IFLA_IPTUN_6RD_RELAY_PREFIX]);
+		sit->sit_mask |= SIT_ATTR_6RD_RELAY_PREFIX;
+	}
+
+	if (tb[IFLA_IPTUN_6RD_PREFIXLEN]) {
+		sit->ip6rd_prefixlen = nla_get_u16(tb[IFLA_IPTUN_6RD_PREFIXLEN]);
+		sit->sit_mask |= SIT_ATTR_6RD_PREFIXLEN;
+	}
+
+	if (tb[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]) {
+		sit->ip6rd_relay_prefixlen = nla_get_u16(tb[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]);
+		sit->sit_mask |= SIT_ATTR_6RD_RELAY_PREFIXLEN;
+	}
+
 	err = 0;
 
- errout:
+errout:
 	return err;
 }
 
@@ -175,6 +213,18 @@
 	if (sit->sit_mask & SIT_ATTR_PROTO)
 		NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, sit->proto);
 
+	if (sit->sit_mask & SIT_ATTR_6RD_PREFIX)
+		NLA_PUT(msg, IFLA_IPTUN_6RD_PREFIX, sizeof(struct in6_addr), &sit->ip6rd_prefix);
+
+	if (sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIX)
+		NLA_PUT_U32(msg, IFLA_IPTUN_6RD_RELAY_PREFIX, sit->ip6rd_relay_prefix);
+
+	if (sit->sit_mask & SIT_ATTR_6RD_PREFIXLEN)
+		NLA_PUT_U16(msg, IFLA_IPTUN_6RD_PREFIXLEN, sit->ip6rd_prefixlen);
+
+	if (sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIXLEN)
+		NLA_PUT_U16(msg, IFLA_IPTUN_6RD_RELAY_PREFIXLEN, sit->ip6rd_relay_prefixlen);
+
 	nla_nest_end(msg, data);
 
 nla_put_failure:
@@ -198,11 +248,17 @@
 static void sit_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
 {
 	struct sit_info *sit = link->l_info;
-	char *name, addr[INET_ADDRSTRLEN];
+	char *name, addr[INET_ADDRSTRLEN], addr6[INET6_ADDRSTRLEN];
+	struct rtnl_link *parent;
 
 	if (sit->sit_mask & SIT_ATTR_LINK) {
 		nl_dump(p, "      link ");
-		name = rtnl_link_get_name(link);
+
+		name = NULL;
+		parent = link_lookup(link->ce_cache, sit->link);
+		if (parent)
+			name = rtnl_link_get_name(parent);
+
 		if (name)
 			nl_dump_line(p, "%s\n", name);
 		else
@@ -241,9 +297,35 @@
 	}
 
 	if (sit->sit_mask & SIT_ATTR_PROTO) {
-		nl_dump(p, "    proto   ");
+		nl_dump(p, "      proto   ");
 		nl_dump_line(p, " (%x)\n", sit->proto);
 	}
+
+	if (sit->sit_mask & SIT_ATTR_6RD_PREFIX) {
+		nl_dump(p, "      6rd_prefix   ");
+		if(inet_ntop(AF_INET6, &sit->ip6rd_prefix, addr6, INET6_ADDRSTRLEN))
+			nl_dump_line(p, "%s\n", addr6);
+		else
+			nl_dump_line(p, "[unknown]\n");
+	}
+
+	if (sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIX) {
+		nl_dump(p, "      6rd_relay_prefix   ");
+		if(inet_ntop(AF_INET, &sit->ip6rd_relay_prefix, addr, sizeof(addr)))
+			nl_dump_line(p, "%s\n", addr);
+		else
+			nl_dump_line(p, "[unknown]\n");
+	}
+
+	if (sit->sit_mask & SIT_ATTR_6RD_PREFIXLEN) {
+		nl_dump(p, "      6rd_prefixlen   ");
+		nl_dump_line(p, "%d\n", sit->ip6rd_prefixlen);
+	}
+
+	if (sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIXLEN) {
+		nl_dump(p, "      6rd_relay_prefixlen   ");
+		nl_dump_line(p, "%d\n", sit->ip6rd_relay_prefixlen);
+	}
 }
 
 static int sit_clone(struct rtnl_link *dst, struct rtnl_link *src)
@@ -280,11 +362,16 @@
 	.io_free                = sit_free,
 };
 
-#define IS_SIT_LINK_ASSERT(link)                                           \
-        if ((link)->l_info_ops != &sit_info_ops) {                         \
-                APPBUG("Link is not a sit link. set type \"sit\" first."); \
-                return -NLE_OPNOTSUPP;                                     \
-        }
+#define IS_SIT_LINK_ASSERT(link, sit)                                              \
+        struct sit_info *sit;                                                      \
+        do {                                                                       \
+                const struct rtnl_link *_link = (link);                            \
+                if (!_link || _link->l_info_ops != &sit_info_ops) {                \
+                        APPBUG("Link is not a sit link. set type \"sit\" first."); \
+                        return -NLE_OPNOTSUPP;                                     \
+                }                                                                  \
+                (sit) = _link->l_info;                                             \
+        } while (0)
 
 struct rtnl_link *rtnl_link_sit_alloc(void)
 {
@@ -350,9 +437,7 @@
  */
 int rtnl_link_sit_set_link(struct rtnl_link *link,  uint32_t index)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	sit->link = index;
 	sit->sit_mask |= SIT_ATTR_LINK;
@@ -368,9 +453,7 @@
  */
 uint32_t rtnl_link_sit_get_link(struct rtnl_link *link)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	return sit->link;
 }
@@ -384,9 +467,7 @@
  */
 int rtnl_link_sit_set_local(struct rtnl_link *link, uint32_t addr)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	sit->local = addr;
 	sit->sit_mask |= SIT_ATTR_LOCAL;
@@ -402,9 +483,7 @@
  */
 uint32_t rtnl_link_sit_get_local(struct rtnl_link *link)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	return sit->local;
 }
@@ -418,9 +497,7 @@
  */
 int rtnl_link_sit_set_remote(struct rtnl_link *link, uint32_t addr)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	sit->remote = addr;
 	sit->sit_mask |= SIT_ATTR_REMOTE;
@@ -436,9 +513,7 @@
  */
 uint32_t rtnl_link_sit_get_remote(struct rtnl_link *link)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	return sit->remote;
 }
@@ -452,9 +527,7 @@
  */
 int rtnl_link_sit_set_ttl(struct rtnl_link *link, uint8_t ttl)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	sit->ttl = ttl;
 	sit->sit_mask |= SIT_ATTR_TTL;
@@ -470,9 +543,7 @@
  */
 uint8_t rtnl_link_sit_get_ttl(struct rtnl_link *link)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	return sit->ttl;
 }
@@ -486,9 +557,7 @@
  */
 int rtnl_link_sit_set_tos(struct rtnl_link *link, uint8_t tos)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	sit->tos = tos;
 	sit->sit_mask |= SIT_ATTR_TOS;
@@ -504,9 +573,7 @@
  */
 uint8_t rtnl_link_sit_get_tos(struct rtnl_link *link)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	return sit->tos;
 }
@@ -520,9 +587,7 @@
  */
 int rtnl_link_sit_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	sit->pmtudisc = pmtudisc;
 	sit->sit_mask |= SIT_ATTR_PMTUDISC;
@@ -538,9 +603,7 @@
  */
 uint8_t rtnl_link_sit_get_pmtudisc(struct rtnl_link *link)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	return sit->pmtudisc;
 }
@@ -554,9 +617,7 @@
  */
 int rtnl_link_sit_set_flags(struct rtnl_link *link, uint16_t flags)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	sit->flags = flags;
 	sit->sit_mask |= SIT_ATTR_FLAGS;
@@ -572,9 +633,7 @@
  */
 uint16_t rtnl_link_sit_get_flags(struct rtnl_link *link)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	return sit->flags;
 }
@@ -588,9 +647,7 @@
  */
 int rtnl_link_sit_set_proto(struct rtnl_link *link, uint8_t proto)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	sit->proto = proto;
 	sit->sit_mask |= SIT_ATTR_PROTO;
@@ -606,13 +663,155 @@
  */
 uint8_t rtnl_link_sit_get_proto(struct rtnl_link *link)
 {
-	struct sit_info *sit = link->l_info;
-
-	IS_SIT_LINK_ASSERT(link);
+	IS_SIT_LINK_ASSERT(link, sit);
 
 	return sit->proto;
 }
 
+/**
+ * Set ip6rd prefix
+ * @arg link            Link object
+ * @arg prefix          The IPv6 prefix
+ *
+ * @return 0 on success or an error code.
+ */
+int rtnl_link_sit_set_ip6rd_prefix(struct rtnl_link *link, const struct in6_addr *prefix)
+{
+	IS_SIT_LINK_ASSERT(link, sit);
+
+	sit->ip6rd_prefix = *prefix;
+	sit->sit_mask |= SIT_ATTR_6RD_PREFIX;
+	return 0;
+}
+
+/**
+ * Get ip6rd prefix
+ * @arg link            Link object
+ * @arg prefix          The output IPv6 prefix
+ *
+ * @return 0 on success or an error code. If the property is unset,
+ *   this call fails too.
+ */
+int rtnl_link_sit_get_ip6rd_prefix(const struct rtnl_link *link, struct in6_addr *prefix)
+{
+	IS_SIT_LINK_ASSERT(link, sit);
+
+	if (!(sit->sit_mask & SIT_ATTR_6RD_PREFIX))
+		return -NLE_NOATTR;
+
+	if (prefix)
+		*prefix = sit->ip6rd_prefix;
+	return 0;
+}
+
+/**
+ * Set ip6rd prefix length
+ * @arg link            Link object
+ * @arg prefixlen       The IPv6 prefix length
+ *
+ * @return 0 on success or an error code.
+ */
+int rtnl_link_sit_set_ip6rd_prefixlen(struct rtnl_link *link, uint16_t prefixlen)
+{
+	IS_SIT_LINK_ASSERT(link, sit);
+
+	sit->sit_mask |= SIT_ATTR_6RD_PREFIXLEN;
+	sit->ip6rd_prefixlen = prefixlen;
+	return 0;
+}
+
+/**
+ * Get ip6rd prefix length
+ * @arg link            Link object
+ * @arg prefixlen       Output pointer for the prefix length
+ *
+ * @return 0 on success or an error code. If the property is unset,
+ *   this call fails.
+ */
+int rtnl_link_sit_get_ip6rd_prefixlen(struct rtnl_link *link, uint16_t *prefixlen)
+{
+	IS_SIT_LINK_ASSERT(link, sit);
+
+	if (!(sit->sit_mask & SIT_ATTR_6RD_PREFIXLEN))
+		return -NLE_NOATTR;
+
+	if (prefixlen)
+		*prefixlen = sit->ip6rd_prefixlen;
+	return 0;
+}
+
+/**
+ * Set ip6rd relay prefix
+ * @arg link            Link object
+ * @arg prefix          The IPv6 prefix length
+ *
+ * @return 0 on success or an error code.
+ */
+int rtnl_link_sit_set_ip6rd_relay_prefix(struct rtnl_link *link, uint32_t prefix)
+{
+	IS_SIT_LINK_ASSERT(link, sit);
+
+	sit->sit_mask |= SIT_ATTR_6RD_RELAY_PREFIX;
+	sit->ip6rd_relay_prefix = prefix;
+	return 0;
+}
+
+/**
+ * Get ip6rd prefix length
+ * @arg link            Link object
+ * @arg prefixlen       Output pointer for the prefix length
+ *
+ * @return 0 on success or an error code. If the property is unset,
+ *   this call fails.
+ */
+int rtnl_link_sit_get_ip6rd_relay_prefix(const struct rtnl_link *link, uint32_t *prefix)
+{
+	IS_SIT_LINK_ASSERT(link, sit);
+
+	if (!(sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIX))
+		return -NLE_NOATTR;
+
+	if (prefix)
+		*prefix = sit->ip6rd_relay_prefix;
+	return 0;
+}
+
+/**
+ * Set ip6rd relay prefix length
+ * @arg link            Link object
+ * @arg prefixlen       The IPv6 prefix length
+ *
+ * @return 0 on success or an error code.
+ */
+int rtnl_link_sit_set_ip6rd_relay_prefixlen(struct rtnl_link *link, uint16_t prefixlen)
+{
+	IS_SIT_LINK_ASSERT(link, sit);
+
+	sit->sit_mask |= SIT_ATTR_6RD_RELAY_PREFIXLEN;
+	sit->ip6rd_relay_prefixlen = prefixlen;
+	return 0;
+}
+
+/**
+ * Get ip6rd relay prefix length
+ * @arg link            Link object
+ * @arg prefixlen       Output pointer for the prefix length
+ *
+ * @return 0 on success or an error code. If the property is unset,
+ *   this call fails.
+ */
+int rtnl_link_sit_get_ip6rd_relay_prefixlen(struct rtnl_link *link, uint16_t *prefixlen)
+{
+	IS_SIT_LINK_ASSERT(link, sit);
+
+	if (!(sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIX))
+		return -NLE_NOATTR;
+
+	if (prefixlen)
+		*prefixlen = sit->ip6rd_relay_prefixlen;
+	return 0;
+}
+
 static void __init sit_init(void)
 {
 	rtnl_link_register_info(&sit_info_ops);
diff --git a/lib/route/link/sriov.c b/lib/route/link/sriov.c
new file mode 100644
index 0000000..2a87cfe
--- /dev/null
+++ b/lib/route/link/sriov.c
@@ -0,0 +1,1466 @@
+/*
+ * lib/route/link/sriov.c      SRIOV VF Info
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2016 Intel Corp. All rights reserved.
+ * Copyright (c) 2016 Jef Oliver <jef.oliver@intel.com>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup sriov SRIOV
+ * SR-IOV VF link module
+ *
+ * @details
+ * SR-IOV (Single Root Input/Output Virtualization) is a network interface
+ * that allows for the isolation of the PCI Express resources. In a virtual
+ * environment, SR-IOV allows multiple virtual machines can share a single
+ * PCI Express hardware interface. This is done via VFs (Virtual Functions),
+ * virtual hardware devices with their own PCI address.
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/route/link/api.h>
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#include <linux/if_ether.h>
+#include <linux/if_link.h>
+#include <netlink-private/route/link/sriov.h>
+#include <netlink/route/link/sriov.h>
+
+/** @cond SKIP */
+
+#define SRIOVON "on"
+#define SRIOVOFF "off"
+
+#define SET_VF_STAT(link, vf_num, stb, stat, attr) \
+	vf_data->vf_stats[stat] = nla_get_u64(stb[attr])
+
+/* SRIOV-VF Attributes */
+#define SRIOV_ATTR_INDEX 		(1 <<  0)
+#define SRIOV_ATTR_ADDR 		(1 <<  1)
+#define SRIOV_ATTR_VLAN 		(1 <<  2)
+#define SRIOV_ATTR_TX_RATE 		(1 <<  3)
+#define SRIOV_ATTR_SPOOFCHK 		(1 <<  4)
+#define SRIOV_ATTR_RATE_MAX 		(1 <<  5)
+#define SRIOV_ATTR_RATE_MIN 		(1 <<  6)
+#define SRIOV_ATTR_LINK_STATE 		(1 <<  7)
+#define SRIOV_ATTR_RSS_QUERY_EN 	(1 <<  8)
+#define SRIOV_ATTR_STATS 		(1 <<  9)
+#define SRIOV_ATTR_TRUST 		(1 << 10)
+#define SRIOV_ATTR_IB_NODE_GUID 	(1 << 11)
+#define SRIOV_ATTR_IB_PORT_GUID 	(1 << 12)
+
+static struct nla_policy sriov_info_policy[IFLA_VF_MAX+1] = {
+	[IFLA_VF_MAC]		= { .minlen = sizeof(struct ifla_vf_mac) },
+	[IFLA_VF_VLAN]		= { .minlen = sizeof(struct ifla_vf_vlan) },
+	[IFLA_VF_VLAN_LIST]     = { .type = NLA_NESTED },
+	[IFLA_VF_TX_RATE]	= { .minlen = sizeof(struct ifla_vf_tx_rate) },
+	[IFLA_VF_SPOOFCHK]	= { .minlen = sizeof(struct ifla_vf_spoofchk) },
+	[IFLA_VF_RATE]		= { .minlen = sizeof(struct ifla_vf_rate) },
+	[IFLA_VF_LINK_STATE]	= { .minlen = sizeof(struct ifla_vf_link_state) },
+	[IFLA_VF_RSS_QUERY_EN]	= { .minlen = sizeof(struct ifla_vf_rss_query_en) },
+	[IFLA_VF_STATS]		= { .type = NLA_NESTED },
+	[IFLA_VF_TRUST]		= { .minlen = sizeof(struct ifla_vf_trust) },
+	[IFLA_VF_IB_NODE_GUID]	= { .minlen = sizeof(struct ifla_vf_guid) },
+	[IFLA_VF_IB_PORT_GUID]	= { .minlen = sizeof(struct ifla_vf_guid) },
+};
+
+static struct nla_policy sriov_stats_policy[IFLA_VF_STATS_MAX+1] = {
+	[IFLA_VF_STATS_RX_PACKETS]	= { .type = NLA_U64 },
+	[IFLA_VF_STATS_TX_PACKETS]	= { .type = NLA_U64 },
+	[IFLA_VF_STATS_RX_BYTES]	= { .type = NLA_U64 },
+	[IFLA_VF_STATS_TX_BYTES]	= { .type = NLA_U64 },
+	[IFLA_VF_STATS_BROADCAST]	= { .type = NLA_U64 },
+	[IFLA_VF_STATS_MULTICAST]	= { .type = NLA_U64 },
+};
+
+/** @endcond */
+
+/* Clone SRIOV VF list in link object */
+int rtnl_link_sriov_clone(struct rtnl_link *dst, struct rtnl_link *src) {
+	int err = 0;
+	struct nl_addr *vf_addr;
+	struct rtnl_link_vf *s_list, *d_vf, *s_vf, *next, *dest_h = NULL;
+	nl_vf_vlans_t *src_vlans = NULL, *dst_vlans = NULL;
+	nl_vf_vlan_info_t *src_vlan_info = NULL, *dst_vlan_info = NULL;
+
+	if (!(err = rtnl_link_has_vf_list(src)))
+		return 0;
+
+	dst->l_vf_list = rtnl_link_vf_alloc();
+	if (!dst->l_vf_list)
+		return -NLE_NOMEM;
+	dest_h = dst->l_vf_list;
+	s_list = src->l_vf_list;
+
+	nl_list_for_each_entry_safe(s_vf, next, &s_list->vf_list, vf_list) {
+		if (!(d_vf = rtnl_link_vf_alloc()))
+			return -NLE_NOMEM;
+
+		memcpy(d_vf, s_vf, sizeof(*s_vf));
+
+		if (s_vf->ce_mask & SRIOV_ATTR_ADDR) {
+			vf_addr = nl_addr_clone(s_vf->vf_lladdr);
+			if (!vf_addr) {
+				rtnl_link_vf_put(d_vf);
+				return -NLE_NOMEM;
+			}
+			d_vf->vf_lladdr = vf_addr;
+		}
+
+		if (s_vf->ce_mask & SRIOV_ATTR_VLAN) {
+			src_vlans = s_vf->vf_vlans;
+			src_vlan_info = src_vlans->vlans;
+
+			err = rtnl_link_vf_vlan_alloc(&dst_vlans,
+						      src_vlans->size);
+			if (err < 0) {
+				rtnl_link_vf_put(d_vf);
+				return err;
+			}
+			dst_vlan_info = dst_vlans->vlans;
+			memcpy(dst_vlans, src_vlans, sizeof(nl_vf_vlans_t));
+			memcpy(dst_vlan_info, src_vlan_info,
+			       dst_vlans->size * sizeof(dst_vlan_info));
+			d_vf->vf_vlans = dst_vlans;
+		}
+
+		nl_list_add_head(&d_vf->vf_list, &dest_h->vf_list);
+		dest_h = d_vf;
+	}
+
+	return 0;
+}
+
+/* Dump VLAN details for each SRIOV VF */
+static void dump_sriov_vlans(nl_vf_vlans_t *vlans,
+			     struct nl_dump_params *p) {
+	char buf[64];
+	int cur = 0;
+	nl_vf_vlan_info_t *vlan_data;
+	uint16_t prot;
+
+	vlan_data = vlans->vlans;
+	nl_dump(p, "\t      VLANS:\n");
+	while (cur < vlans->size) {
+		nl_dump(p, "\t      vlan %u", vlan_data[cur].vf_vlan);
+		if (vlan_data[cur].vf_vlan_qos)
+			nl_dump(p, " qos %u", vlan_data[cur].vf_vlan_qos);
+		if (vlan_data[cur].vf_vlan_proto) {
+			prot = vlan_data[cur].vf_vlan_proto;
+			nl_dump(p, " proto %s",
+				rtnl_link_vf_vlanproto2str(prot, buf,
+							   sizeof(buf)));
+		}
+		nl_dump(p, "\n");
+		cur++;
+	}
+
+	return;
+}
+
+/* Dump details for each SRIOV VF */
+static void dump_vf_details(struct rtnl_link_vf *vf_data,
+			    struct nl_dump_params *p) {
+	char buf[64];
+	int err = 0;
+	struct nl_vf_rate vf_rate;
+	uint32_t v = 0;
+
+	nl_dump(p, "\tvf %u: ", vf_data->vf_index);
+	if (vf_data->ce_mask & SRIOV_ATTR_LINK_STATE) {
+		v = vf_data->vf_linkstate;
+		nl_dump(p, "state %s ",
+			rtnl_link_vf_linkstate2str(v, buf, sizeof(buf)));
+	}
+	if (vf_data->ce_mask & SRIOV_ATTR_ADDR) {
+		nl_dump(p, "addr %s ",
+			nl_addr2str(vf_data->vf_lladdr, buf, sizeof(buf)));
+	}
+	nl_dump(p, "\n");
+
+	v = vf_data->vf_spoofchk;
+	nl_dump(p, "\t      spoofchk %s ", v ? SRIOVON : SRIOVOFF);
+	v = vf_data->vf_trust;
+	nl_dump(p, "trust %s ", v ? SRIOVON : SRIOVOFF);
+	v = vf_data->vf_rss_query_en;
+	nl_dump(p, "rss_query %s\n", v ? SRIOVON : SRIOVOFF);
+
+	err = rtnl_link_vf_get_rate(vf_data, &vf_rate);
+	if (!err) {
+		if (vf_rate.api == RTNL_LINK_VF_RATE_API_OLD)
+			nl_dump(p, "\t      rate_api old rate %u\n",
+				vf_rate.rate);
+		else if (vf_rate.api == RTNL_LINK_VF_RATE_API_NEW)
+			nl_dump(p, "\t      rate_api new min_rate %u "
+					"max_rate %u\n", vf_rate.min_tx_rate,
+				vf_rate.max_tx_rate);
+	}
+	if (vf_data->ce_mask & SRIOV_ATTR_VLAN)
+		dump_sriov_vlans(vf_data->vf_vlans, p);
+
+	return;
+}
+
+/* Loop through SRIOV VF list dump details */
+void rtnl_link_sriov_dump_details(struct rtnl_link *link,
+				  struct nl_dump_params *p) {
+	int err;
+	struct rtnl_link_vf *vf_data, *list, *next;
+
+	if (!(err = rtnl_link_has_vf_list(link)))
+		BUG();
+
+	nl_dump(p, "    SRIOV VF List\n");
+	list = link->l_vf_list;
+	nl_list_for_each_entry_safe(vf_data, next, &list->vf_list, vf_list) {
+		if (vf_data->ce_mask & SRIOV_ATTR_INDEX)
+			dump_vf_details(vf_data, p);
+	}
+
+	return;
+}
+
+/* Dump stats for each SRIOV VF */
+static void dump_vf_stats(struct rtnl_link_vf *vf_data,
+			  struct nl_dump_params *p) {
+	char *unit;
+	float res;
+
+	nl_dump(p, "    VF %" PRIu64 " Stats:\n", vf_data->vf_index);
+	nl_dump_line(p, "\tRX:    %-14s %-10s   %-10s %-10s\n",
+		     "bytes", "packets", "multicast", "broadcast");
+
+	res = nl_cancel_down_bytes(vf_data->vf_stats[RTNL_LINK_VF_STATS_RX_BYTES],
+				   &unit);
+
+	nl_dump_line(p,
+		"\t%10.2f %3s   %10" PRIu64 "   %10" PRIu64 " %10" PRIu64 "\n",
+		res, unit,
+		vf_data->vf_stats[RTNL_LINK_VF_STATS_RX_PACKETS],
+		vf_data->vf_stats[RTNL_LINK_VF_STATS_MULTICAST],
+		vf_data->vf_stats[RTNL_LINK_VF_STATS_BROADCAST]);
+
+	nl_dump_line(p, "\tTX:    %-14s %-10s\n", "bytes", "packets");
+
+	res = nl_cancel_down_bytes(vf_data->vf_stats[RTNL_LINK_VF_STATS_TX_BYTES],
+				   &unit);
+
+	nl_dump_line(p, "\t%10.2f %3s   %10" PRIu64 "\n", res, unit,
+		vf_data->vf_stats[RTNL_LINK_VF_STATS_TX_PACKETS]);
+
+	return;
+}
+
+/* Loop through SRIOV VF list dump stats */
+void rtnl_link_sriov_dump_stats(struct rtnl_link *link,
+				struct nl_dump_params *p) {
+	struct rtnl_link_vf *vf_data, *list, *next;
+
+	list = link->l_vf_list;
+	nl_list_for_each_entry_safe(vf_data, next, &list->vf_list, vf_list) {
+		if (vf_data->ce_mask & SRIOV_ATTR_INDEX)
+			dump_vf_stats(vf_data, p);
+	}
+	nl_dump(p, "\n");
+
+	return;
+}
+
+/* Free stored SRIOV VF data */
+void rtnl_link_sriov_free_data(struct rtnl_link *link) {
+	int err = 0;
+	struct rtnl_link_vf *list, *vf, *next;
+
+	if (!(err = rtnl_link_has_vf_list(link)))
+		return;
+
+	list = link->l_vf_list;
+	nl_list_for_each_entry_safe(vf, next, &list->vf_list, vf_list) {
+		nl_list_del(&vf->vf_list);
+		rtnl_link_vf_put(vf);
+	}
+
+	rtnl_link_vf_put(link->l_vf_list);
+
+	return;
+}
+
+/* Fill VLAN info array */
+static int rtnl_link_vf_vlan_info(int len, struct ifla_vf_vlan_info **vi,
+				  nl_vf_vlans_t **nvi) {
+	int cur = 0, err;
+	nl_vf_vlans_t *vlans;
+
+	if (len <= 0)
+		return 0;
+
+	if ((err = rtnl_link_vf_vlan_alloc(&vlans, len)) < 0)
+		return err;
+
+	cur = 0;
+	while (cur < len) {
+		vlans->vlans[cur].vf_vlan = vi[cur]->vlan ? vi[cur]->vlan : 0;
+		vlans->vlans[cur].vf_vlan_qos = vi[cur]->qos ? vi[cur]->qos : 0;
+		if (vi[cur]->vlan_proto) {
+			vlans->vlans[cur].vf_vlan_proto = ntohs(vi[cur]->vlan_proto);
+		} else {
+			vlans->vlans[cur].vf_vlan_proto = ETH_P_8021Q;
+		}
+		cur++;
+	}
+
+	*nvi = vlans;
+	return 0;
+}
+
+/* Fill the IFLA_VF_VLAN attribute */
+static void sriov_fill_vf_vlan(struct nl_msg *msg, nl_vf_vlan_info_t *vinfo,
+			       uint32_t index) {
+	struct ifla_vf_vlan vlan;
+
+	vlan.vf = index;
+	vlan.vlan = vinfo[0].vf_vlan;
+	vlan.qos = vinfo[0].vf_vlan_qos;
+	NLA_PUT(msg, IFLA_VF_VLAN, sizeof(vlan), &vlan);
+
+nla_put_failure:
+	return;
+}
+
+/* Fill the IFLA_VF_VLAN_LIST attribute */
+static int sriov_fill_vf_vlan_list(struct nl_msg *msg, nl_vf_vlans_t *vlans,
+				   uint32_t index) {
+	int cur = 0;
+	nl_vf_vlan_info_t *vlan_info = vlans->vlans;
+	struct ifla_vf_vlan_info vlan;
+	struct nlattr *list;
+
+	if (!(list = nla_nest_start(msg, IFLA_VF_VLAN_LIST)))
+		return -NLE_MSGSIZE;
+
+	vlan.vf = index;
+	while (cur < vlans->size) {
+		vlan.vlan = vlan_info[cur].vf_vlan;
+		vlan.qos = vlan_info[cur].vf_vlan_qos;
+		vlan.vlan_proto = vlan_info[cur].vf_vlan_proto;
+
+		NLA_PUT(msg, IFLA_VF_VLAN_INFO, sizeof(vlan), &vlan);
+
+		cur++;
+	}
+
+nla_put_failure:
+	nla_nest_end(msg, list);
+
+	return 0;
+}
+
+/* Fill individual IFLA_VF_INFO attributes */
+static int sriov_fill_vfinfo(struct nl_msg *msg,
+			     struct rtnl_link_vf *vf_data) {
+	int err = 0, new_rate = 0;
+	nl_vf_vlans_t *vlan_list;
+	nl_vf_vlan_info_t *vlan_info;
+	struct ifla_vf_guid vf_node_guid;
+	struct ifla_vf_guid vf_port_guid;
+	struct ifla_vf_link_state vf_link_state;
+	struct ifla_vf_mac vf_mac;
+	struct ifla_vf_rate new_vf_rate;
+	struct ifla_vf_rss_query_en vf_rss_query_en;
+	struct ifla_vf_spoofchk vf_spoofchk;
+	struct ifla_vf_trust vf_trust;
+	struct ifla_vf_tx_rate vf_rate;
+	struct nlattr *list;
+	uint16_t proto;
+
+	if (!(vf_data->ce_mask & SRIOV_ATTR_INDEX))
+		return -NLE_MISSING_ATTR;
+
+	if (!(list = nla_nest_start(msg, IFLA_VF_INFO)))
+		return -NLE_MSGSIZE;
+
+	/* IFLA_VF_MAC */
+	if (vf_data->ce_mask & SRIOV_ATTR_ADDR) {
+		vf_mac.vf = vf_data->vf_index;
+		memset(vf_mac.mac, 0, sizeof(vf_mac.mac));
+		memcpy(vf_mac.mac, nl_addr_get_binary_addr(vf_data->vf_lladdr),
+		       nl_addr_get_len(vf_data->vf_lladdr));
+		NLA_PUT(msg, IFLA_VF_MAC, sizeof(vf_mac), &vf_mac);
+	}
+
+	/* IFLA_VF_VLAN IFLA_VF_VLAN_LIST */
+	if (vf_data->ce_mask & SRIOV_ATTR_VLAN) {
+		vlan_list = vf_data->vf_vlans;
+		vlan_info = vlan_list->vlans;
+		proto = vlan_info[0].vf_vlan_proto;
+		if (!proto)
+			proto = ETH_P_8021Q;
+
+		if ((vlan_list->size == 1) && (proto == ETH_P_8021Q))
+			sriov_fill_vf_vlan(msg, vlan_info, vf_data->vf_index);
+		else
+			err = sriov_fill_vf_vlan_list(msg, vlan_list,
+						      vf_data->vf_index);
+	}
+
+	/* IFLA_VF_TX_RATE */
+	if (vf_data->ce_mask & SRIOV_ATTR_TX_RATE) {
+		vf_rate.vf = vf_data->vf_index;
+		vf_rate.rate = vf_data->vf_rate;
+
+		NLA_PUT(msg, IFLA_VF_TX_RATE, sizeof(vf_rate), &vf_rate);
+	}
+
+	/* IFLA_VF_RATE */
+	new_vf_rate.min_tx_rate = 0;
+	new_vf_rate.max_tx_rate = 0;
+	new_vf_rate.vf = vf_data->vf_index;
+	if (vf_data->ce_mask & SRIOV_ATTR_RATE_MIN) {
+		new_vf_rate.min_tx_rate = vf_data->vf_min_tx_rate;
+		new_rate = 1;
+	}
+	if (vf_data->ce_mask & SRIOV_ATTR_RATE_MAX) {
+		new_vf_rate.max_tx_rate = vf_data->vf_max_tx_rate;
+		new_rate = 1;
+	}
+	if (new_rate)
+		NLA_PUT(msg, IFLA_VF_RATE, sizeof(new_vf_rate), &new_vf_rate);
+
+	/* IFLA_VF_SPOOFCHK */
+	if (vf_data->ce_mask & SRIOV_ATTR_SPOOFCHK) {
+		vf_spoofchk.vf = vf_data->vf_index;
+		vf_spoofchk.setting = vf_data->vf_spoofchk;
+
+		NLA_PUT(msg, IFLA_VF_SPOOFCHK, sizeof(vf_spoofchk),
+			&vf_spoofchk);
+	}
+
+	/* IFLA_VF_LINK_STATE */
+	if (vf_data->ce_mask & SRIOV_ATTR_LINK_STATE) {
+		vf_link_state.vf = vf_data->vf_index;
+		vf_link_state.link_state = vf_data->vf_linkstate;
+
+		NLA_PUT(msg, IFLA_VF_LINK_STATE, sizeof(vf_link_state),
+			&vf_link_state);
+	}
+
+	/* IFLA_VF_RSS_QUERY_EN */
+	if (vf_data->ce_mask & SRIOV_ATTR_RSS_QUERY_EN) {
+		vf_rss_query_en.vf = vf_data->vf_index;
+		vf_rss_query_en.setting = vf_data->vf_rss_query_en;
+
+		NLA_PUT(msg, IFLA_VF_RSS_QUERY_EN, sizeof(vf_rss_query_en),
+			&vf_rss_query_en);
+	}
+
+	/* IFLA_VF_TRUST */
+	if (vf_data->ce_mask & SRIOV_ATTR_TRUST) {
+		vf_trust.vf = vf_data->vf_index;
+		vf_trust.setting = vf_data->vf_trust;
+
+		NLA_PUT(msg, IFLA_VF_TRUST, sizeof(vf_trust), &vf_trust);
+	}
+
+	/* IFLA_VF_IB_NODE_GUID */
+	if (vf_data->ce_mask & SRIOV_ATTR_IB_NODE_GUID) {
+		vf_node_guid.vf = vf_data->vf_index;
+		vf_node_guid.guid = vf_data->vf_guid_node;
+
+		NLA_PUT(msg, IFLA_VF_IB_NODE_GUID, sizeof(vf_node_guid),
+			&vf_node_guid);
+	}
+
+	/* IFLA_VF_IB_PORT_GUID */
+	if (vf_data->ce_mask & SRIOV_ATTR_IB_PORT_GUID) {
+		vf_port_guid.vf = vf_data->vf_index;
+		vf_port_guid.guid = vf_data->vf_guid_port;
+
+		NLA_PUT(msg, IFLA_VF_IB_PORT_GUID, sizeof(vf_port_guid),
+			&vf_port_guid);
+	}
+
+nla_put_failure:
+	nla_nest_end(msg, list);
+
+	return err;
+}
+
+/* Fill the IFLA_VFINFO_LIST attribute */
+int rtnl_link_sriov_fill_vflist(struct nl_msg *msg, struct rtnl_link *link) {
+	int err = 0;
+	struct nlattr *data;
+	struct rtnl_link_vf *list, *vf, *next;
+
+	if (!(err = rtnl_link_has_vf_list(link)))
+		return 0;
+
+	if (!(data = nla_nest_start(msg, IFLA_VFINFO_LIST)))
+		return -NLE_MSGSIZE;
+
+	list = link->l_vf_list;
+	nl_list_for_each_entry_safe(vf, next, &list->vf_list, vf_list) {
+		if (vf->ce_mask & SRIOV_ATTR_INDEX) {
+			if ((err = sriov_fill_vfinfo(msg, vf)) < 0)
+				goto nla_nest_list_failure;
+		}
+	}
+
+nla_nest_list_failure:
+	nla_nest_end(msg, data);
+
+	return err;
+}
+
+/* Parse IFLA_VFINFO_LIST and IFLA_VF_INFO attributes */
+int rtnl_link_sriov_parse_vflist(struct rtnl_link *link, struct nlattr **tb) {
+	int err, len, list_len, list_rem;
+	struct ifla_vf_mac *vf_lladdr;
+	struct ifla_vf_vlan *vf_vlan;
+	struct ifla_vf_vlan_info *vf_vlan_info[MAX_VLAN_LIST_LEN];
+	struct ifla_vf_tx_rate *vf_tx_rate;
+	struct ifla_vf_spoofchk *vf_spoofchk;
+	struct ifla_vf_link_state *vf_linkstate;
+	struct ifla_vf_rate *vf_rate;
+	struct ifla_vf_rss_query_en *vf_rss_query;
+	struct ifla_vf_trust *vf_trust;
+	struct nlattr *nla, *nla_list, *t[IFLA_VF_MAX+1],
+		*stb[RTNL_LINK_VF_STATS_MAX+1];
+	nl_vf_vlans_t *vf_vlans = NULL;
+	struct rtnl_link_vf *vf_data, *vf_head = NULL;
+
+	len = nla_len(tb[IFLA_VFINFO_LIST]);
+	link->l_vf_list = rtnl_link_vf_alloc();
+	if (!link->l_vf_list)
+		return -NLE_NOMEM;
+	vf_head = link->l_vf_list;
+
+	for (nla = nla_data(tb[IFLA_VFINFO_LIST]); nla_ok(nla, len);
+	     nla = nla_next(nla, &len)) {
+		err = nla_parse(t, IFLA_VF_MAX, nla_data(nla), nla_len(nla),
+				sriov_info_policy);
+		if (err < 0)
+			return err;
+
+		vf_data = rtnl_link_vf_alloc();
+		if (!vf_data)
+			return -NLE_NOMEM;
+
+		if (t[IFLA_VF_MAC]) {
+			vf_lladdr = nla_data(t[IFLA_VF_MAC]);
+
+			vf_data->vf_index = vf_lladdr->vf;
+			vf_data->ce_mask |= SRIOV_ATTR_INDEX;
+
+			vf_data->vf_lladdr = nl_addr_build(AF_LLC,
+							   vf_lladdr->mac, 6);
+			if (vf_data->vf_lladdr == NULL) {
+				rtnl_link_vf_put(vf_data);
+				return -NLE_NOMEM;
+			}
+			nl_addr_set_family(vf_data->vf_lladdr, AF_LLC);
+			vf_data->ce_mask |= SRIOV_ATTR_ADDR;
+		}
+
+		if (t[IFLA_VF_VLAN_LIST]) {
+			list_len = 0;
+			nla_for_each_nested(nla_list, t[IFLA_VF_VLAN_LIST],
+					    list_rem) {
+				if (list_len >= MAX_VLAN_LIST_LEN)
+					break;
+				vf_vlan_info[list_len] = nla_data(nla_list);
+				list_len++;
+			}
+
+			err = rtnl_link_vf_vlan_info(list_len, vf_vlan_info,
+						     &vf_vlans);
+			if (err < 0) {
+				rtnl_link_vf_put(vf_data);
+				return err;
+			}
+
+			vf_data->vf_vlans = vf_vlans;
+			vf_data->ce_mask |= SRIOV_ATTR_VLAN;
+		} else if (t[IFLA_VF_VLAN]) {
+			vf_vlan = nla_data(t[IFLA_VF_VLAN]);
+
+			if (vf_vlan->vlan) {
+				err = rtnl_link_vf_vlan_alloc(&vf_vlans, 1);
+				if (err < 0) {
+					rtnl_link_vf_put(vf_data);
+					return err;
+				}
+
+				vf_vlans->vlans[0].vf_vlan = vf_vlan->vlan;
+				vf_vlans->vlans[0].vf_vlan_qos = vf_vlan->qos;
+				vf_vlans->vlans[0].vf_vlan_proto = ETH_P_8021Q;
+
+				vf_data->vf_vlans = vf_vlans;
+				vf_data->ce_mask |= SRIOV_ATTR_VLAN;
+			}
+		}
+
+		if (t[IFLA_VF_TX_RATE]) {
+			vf_tx_rate = nla_data(t[IFLA_VF_TX_RATE]);
+
+			if (vf_tx_rate->rate) {
+				vf_data->vf_rate = vf_tx_rate->rate;
+				vf_data->ce_mask |= SRIOV_ATTR_TX_RATE;
+			}
+		}
+
+		if (t[IFLA_VF_SPOOFCHK]) {
+			vf_spoofchk = nla_data(t[IFLA_VF_SPOOFCHK]);
+
+			if (vf_spoofchk->setting != -1) {
+				vf_data->vf_spoofchk = vf_spoofchk->setting ? 1 : 0;
+				vf_data->ce_mask |= SRIOV_ATTR_SPOOFCHK;
+			}
+		}
+
+		if (t[IFLA_VF_LINK_STATE]) {
+			vf_linkstate = nla_data(t[IFLA_VF_LINK_STATE]);
+
+			vf_data->vf_linkstate = vf_linkstate->link_state;
+			vf_data->ce_mask |= SRIOV_ATTR_LINK_STATE;
+		}
+
+		if (t[IFLA_VF_RATE]) {
+			vf_rate = nla_data(t[IFLA_VF_RATE]);
+
+			if (vf_rate->max_tx_rate) {
+				vf_data->vf_max_tx_rate = vf_rate->max_tx_rate;
+				vf_data->ce_mask |= SRIOV_ATTR_RATE_MAX;
+			}
+			if (vf_rate->min_tx_rate) {
+				vf_data->vf_min_tx_rate = vf_rate->min_tx_rate;
+				vf_data->ce_mask |= SRIOV_ATTR_RATE_MIN;
+			}
+		}
+
+		if (t[IFLA_VF_RSS_QUERY_EN]) {
+			vf_rss_query = nla_data(t[IFLA_VF_RSS_QUERY_EN]);
+
+			if (vf_rss_query->setting != -1) {
+				vf_data->vf_rss_query_en = vf_rss_query->setting ? 1 : 0;
+				vf_data->ce_mask |= SRIOV_ATTR_RSS_QUERY_EN;
+			}
+		}
+
+		if (t[IFLA_VF_STATS]) {
+			err = nla_parse_nested(stb, IFLA_VF_STATS_MAX,
+					       t[IFLA_VF_STATS],
+					       sriov_stats_policy);
+			if (err < 0) {
+				rtnl_link_vf_put(vf_data);
+				return err;
+			}
+
+			SET_VF_STAT(link, cur, stb,
+				    RTNL_LINK_VF_STATS_RX_PACKETS,
+				    IFLA_VF_STATS_RX_PACKETS);
+			SET_VF_STAT(link, cur, stb,
+				    RTNL_LINK_VF_STATS_TX_PACKETS,
+				    IFLA_VF_STATS_TX_PACKETS);
+			SET_VF_STAT(link, cur, stb,
+				    RTNL_LINK_VF_STATS_RX_BYTES,
+				    IFLA_VF_STATS_RX_BYTES);
+			SET_VF_STAT(link, cur, stb,
+				    RTNL_LINK_VF_STATS_TX_BYTES,
+				    IFLA_VF_STATS_TX_BYTES);
+			SET_VF_STAT(link, cur, stb,
+				    RTNL_LINK_VF_STATS_BROADCAST,
+				    IFLA_VF_STATS_BROADCAST);
+			SET_VF_STAT(link, cur, stb,
+				    RTNL_LINK_VF_STATS_MULTICAST,
+				    IFLA_VF_STATS_MULTICAST);
+
+			vf_data->ce_mask |= IFLA_VF_STATS;
+		}
+
+		if (t[IFLA_VF_TRUST]) {
+			vf_trust = nla_data(t[IFLA_VF_TRUST]);
+
+			if (vf_trust->setting != -1) {
+				vf_data->vf_trust = vf_trust->setting ? 1 : 0;
+				vf_data->ce_mask |= SRIOV_ATTR_TRUST;
+			}
+		}
+
+		nl_list_add_head(&vf_data->vf_list, &vf_head->vf_list);
+		vf_head = vf_data;
+	}
+
+	return 0;
+}
+
+/**
+ * @name SR-IOV Sub-Object
+ * @{
+ */
+
+/**
+ * Add a SRIOV VF object to a link object
+ * @param link  	Link object to add to
+ * @param vf_data 	SRIOV VF object to add
+ *
+ * @return 0 if SRIOV VF object added successfully
+ * @return -NLE_OBJ_NOTFOUND if \p link or \p vf_data not provided
+ * @return -NLE_NOMEM if out of memory
+ */
+int rtnl_link_vf_add(struct rtnl_link *link, struct rtnl_link_vf *vf_data) {
+	struct rtnl_link_vf *vf_head = NULL;
+
+	if (!link||!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (!link->l_vf_list) {
+		link->l_vf_list = rtnl_link_vf_alloc();
+		if (!link->l_vf_list)
+			return -NLE_NOMEM;
+	}
+
+	vf_head = vf_data;
+	vf_head->ce_refcnt++;
+
+	vf_head = link->l_vf_list;
+	nl_list_add_head(&vf_data->vf_list, &vf_head->vf_list);
+	link->l_vf_list = vf_head;
+
+	rtnl_link_set_vf_list(link);
+
+	return 0;
+}
+
+/**
+ * Allocate a new SRIOV VF object
+ *
+ * @return NULL if out of memory
+ * @return New VF Object
+ *
+ * @see rtnl_link_vf_put()
+ *
+ * The SRIOV VF object must be returned to the link object with
+ * rtnl_link_vf_put() when operations are done to prevent memory leaks.
+ */
+struct rtnl_link_vf *rtnl_link_vf_alloc(void) {
+	struct rtnl_link_vf *vf;
+
+	if (!(vf = calloc(1, sizeof(*vf))))
+		return NULL;
+
+	NL_INIT_LIST_HEAD(&vf->vf_list);
+	vf->ce_refcnt = 1;
+
+	NL_DBG(4, "Allocated new SRIOV VF object %p\n", vf);
+
+	return vf;
+}
+
+/**
+ * Free SRIOV VF object.
+ * @arg vf_data 	SRIOV VF data object
+ */
+void rtnl_link_vf_free(struct rtnl_link_vf *vf_data) {
+	if (!vf_data)
+		return;
+
+	if (vf_data->ce_refcnt > 0)
+		NL_DBG(1, "Warning: Freeing SRIOV VF object in use...\n");
+
+	if (vf_data->ce_mask & SRIOV_ATTR_ADDR)
+		nl_addr_put(vf_data->vf_lladdr);
+	if (vf_data->ce_mask & SRIOV_ATTR_VLAN)
+		rtnl_link_vf_vlan_put(vf_data->vf_vlans);
+
+	NL_DBG(4, "Freed SRIOV VF object %p\n", vf_data);
+	free(vf_data);
+
+	return;
+}
+
+/**
+ * Lookup SRIOV VF in link object by VF index.
+ *
+ * @return NULL if VF not found
+ * @return VF Object
+ *
+ * @see rtnl_link_vf_put()
+ *
+ * The SRIOV VF object must be returned to the link object with
+ * rtnl_link_vf_put() when operations are done to prevent memory leaks.
+ */
+struct rtnl_link_vf *rtnl_link_vf_get(struct rtnl_link *link, uint32_t vf_num) {
+	struct rtnl_link_vf *list, *vf, *next, *ret = NULL;
+
+	list = link->l_vf_list;
+	nl_list_for_each_entry_safe(vf, next, &list->vf_list, vf_list) {
+		if (vf->vf_index == vf_num) {
+			ret = vf;
+			break;
+		}
+	}
+
+	if (ret) {
+		ret->ce_refcnt++;
+		NL_DBG(4, "New reference to SRIOV VF object %p, total %i\n",
+		       ret, ret->ce_refcnt);
+	}
+
+	return ret;
+}
+
+/**
+ * Return SRIOV VF object to the owning link object.
+ * @arg vf_data 	SRIOV VF data object
+ *
+ * @see rtnl_link_vf_alloc()
+ * @see rtnl_link_vf_get()
+ */
+void rtnl_link_vf_put(struct rtnl_link_vf *vf_data) {
+	if (!vf_data)
+		return;
+
+	vf_data->ce_refcnt--;
+	NL_DBG(4, "Returned SRIOV VF object reference %p, %i remaining\n",
+	       vf_data, vf_data->ce_refcnt);
+
+	if (vf_data->ce_refcnt < 0)
+		BUG();
+
+	if (vf_data->ce_refcnt <= 0)
+		rtnl_link_vf_free(vf_data);
+
+	return;
+}
+
+/**
+ * Get link layer address of SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg addr 		Pointer to store Link Layer address
+ *
+ * @see rtnl_link_get_num_vf()
+ * @see rtnl_link_vf_set_addr()
+ *
+ * @copydoc pointer_lifetime_warning
+ * @return 0 if addr is present and addr is set to pointer containing address
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the link layer address is not set
+ */
+int rtnl_link_vf_get_addr(struct rtnl_link_vf *vf_data, struct nl_addr **addr)
+{
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_ADDR)
+		*addr = vf_data->vf_lladdr;
+	else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Set link layer address of SRIOV Virtual Function object
+ * @param vf_data 	SRIOV VF object
+ * @param addr 		New link layer address
+ *
+ * This function increments the reference counter of the address object
+ * and overwrites any existing link layer address previously assigned.
+ *
+ * @see rtnl_link_vf_get_addr()
+ */
+void rtnl_link_vf_set_addr(struct rtnl_link_vf *vf_data, struct nl_addr *addr) {
+	if (vf_data->vf_lladdr)
+		nl_addr_put(vf_data->vf_lladdr);
+
+	nl_addr_get(addr);
+	vf_data->vf_lladdr = addr;
+	vf_data->ce_mask |= SRIOV_ATTR_ADDR;
+
+	return;
+}
+
+/**
+ * Set the Infiniband node GUID for the SRIOV Virtual Function object
+ * @param vf_data 	SRIOV VF object
+ * @param guid  	node GUID
+ */
+void rtnl_link_vf_set_ib_node_guid(struct rtnl_link_vf *vf_data,
+				   uint64_t guid) {
+	vf_data->vf_guid_node = guid;
+	vf_data->ce_mask |= SRIOV_ATTR_IB_NODE_GUID;
+
+	return;
+}
+
+/**
+ * Set the Infiniband port GUID for the SRIOV Virtual Function object
+ * @param vf_data 	SRIOV VF object
+ * @param guid  	port GUID
+ */
+void rtnl_link_vf_set_ib_port_guid(struct rtnl_link_vf *vf_data,
+				   uint64_t guid) {
+	vf_data->vf_guid_port = guid;
+	vf_data->ce_mask |= SRIOV_ATTR_IB_PORT_GUID;
+
+	return;
+}
+
+/**
+ * Get index of SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg vf_index 	Pointer to store VF index
+ *
+ * @see rtnl_link_get_num_vf()
+ *
+ * @return 0 if index is present and vf_index is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF index is not set
+ */
+int rtnl_link_vf_get_index(struct rtnl_link_vf *vf_data, uint32_t *vf_index)
+{
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_INDEX)
+		*vf_index = vf_data->vf_index;
+	else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Set index of SRIOV Virtual Function object
+ * @param vf_data 	SRIOV VF object
+ * @param vf_index 	Index value
+ *
+ * @see rtnl_link_vf_get_index()
+ */
+void rtnl_link_vf_set_index(struct rtnl_link_vf *vf_data, uint32_t vf_index)
+{
+	vf_data->vf_index = vf_index;
+	vf_data->ce_mask |= SRIOV_ATTR_INDEX;
+
+	return;
+}
+
+/**
+ * Get link state of SRIOV Virtual Function
+ * @arg vf_data  	SRIOV VF object
+ * @arg vf_linkstate 	Pointer to store VF link state
+ *
+ * @see rtnl_link_get_num_vf()
+ * @see rtnl_link_set_linkstate()
+ *
+ * @return 0 if link state is present and vf_linkstate is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF link state is not set
+ */
+int rtnl_link_vf_get_linkstate(struct rtnl_link_vf *vf_data,
+			       uint32_t *vf_linkstate)
+{
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_LINK_STATE)
+		*vf_linkstate = vf_data->vf_linkstate;
+	else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Set link state of SRIOV Virtual Function object
+ * @param vf_data 	SRIOV VF object
+ * @param vf_linkstate Link state value
+ *
+ * @see rtnl_link_get_linkstate()
+ *
+ * Not all hardware supports setting link state. If the feature is unsupported,
+ * the link change request will fail with -NLE_OPNOTSUPP
+ */
+void rtnl_link_vf_set_linkstate(struct rtnl_link_vf *vf_data,
+				uint32_t vf_linkstate) {
+	vf_data->vf_linkstate = vf_linkstate;
+	vf_data->ce_mask |= SRIOV_ATTR_LINK_STATE;
+
+	return;
+}
+
+/**
+ * Get TX Rate Limit of SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg vf_rate 	Pointer to store VF rate limiting data
+ *
+ * @see rtnl_link_get_num_vf()
+ * @see rtnl_link_set_rate()
+ *
+ * When the older rate API has been implemented, the rate member of the struct
+ * will be set, and the api member will be set to RTNL_LINK_VF_API_OLD.
+ * When the newer rate API has been implemented, the max_tx_rate
+ * and/or the minx_tx_rate will be set, and the api member will be set to
+ * RTNL_LINK_VF_API_NEW.
+ *
+ * Old rate API supports only a maximum TX rate.
+ *   ip link set dev vf 0 rate
+ * New rate API supports minumum and maximum TX rates.
+ *   ip link set dev vf 0 min_tx_rate
+ *   ip link set dev vf 0 max_tx_rate
+ *
+ * @return 0 if rate is present and vf_rate is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF rate is not set
+ */
+int rtnl_link_vf_get_rate(struct rtnl_link_vf *vf_data,
+			  struct nl_vf_rate *vf_rate)
+{
+	int set = 0;
+
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	vf_rate->api = RTNL_LINK_VF_RATE_API_UNSPEC;
+	vf_rate->rate = 0;
+	vf_rate->max_tx_rate = 0;
+	vf_rate->min_tx_rate = 0;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_RATE_MAX) {
+		if (vf_data->vf_max_tx_rate) {
+			vf_rate->api = RTNL_LINK_VF_RATE_API_NEW;
+			vf_rate->max_tx_rate = vf_data->vf_max_tx_rate;
+			set = 1;
+		}
+	}
+	if (vf_data->ce_mask & SRIOV_ATTR_RATE_MIN) {
+		if (vf_data->vf_min_tx_rate) {
+			vf_rate->api = RTNL_LINK_VF_RATE_API_NEW;
+			vf_rate->min_tx_rate = vf_data->vf_min_tx_rate;
+			set = 1;
+		}
+	}
+	if ((!set) && (vf_data->ce_mask & SRIOV_ATTR_TX_RATE)) {
+		if (vf_data->vf_rate) {
+			vf_rate->api = RTNL_LINK_VF_RATE_API_OLD;
+			vf_rate->rate = vf_data->vf_rate;
+			set = 1;
+		}
+	}
+
+	if (!set)
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Set TX Rate Limit of SRIOV Virtual Function object
+ * @param vf_data 	SRIOV VF object
+ * @param vf_rate 	Rate limiting structure
+ *
+ * @see rtnl_link_vf_get_rate()
+ *
+ * When setting the rate, the API level must be specificed.
+ * Valid API levels:
+ *   RTNL_LINK_VF_RATE_API_NEW
+ *   RTNL_LINK_VF_RATE_API_OLD
+ *
+ * When using the new API, if either the min_tx_rate or
+ * max_tx_rate has been set, and the other is being changed,
+ * you must specify the currently set values to preserve
+ * them. If this is not done, that setting will be disabled.
+ *
+ * Old rate API supports only a maximum TX rate.
+ *   ip link set dev vf 0 rate
+ * New rate API supports minumum and maximum TX rates.
+ *   ip link set dev vf 0 min_tx_rate
+ *   ip link set dev vf 0 max_tx_rate
+ *
+ * Not all hardware supports min_tx_rate.
+ */
+void rtnl_link_vf_set_rate(struct rtnl_link_vf *vf_data,
+			   struct nl_vf_rate *vf_rate) {
+	if (vf_rate->api == RTNL_LINK_VF_RATE_API_OLD) {
+		vf_data->vf_rate = vf_rate->rate;
+		vf_data->ce_mask |= SRIOV_ATTR_TX_RATE;
+	} else if (vf_rate->api == RTNL_LINK_VF_RATE_API_NEW) {
+		vf_data->vf_max_tx_rate = vf_rate->max_tx_rate;
+		vf_data->ce_mask |= SRIOV_ATTR_RATE_MAX;
+
+		vf_data->vf_min_tx_rate = vf_rate->min_tx_rate;
+		vf_data->ce_mask |= SRIOV_ATTR_RATE_MIN;
+	}
+
+	return;
+}
+
+/**
+ * Get RSS Query EN value of SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg vf_rss_query_en	Pointer to store VF RSS Query value
+ *
+ * @see rtnl_link_get_num_vf()
+ * @see rtnl_link_vf_set_rss_query_en()
+ *
+ * @return 0 if rss_query_en is present and vf_rss_query_en is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF RSS Query EN value is not set
+ */
+int rtnl_link_vf_get_rss_query_en(struct rtnl_link_vf *vf_data,
+				  uint32_t *vf_rss_query_en)
+{
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_RSS_QUERY_EN)
+		*vf_rss_query_en = vf_data->vf_rss_query_en;
+	else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Set RSS configuration querying of SRIOV Virtual Function Object
+ * @arg vf_data 	SRIOV VF object
+ * @arg vf_rss_query_en	RSS Query value
+ *
+ * @see rtnl_link_vf_get_rss_query_en()
+ */
+void rtnl_link_vf_set_rss_query_en(struct rtnl_link_vf *vf_data,
+				  uint32_t vf_rss_query_en) {
+	vf_data->vf_rss_query_en = vf_rss_query_en;
+	vf_data->ce_mask |= SRIOV_ATTR_RSS_QUERY_EN;
+
+	return;
+}
+
+/**
+ * Get spoof checking value of SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg vf_spoofchk 	Pointer to store VF spoofchk value
+ *
+ * @see rtnl_link_get_num_vf()
+ * @see rtnl_link_set_spoofchk()
+ *
+ * @return 0 if spoofchk is present and vf_spoofchk is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF spoofcheck is not set
+ */
+int rtnl_link_vf_get_spoofchk(struct rtnl_link_vf *vf_data,
+			      uint32_t *vf_spoofchk)
+{
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_SPOOFCHK)
+		*vf_spoofchk = vf_data->vf_spoofchk;
+	else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Set spoof checking value of SRIOV Virtual Function Object
+ * @param vf_data
+ * @param vf_spoofchk
+ *
+ * @see rtnl_link_vf_get_spoofchk()
+ */
+void rtnl_link_vf_set_spoofchk(struct rtnl_link_vf *vf_data,
+			       uint32_t vf_spoofchk) {
+	vf_data->vf_spoofchk = vf_spoofchk;
+	vf_data->ce_mask |= SRIOV_ATTR_SPOOFCHK;
+
+	return;
+}
+
+/**
+ * Get value of stat counter for SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg stat 		Identifier of statistical counter
+ * @arg vf_stat 	Pointer to store VF stat value in
+ *
+ * @see rtnl_link_get_num_vf()
+ *
+ * @return 0 if stat is present and vf_stat is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF stat is not set
+ */
+int rtnl_link_vf_get_stat(struct rtnl_link_vf *vf_data,
+			  rtnl_link_vf_stats_t stat, uint64_t *vf_stat)
+{
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_STATS)
+		*vf_stat = vf_data->vf_stats[stat];
+	else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Get trust setting of SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg vf_trust 	Pointer to store VF trust value
+ *
+ * @see rtnl_link_get_num_vf()
+ * @see rtnl_link_set_trust()
+ *
+ * @return 0 if trust is present and vf_trust is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF trust setting is not set
+ */
+int rtnl_link_vf_get_trust(struct rtnl_link_vf *vf_data, uint32_t *vf_trust)
+{
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_TRUST)
+		*vf_trust = vf_data->vf_trust;
+	else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Set user trust setting on SRIOV Virtual Function Object
+ * @param vf_data
+ * @param vf_trust
+ *
+ * @see rtnl_link_vf_get_trust()
+ */
+void rtnl_link_vf_set_trust(struct rtnl_link_vf *vf_data, uint32_t vf_trust) {
+	vf_data->vf_trust = vf_trust;
+	vf_data->ce_mask |= SRIOV_ATTR_TRUST;
+
+	return;
+}
+
+/**
+ * Get an array of VLANS on SRIOV Virtual Function
+ * @arg vf_data 	SRIOV VF object
+ * @arg vf_vlans 	Pointer to nl_vf_vlans_t struct to store vlan info.
+ *
+ * @see rtnl_link_get_num_vf()
+ *
+ * The SRIOV VF VLANs object must be returned to the SRIOV VF object with
+ * rtnl_link_vf_vlans_put() when operations are done to prevent memory leaks.
+ *
+ * @copydoc pointer_lifetime_warning
+ * @return 0 if VLAN info is present and vf_vlans is set
+ * @return -NLE_OBJ_NOTFOUND if information for VF info is not found
+ * @return -NLE_NOATTR if the VF vlans is not set
+ */
+int rtnl_link_vf_get_vlans(struct rtnl_link_vf *vf_data,
+			   nl_vf_vlans_t **vf_vlans) {
+	nl_vf_vlans_t *vf;
+
+	if (!vf_data)
+		return -NLE_OBJ_NOTFOUND;
+
+	if (vf_data->ce_mask & SRIOV_ATTR_VLAN) {
+		vf = vf_data->vf_vlans;
+		vf->ce_refcnt++;
+		*vf_vlans = vf;
+	} else
+		return -NLE_NOATTR;
+
+	return 0;
+}
+
+/**
+ * Add a SRIOV VF VLANs object to the SRIOV Virtual Function Object
+ * @param vf_data 	SRIOV VF object
+ * @param vf_vlans 	SRIOV VF VLANs object
+ *
+ * @see rtnl_link_vf_get_vlans()
+ * @see rtnl_link_vf_vlan_alloc()
+ *
+ * This function assigns ownership of the SRIOV VF object \p vf_vlans
+ * to the SRIOV Virtual Function object \p vf_data. Do not use
+ * rtnl_link_vf_vlan_put() on \p vf_vlans after this.
+ */
+void rtnl_link_vf_set_vlans(struct rtnl_link_vf *vf_data,
+			    nl_vf_vlans_t *vf_vlans) {
+	if (!vf_data||!vf_vlans)
+		return;
+
+	vf_data->vf_vlans = vf_vlans;
+	vf_data->vf_vlans->ce_refcnt++;
+	vf_data->ce_mask |= SRIOV_ATTR_VLAN;
+
+	return;
+}
+
+/**
+ * Allocate a SRIOV VF VLAN object
+ * @param vf_vlans 	Pointer to store VLAN object at
+ * @param vlan_count 	Number of VLANs that will be stored in VLAN object
+ *
+ * The SRIOV VF VLANs object must be returned to the sRIOV VF object with
+ * rtnl_link_vf_vlan_put() when operations are done to prevent memory leaks.
+ *
+ * @return 0 if VLAN object is created and vf_vlans is set.
+ * @return -NLE_NOMEM if object could not be allocated.
+ * @return -NLE_INVAL if vlan_count is more than supported by SRIOV VF
+ */
+int rtnl_link_vf_vlan_alloc(nl_vf_vlans_t **vf_vlans, int vlan_count) {
+	nl_vf_vlans_t *vlans;
+	nl_vf_vlan_info_t *vlan_info;
+
+	if (vlan_count > MAX_VLAN_LIST_LEN)
+		return -NLE_INVAL;
+
+	vlans = calloc(1, sizeof(*vlans));
+	if (!vlans)
+		return -NLE_NOMEM;
+
+	vlan_info = calloc(vlan_count+1, sizeof(*vlan_info));
+	if (!vlan_info) {
+		free(vlans);
+		return -NLE_NOMEM;
+	}
+
+	NL_DBG(4, "Allocated new SRIOV VF VLANs object %p\n", vlans);
+
+	vlans->ce_refcnt = 1;
+	vlans->size = vlan_count;
+	vlans->vlans = vlan_info;
+	*vf_vlans = vlans;
+
+	return 0;
+}
+
+/**
+ * Free an allocated SRIOV VF VLANs object
+ * @param vf_vlans 	SRIOV VF VLANs object
+ */
+void rtnl_link_vf_vlan_free(nl_vf_vlans_t *vf_vlans) {
+	if (!vf_vlans)
+		return;
+
+	if (vf_vlans->ce_refcnt > 0)
+		NL_DBG(1, "Warning: Freeing SRIOV VF VLANs object in use...\n");
+
+	NL_DBG(4, "Freed SRIOV VF object %p\n", vf_vlans);
+	free(vf_vlans->vlans);
+	free(vf_vlans);
+
+	return;
+}
+
+/**
+ * Return SRIOV VF VLANs object to the owning SRIOV VF object.
+ * @param vf_vlans 	SRIOV VF VLANs object
+ */
+void rtnl_link_vf_vlan_put(nl_vf_vlans_t *vf_vlans) {
+	if (!vf_vlans)
+		return;
+
+	vf_vlans->ce_refcnt--;
+	NL_DBG(4, "Returned SRIOV VF VLANs object reference %p, %i remaining\n",
+	       vf_vlans, vf_vlans->ce_refcnt);
+
+	if (vf_vlans->ce_refcnt < 0)
+		BUG();
+
+	if (vf_vlans->ce_refcnt <= 0)
+		rtnl_link_vf_vlan_free(vf_vlans);
+
+	return;
+}
+
+/** @} */
+
+/**
+ * @name Utilities
+ * @{
+ */
+
+static const struct trans_tbl vf_link_states[] = {
+	__ADD(IFLA_VF_LINK_STATE_AUTO, autodetect),
+	__ADD(IFLA_VF_LINK_STATE_ENABLE, up),
+	__ADD(IFLA_VF_LINK_STATE_DISABLE, down),
+};
+
+char *rtnl_link_vf_linkstate2str(uint32_t ls, char *buf, size_t len)
+{
+	return __type2str(ls, buf, len, vf_link_states,
+			  ARRAY_SIZE(vf_link_states));
+}
+
+int rtnl_link_vf_str2linkstate(const char *name)
+{
+	return __str2type(name, vf_link_states, ARRAY_SIZE(vf_link_states));
+}
+
+static const struct trans_tbl vf_vlan_proto[] = {
+	__ADD(ETH_P_8021Q, 8021Q),
+	__ADD(ETH_P_8021AD, 8021AD),
+};
+
+char *rtnl_link_vf_vlanproto2str(uint16_t proto, char *buf, size_t len)
+{
+	return __type2str(proto, buf, len, vf_vlan_proto,
+			  ARRAY_SIZE(vf_vlan_proto));
+}
+
+int rtnl_link_vf_str2vlanproto(const char *name)
+{
+	return __str2type(name, vf_vlan_proto, ARRAY_SIZE(vf_vlan_proto));
+}
+
+/* Return a guid from a format checked string.
+ * Format string must be xx:xx:xx:xx:xx:xx:xx:xx where XX can be an
+ * arbitrary hex digit
+ *
+ * Function modified from original at iproute2/lib/utils.c:get_guid()
+ * Original by Eli Cohen <eli@mellanox.com>.
+ * iproute2 git commit d91fb3f4c7e4dba806541bdc90b1fb60a3581541
+ */
+int rtnl_link_vf_str2guid(uint64_t *guid, const char *guid_s) {
+	unsigned long int tmp;
+	char *endptr;
+	int i;
+
+	if (strlen(guid_s) != RTNL_VF_GUID_STR_LEN)
+		return -1;
+
+	for (i = 0; i < 7; i++) {
+		if (guid_s[2 + i * 3] != ':')
+			return -1;
+	}
+
+	*guid = 0;
+	for (i = 0; i < 8; i++) {
+		tmp = strtoul(guid_s + i * 3, &endptr, 16);
+		if (endptr != guid_s + i * 3 + 2)
+			return -1;
+
+		if (tmp > 255)
+			return -1;
+
+		*guid |= tmp << (56 - 8 * i);
+	}
+
+	return 0;
+}
+
+/** @} */
+
+/** @} */
diff --git a/lib/route/link/veth.c b/lib/route/link/veth.c
index e7e4a26..15859de 100644
--- a/lib/route/link/veth.c
+++ b/lib/route/link/veth.c
@@ -32,6 +32,7 @@
 #include <netlink/route/link/veth.h>
 
 #include <linux/if_link.h>
+#include <linux/veth.h>
 
 static struct nla_policy veth_policy[VETH_INFO_MAX+1] = {
 	[VETH_INFO_PEER]	= { .minlen = sizeof(struct ifinfomsg) },
@@ -45,7 +46,7 @@
 	struct rtnl_link *peer = link->l_info;
 	int err;
 
-	NL_DBG(3, "Parsing veth link info");
+	NL_DBG(3, "Parsing veth link info\n");
 
 	if ((err = nla_parse_nested(tb, VETH_INFO_MAX, data, veth_policy)) < 0)
 		goto errout;
@@ -62,8 +63,8 @@
 		peer->l_index = ifi->ifi_index;
 		peer->l_flags = ifi->ifi_flags;
 		peer->l_change = ifi->ifi_change;
-		err = nla_parse(peer_tb, IFLA_MAX,
-				nla_data(nla_peer) + sizeof(struct ifinfomsg),
+		err = nla_parse(peer_tb, IFLA_MAX, (struct nlattr *)
+		                ((char *) nla_data(nla_peer) + sizeof(struct ifinfomsg)),
 				nla_len(nla_peer) - sizeof(struct ifinfomsg),
 				rtln_link_policy);
 		if (err < 0)
@@ -281,10 +282,10 @@
 		return -NLE_NOMEM;
 	peer = link->l_info;
 
-	if (name && peer_name) {
+	if (name)
 		rtnl_link_set_name(link, name);
+	if (peer_name)
 		rtnl_link_set_name(peer, peer_name);
-	}
 
 	rtnl_link_set_ns_pid(peer, pid);
 	err = rtnl_link_add(sock, link, NLM_F_CREATE | NLM_F_EXCL);
diff --git a/lib/route/link/vlan.c b/lib/route/link/vlan.c
index b9f0c66..7c5aa06 100644
--- a/lib/route/link/vlan.c
+++ b/lib/route/link/vlan.c
@@ -44,6 +44,7 @@
 {
 	uint16_t		vi_vlan_id;
 	uint16_t		vi_protocol;
+	unsigned int            vi_ingress_qos_mask:(VLAN_PRIO_MAX+1);
 	uint32_t		vi_flags;
 	uint32_t		vi_flags_mask;
 	uint32_t		vi_ingress_qos[VLAN_PRIO_MAX+1];
@@ -67,10 +68,16 @@
 {
 	struct vlan_info *vi;
 
-	if ((vi = calloc(1, sizeof(*vi))) == NULL)
-		return -NLE_NOMEM;
+	if (link->l_info) {
+		vi = link->l_info;
+		free(vi->vi_egress_qos);
+		memset(link->l_info, 0, sizeof(*vi));
+	} else {
+		if ((vi = calloc(1, sizeof(*vi))) == NULL)
+			return -NLE_NOMEM;
 
-	link->l_info = vi;
+		link->l_info = vi;
+	}
 
 	return 0;
 }
@@ -82,7 +89,7 @@
 	struct vlan_info *vi;
 	int err;
 
-	NL_DBG(3, "Parsing VLAN link info");
+	NL_DBG(3, "Parsing VLAN link info\n");
 
 	if ((err = nla_parse_nested(tb, IFLA_VLAN_MAX, data, vlan_policy)) < 0)
 		goto errout;
@@ -115,6 +122,7 @@
 		struct nlattr *nla;
 		int remaining;
 
+		vi->vi_ingress_qos_mask = 0;
 		memset(vi->vi_ingress_qos, 0, sizeof(vi->vi_ingress_qos));
 
 		nla_for_each_nested(nla, tb[IFLA_VLAN_INGRESS_QOS], remaining) {
@@ -126,6 +134,17 @@
 				return -NLE_INVAL;
 			}
 
+			/* Kernel will not explicitly serialize mappings with "to" zero
+			 * (although they are implicitly set).
+			 *
+			 * Thus we only mark those as "set" which are explicitly sent.
+			 * That is similar to what we do with the egress map and it preserves
+			 * previous behavior before NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR.
+			 *
+			 * It matters only when a received object is send back to kernel to modify
+			 * the link.
+			 */
+			vi->vi_ingress_qos_mask |= (1 << map->from);
 			vi->vi_ingress_qos[map->from] = map->to;
 		}
 
@@ -197,7 +216,7 @@
 	nl_dump_line(p, "    vlan-info id %d <%s>", vi->vi_vlan_id, buf);
 
 	if (vi->vi_mask & VLAN_HAS_PROTOCOL)
-		nl_dump_line(p, "    vlan protocol <%d>", vi->vi_protocol);
+		nl_dump_line(p, "    vlan protocol <%d>", ntohs(vi->vi_protocol));
 
 	nl_dump(p, "\n");
 
@@ -205,7 +224,7 @@
 		nl_dump_line(p, 
 		"      ingress vlan prio -> qos/socket prio mapping:\n");
 		for (i = 0, printed = 0; i <= VLAN_PRIO_MAX; i++) {
-			if (vi->vi_ingress_qos[i]) {
+			if (vi->vi_ingress_qos_mask & (1 << i)) {
 				if (printed == 0)
 					nl_dump_line(p, "      ");
 				nl_dump(p, "%x -> %#08x, ",
@@ -245,19 +264,28 @@
 {
 	struct vlan_info *vdst, *vsrc = src->l_info;
 	int err;
+	struct vlan_map *p = NULL;
 
 	dst->l_info = NULL;
 	if ((err = rtnl_link_set_type(dst, "vlan")) < 0)
 		return err;
 	vdst = dst->l_info;
 
-	vdst->vi_egress_qos = calloc(vsrc->vi_egress_size,
-				     sizeof(struct vlan_map));
-	if (!vdst->vi_egress_qos)
-		return -NLE_NOMEM;
+	if (vsrc->vi_negress) {
+		p = calloc(vsrc->vi_negress,
+		           sizeof(struct vlan_map));
+		if (!p)
+			return -NLE_NOMEM;
+	}
 
-	memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos,
-	       vsrc->vi_egress_size * sizeof(struct vlan_map));
+	*vdst = *vsrc;
+
+	if (vsrc->vi_negress) {
+		vdst->vi_egress_size = vsrc->vi_negress;
+		vdst->vi_egress_qos = p;
+		memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos,
+		       vsrc->vi_negress * sizeof(struct vlan_map));
+	}
 
 	return 0;
 }
@@ -273,6 +301,9 @@
 	if (vi->vi_mask & VLAN_HAS_ID)
 		NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id);
 
+	if (vi->vi_mask & VLAN_HAS_PROTOCOL)
+		NLA_PUT_U16(msg, IFLA_VLAN_PROTOCOL, vi->vi_protocol);
+
 	if (vi->vi_mask & VLAN_HAS_FLAGS) {
 		struct ifla_vlan_flags flags = {
 			.flags = vi->vi_flags,
@@ -291,7 +322,7 @@
 			goto nla_put_failure;
 
 		for (i = 0; i <= VLAN_PRIO_MAX; i++) {
-			if (vi->vi_ingress_qos[i]) {
+			if (vi->vi_ingress_qos_mask & (1 << i)) {
 				map.from = i;
 				map.to = vi->vi_ingress_qos[i];
 
@@ -425,7 +456,8 @@
 /**
  * Set VLAN protocol
  * @arg link		Link object
- * @arg protocol	VLAN protocol
+ * @arg protocol	VLAN protocol in network byte order.
+ *   Probably you want to set it to something like htons(ETH_P_8021Q).
  *
  * @return 0 on success or a negative error code
  */
@@ -445,7 +477,8 @@
  * Get VLAN protocol
  * @arg link		Link object
  *
- * @return VLAN protocol, 0 if not set or a negative error code.
+ * @return VLAN protocol in network byte order like htons(ETH_P_8021Q),
+ *   0 if not set or a negative error code.
  */
 int rtnl_link_vlan_get_protocol(struct rtnl_link *link)
 {
@@ -531,6 +564,7 @@
 	if (from < 0 || from > VLAN_PRIO_MAX)
 		return -NLE_INVAL;
 
+	vi->vi_ingress_qos_mask |= (1 << from);
 	vi->vi_ingress_qos[from] = to;
 	vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
 
@@ -561,10 +595,16 @@
 		return -NLE_INVAL;
 
 	if (vi->vi_negress >= vi->vi_egress_size) {
-		int new_size = vi->vi_egress_size + 32;
+		uint32_t new_size = vi->vi_egress_size + 1 + vi->vi_egress_size / 2;
+		size_t bytes;
 		void *ptr;
 
-		ptr = realloc(vi->vi_egress_qos, new_size);
+		if (new_size < vi->vi_egress_size)
+			return -NLE_NOMEM;
+		bytes = (size_t) new_size * sizeof(struct vlan_map);
+		if (bytes / sizeof (struct vlan_map) != new_size)
+			return -NLE_NOMEM;
+		ptr = realloc(vi->vi_egress_qos, bytes);
 		if (!ptr)
 			return -NLE_NOMEM;
 
@@ -603,7 +643,10 @@
 /** @} */
 
 static const struct trans_tbl vlan_flags[] = {
-	__ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr)
+	__ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr),
+	__ADD(VLAN_FLAG_GVRP, gvrp),
+	__ADD(VLAN_FLAG_LOOSE_BINDING, loose_binding),
+	__ADD(VLAN_FLAG_MVRP, mvrp),
 };
 
 /**
diff --git a/lib/route/link/vrf.c b/lib/route/link/vrf.c
new file mode 100644
index 0000000..8b6b451
--- /dev/null
+++ b/lib/route/link/vrf.c
@@ -0,0 +1,264 @@
+/*
+ * lib/route/link/vrf.c      VRF Link Info
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2015 Cumulus Networks. All rights reserved.
+ * Copyright (c) 2015 David Ahern <dsa@cumulusnetworks.com>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup vrf VRF
+ * Virtual Routing and Forwarding link module
+ *
+ * @details
+ * \b Link Type Name: "vrf"
+ *
+ * @route_doc{link_vrf, VRF Documentation}
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+#include <netlink/route/link/vrf.h>
+
+#include <linux/if_link.h>
+#include <linux-private/linux/rtnetlink.h>
+
+#define VRF_TABLE_ID_MAX  RT_TABLE_MAX
+
+/** @cond SKIP */
+#define VRF_HAS_TABLE_ID    (1<<0)
+
+struct vrf_info {
+	uint32_t        table_id;
+	uint32_t        vi_mask;
+};
+
+/** @endcond */
+
+static struct nla_policy vrf_policy[IFLA_VRF_MAX + 1] = {
+	[IFLA_VRF_TABLE]         = { .type = NLA_U32 },
+};
+
+static int vrf_alloc(struct rtnl_link *link)
+{
+	struct vrf_info *vi;
+
+	if (link->l_info) {
+		memset(link->l_info, 0, sizeof (*vi));
+		return 0;
+	}
+
+	if ((vi = calloc(1, sizeof(*vi))) == NULL)
+		return -NLE_NOMEM;
+
+	link->l_info = vi;
+
+	return 0;
+}
+
+static int vrf_parse(struct rtnl_link *link, struct nlattr *data,
+		     struct nlattr *xstats)
+{
+	struct nlattr *tb[IFLA_VRF_MAX+1];
+	struct vrf_info *vi;
+	int err;
+
+	NL_DBG(3, "Parsing VRF link info");
+
+	if ((err = nla_parse_nested(tb, IFLA_VRF_MAX, data, vrf_policy)) < 0)
+		goto errout;
+
+	if ((err = vrf_alloc(link)) < 0)
+		goto errout;
+
+	vi = link->l_info;
+
+	if (tb[IFLA_VRF_TABLE]) {
+		vi->table_id = nla_get_u32(tb[IFLA_VRF_TABLE]);
+		vi->vi_mask |= VRF_HAS_TABLE_ID;
+	}
+
+	err = 0;
+
+errout:
+	return err;
+}
+
+static void vrf_free(struct rtnl_link *link)
+{
+	free(link->l_info);
+	link->l_info = NULL;
+}
+
+static int vrf_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+	struct vrf_info *vdst, *vsrc = src->l_info;
+	int err;
+
+	dst->l_info = NULL;
+	if ((err = rtnl_link_set_type(dst, "vrf")) < 0)
+		return err;
+	vdst = dst->l_info;
+
+	BUG_ON(!vdst || !vsrc);
+
+	memcpy(vdst, vsrc, sizeof(struct vrf_info));
+
+	return 0;
+}
+
+static int vrf_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+	struct vrf_info *vi = link->l_info;
+	struct nlattr *data;
+
+	if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
+		return -NLE_NOMEM;
+
+	if (vi->vi_mask & VRF_HAS_TABLE_ID) {
+		NLA_PUT_U32(msg, IFLA_VRF_TABLE, vi->table_id);
+	}
+
+	nla_nest_end(msg, data);
+
+nla_put_failure:
+
+	return 0;
+}
+
+static void vrf_dump(struct rtnl_link *link, struct nl_dump_params *p)
+{
+	struct vrf_info *vi = link->l_info;
+
+	if (vi->vi_mask & VRF_HAS_TABLE_ID) {
+		nl_dump(p, "table-id %u", vi->table_id);
+	}
+}
+
+static struct rtnl_link_info_ops vrf_info_ops = {
+	.io_name                = "vrf",
+	.io_alloc               = vrf_alloc,
+	.io_parse               = vrf_parse,
+	.io_dump = {
+		[NL_DUMP_LINE]     = vrf_dump,
+		[NL_DUMP_DETAILS]  = vrf_dump,
+	},
+	.io_clone               = vrf_clone,
+	.io_put_attrs           = vrf_put_attrs,
+	.io_free                = vrf_free,
+};
+
+/** @cond SKIP */
+#define IS_VRF_LINK_ASSERT(link) \
+	if ((link)->l_info_ops != &vrf_info_ops) { \
+		APPBUG("Link is not a VRF link. set type \"vrf\" first."); \
+		return -NLE_OPNOTSUPP; \
+	}
+/** @endcond */
+
+/**
+ * @name VRF Object
+ * @{
+ */
+
+/**
+ * Allocate link object of type VRF
+ *
+ * @return Allocated link object or NULL.
+ */
+struct rtnl_link *rtnl_link_vrf_alloc(void)
+{
+	struct rtnl_link *link;
+	int err;
+
+	if (!(link = rtnl_link_alloc()))
+		return NULL;
+
+	if ((err = rtnl_link_set_type(link, "vrf")) < 0) {
+		rtnl_link_put(link);
+		return NULL;
+	}
+
+	return link;
+}
+
+/**
+ * Check if link is a VRF link
+ * @arg link           Link object
+ *
+ * @return True if link is a VRF link, otherwise false is returned.
+ */
+int rtnl_link_is_vrf(struct rtnl_link *link)
+{
+	return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vrf");
+}
+
+/**
+ * Get VRF table id
+ * @arg link           Link object
+ * @arg id             Pointer to store table identifier
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vrf_get_tableid(struct rtnl_link *link, uint32_t *id)
+{
+	struct vrf_info *vi = link->l_info;
+
+	IS_VRF_LINK_ASSERT(link);
+	if(!id)
+		return -NLE_INVAL;
+
+	if (vi->vi_mask & VRF_HAS_TABLE_ID)
+		*id = vi->table_id;
+	else
+		return -NLE_AGAIN;
+
+	return 0;
+}
+
+/**
+ * Set VRF table id
+ * @arg link           Link object
+ * @arg id             Table identifier associated with VRF link
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vrf_set_tableid(struct rtnl_link *link, uint32_t id)
+{
+	struct vrf_info *vi = link->l_info;
+
+	IS_VRF_LINK_ASSERT(link);
+	if(id > VRF_TABLE_ID_MAX)
+		return -NLE_INVAL;
+
+	vi->table_id = id;
+	vi->vi_mask |= VRF_HAS_TABLE_ID;
+
+	return 0;
+}
+
+/** @} */
+
+static void __init vrf_init(void)
+{
+	rtnl_link_register_info(&vrf_info_ops);
+}
+
+static void __exit vrf_exit(void)
+{
+	rtnl_link_unregister_info(&vrf_info_ops);
+}
+
+/** @} */
diff --git a/lib/route/link/vxlan.c b/lib/route/link/vxlan.c
index f3e3538..686ac31 100644
--- a/lib/route/link/vxlan.c
+++ b/lib/route/link/vxlan.c
@@ -34,30 +34,44 @@
 #include <linux/if_link.h>
 
 /** @cond SKIP */
-#define VXLAN_HAS_ID	(1<<0)
-#define VXLAN_HAS_GROUP	(1<<1)
-#define VXLAN_HAS_LINK	(1<<2)
-#define VXLAN_HAS_LOCAL	(1<<3)
-#define VXLAN_HAS_TTL	(1<<4)
-#define VXLAN_HAS_TOS	(1<<5)
-#define VXLAN_HAS_LEARNING	(1<<6)
-#define VXLAN_HAS_AGEING	(1<<7)
-#define VXLAN_HAS_LIMIT		(1<<8)
-#define VXLAN_HAS_PORT_RANGE	(1<<9)
-#define VXLAN_HAS_PROXY	(1<<10)
-#define VXLAN_HAS_RSC	(1<<11)
-#define VXLAN_HAS_L2MISS	(1<<12)
-#define VXLAN_HAS_L3MISS	(1<<13)
+#define VXLAN_ATTR_ID                  (1<<0)
+#define VXLAN_ATTR_GROUP               (1<<1)
+#define VXLAN_ATTR_LINK                (1<<2)
+#define VXLAN_ATTR_LOCAL               (1<<3)
+#define VXLAN_ATTR_TTL                 (1<<4)
+#define VXLAN_ATTR_TOS                 (1<<5)
+#define VXLAN_ATTR_LEARNING            (1<<6)
+#define VXLAN_ATTR_AGEING              (1<<7)
+#define VXLAN_ATTR_LIMIT               (1<<8)
+#define VXLAN_ATTR_PORT_RANGE          (1<<9)
+#define VXLAN_ATTR_PROXY               (1<<10)
+#define VXLAN_ATTR_RSC                 (1<<11)
+#define VXLAN_ATTR_L2MISS              (1<<12)
+#define VXLAN_ATTR_L3MISS              (1<<13)
+#define VXLAN_ATTR_GROUP6              (1<<14)
+#define VXLAN_ATTR_LOCAL6              (1<<15)
+#define VXLAN_ATTR_PORT                (1<<16)
+#define VXLAN_ATTR_UDP_CSUM            (1<<17)
+#define VXLAN_ATTR_UDP_ZERO_CSUM6_TX   (1<<18)
+#define VXLAN_ATTR_UDP_ZERO_CSUM6_RX   (1<<19)
+#define VXLAN_ATTR_REMCSUM_TX          (1<<20)
+#define VXLAN_ATTR_REMCSUM_RX          (1<<21)
+#define VXLAN_ATTR_COLLECT_METADATA    (1<<22)
+#define VXLAN_ATTR_LABEL               (1<<23)
+#define VXLAN_ATTR_FLAGS               (1<<24)
 
 struct vxlan_info
 {
 	uint32_t		vxi_id;
 	uint32_t		vxi_group;
+	struct in6_addr		vxi_group6;
 	uint32_t		vxi_link;
 	uint32_t		vxi_local;
+	struct in6_addr		vxi_local6;
 	uint8_t			vxi_ttl;
 	uint8_t			vxi_tos;
 	uint8_t			vxi_learning;
+	uint8_t			vxi_flags;
 	uint32_t		vxi_ageing;
 	uint32_t		vxi_limit;
 	struct ifla_vxlan_port_range	vxi_port_range;
@@ -65,18 +79,29 @@
 	uint8_t			vxi_rsc;
 	uint8_t			vxi_l2miss;
 	uint8_t			vxi_l3miss;
-	uint32_t		vxi_mask;
+	uint16_t		vxi_port;
+	uint8_t			vxi_udp_csum;
+	uint8_t			vxi_udp_zero_csum6_tx;
+	uint8_t			vxi_udp_zero_csum6_rx;
+	uint8_t			vxi_remcsum_tx;
+	uint8_t			vxi_remcsum_rx;
+	uint8_t			vxi_collect_metadata;
+	uint32_t		vxi_label;
+	uint32_t		ce_mask;
 };
 
 /** @endcond */
 
 static struct nla_policy vxlan_policy[IFLA_VXLAN_MAX+1] = {
-	[IFLA_VXLAN_ID]	= { .type = NLA_U32 },
+	[IFLA_VXLAN_ID] = { .type = NLA_U32 },
 	[IFLA_VXLAN_GROUP] = { .minlen = sizeof(uint32_t) },
+	[IFLA_VXLAN_GROUP6] = { .minlen = sizeof(struct in6_addr) },
 	[IFLA_VXLAN_LINK] = { .type = NLA_U32 },
 	[IFLA_VXLAN_LOCAL] = { .minlen = sizeof(uint32_t) },
+	[IFLA_VXLAN_LOCAL6] = { .minlen = sizeof(struct in6_addr) },
 	[IFLA_VXLAN_TTL] = { .type = NLA_U8 },
 	[IFLA_VXLAN_TOS] = { .type = NLA_U8 },
+	[IFLA_VXLAN_LABEL] = { .type = NLA_U32 },
 	[IFLA_VXLAN_LEARNING] = { .type = NLA_U8 },
 	[IFLA_VXLAN_AGEING] = { .type = NLA_U32 },
 	[IFLA_VXLAN_LIMIT] = { .type = NLA_U32 },
@@ -85,16 +110,30 @@
 	[IFLA_VXLAN_RSC] = { .type = NLA_U8 },
 	[IFLA_VXLAN_L2MISS] = { .type = NLA_U8 },
 	[IFLA_VXLAN_L3MISS] = { .type = NLA_U8 },
+	[IFLA_VXLAN_COLLECT_METADATA] = { .type = NLA_U8 },
+	[IFLA_VXLAN_PORT] = { .type = NLA_U16 },
+	[IFLA_VXLAN_UDP_CSUM] = { .type = NLA_U8 },
+	[IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 },
+	[IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 },
+	[IFLA_VXLAN_REMCSUM_TX] = { .type = NLA_U8 },
+	[IFLA_VXLAN_REMCSUM_RX] = { .type = NLA_U8 },
+	[IFLA_VXLAN_GBP] = { .type = NLA_FLAG, },
+	[IFLA_VXLAN_GPE] = { .type = NLA_FLAG, },
+	[IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NLA_FLAG },
 };
 
 static int vxlan_alloc(struct rtnl_link *link)
 {
 	struct vxlan_info *vxi;
 
-	if ((vxi = calloc(1, sizeof(*vxi))) == NULL)
-		return -NLE_NOMEM;
+	if (link->l_info)
+		memset(link->l_info, 0, sizeof(*vxi));
+	else {
+		if ((vxi = calloc(1, sizeof(*vxi))) == NULL)
+			return -NLE_NOMEM;
 
-	link->l_info = vxi;
+		link->l_info = vxi;
+	}
 
 	return 0;
 }
@@ -106,7 +145,7 @@
 	struct vxlan_info *vxi;
 	int err;
 
-	NL_DBG(3, "Parsing VXLAN link info");
+	NL_DBG(3, "Parsing VXLAN link info\n");
 
 	if ((err = nla_parse_nested(tb, IFLA_VXLAN_MAX, data, vxlan_policy)) < 0)
 		goto errout;
@@ -118,77 +157,140 @@
 
 	if (tb[IFLA_VXLAN_ID]) {
 		vxi->vxi_id = nla_get_u32(tb[IFLA_VXLAN_ID]);
-		vxi->vxi_mask |= VXLAN_HAS_ID;
+		vxi->ce_mask |= VXLAN_ATTR_ID;
+	}
+
+	if (tb[IFLA_VXLAN_GROUP6]) {
+		nla_memcpy(&vxi->vxi_group6, tb[IFLA_VXLAN_GROUP6],
+			   sizeof(vxi->vxi_group6));
+		vxi->ce_mask |= VXLAN_ATTR_GROUP6;
 	}
 
 	if (tb[IFLA_VXLAN_GROUP]) {
 		nla_memcpy(&vxi->vxi_group, tb[IFLA_VXLAN_GROUP],
 				   sizeof(vxi->vxi_group));
-		vxi->vxi_mask |= VXLAN_HAS_GROUP;
+		vxi->ce_mask |= VXLAN_ATTR_GROUP;
+		vxi->ce_mask &= ~VXLAN_ATTR_GROUP6;
 	}
 
 	if (tb[IFLA_VXLAN_LINK]) {
 		vxi->vxi_link = nla_get_u32(tb[IFLA_VXLAN_LINK]);
-		vxi->vxi_mask |= VXLAN_HAS_LINK;
+		vxi->ce_mask |= VXLAN_ATTR_LINK;
+	}
+
+	if (tb[IFLA_VXLAN_LOCAL6]) {
+		nla_memcpy(&vxi->vxi_local6, tb[IFLA_VXLAN_LOCAL6],
+			   sizeof(vxi->vxi_local6));
+		vxi->ce_mask |= VXLAN_ATTR_LOCAL6;
 	}
 
 	if (tb[IFLA_VXLAN_LOCAL]) {
 		nla_memcpy(&vxi->vxi_local, tb[IFLA_VXLAN_LOCAL],
 				   sizeof(vxi->vxi_local));
-		vxi->vxi_mask |= VXLAN_HAS_LOCAL;
+		vxi->ce_mask |= VXLAN_ATTR_LOCAL;
+		vxi->ce_mask &= ~VXLAN_ATTR_LOCAL6;
 	}
 
 	if (tb[IFLA_VXLAN_TTL]) {
 		vxi->vxi_ttl = nla_get_u8(tb[IFLA_VXLAN_TTL]);
-		vxi->vxi_mask |= VXLAN_HAS_TTL;
+		vxi->ce_mask |= VXLAN_ATTR_TTL;
 	}
 
 	if (tb[IFLA_VXLAN_TOS]) {
 		vxi->vxi_tos = nla_get_u8(tb[IFLA_VXLAN_TOS]);
-		vxi->vxi_mask |= VXLAN_HAS_TOS;
+		vxi->ce_mask |= VXLAN_ATTR_TOS;
 	}
 
 	if (tb[IFLA_VXLAN_LEARNING]) {
 		vxi->vxi_learning = nla_get_u8(tb[IFLA_VXLAN_LEARNING]);
-		vxi->vxi_mask |= VXLAN_HAS_LEARNING;
+		vxi->ce_mask |= VXLAN_ATTR_LEARNING;
 	}
 
 	if (tb[IFLA_VXLAN_AGEING]) {
 		vxi->vxi_ageing = nla_get_u32(tb[IFLA_VXLAN_AGEING]);
-		vxi->vxi_mask |= VXLAN_HAS_AGEING;
+		vxi->ce_mask |= VXLAN_ATTR_AGEING;
 	}
 
 	if (tb[IFLA_VXLAN_LIMIT]) {
 		vxi->vxi_limit = nla_get_u32(tb[IFLA_VXLAN_LIMIT]);
-		vxi->vxi_mask |= VXLAN_HAS_LIMIT;
+		vxi->ce_mask |= VXLAN_ATTR_LIMIT;
 	}
 
 	if (tb[IFLA_VXLAN_PORT_RANGE]) {
 		nla_memcpy(&vxi->vxi_port_range, tb[IFLA_VXLAN_PORT_RANGE],
 				   sizeof(vxi->vxi_port_range));
-		vxi->vxi_mask |= VXLAN_HAS_PORT_RANGE;
+		vxi->ce_mask |= VXLAN_ATTR_PORT_RANGE;
 	}
 
 	if (tb[IFLA_VXLAN_PROXY]) {
 		vxi->vxi_proxy = nla_get_u8(tb[IFLA_VXLAN_PROXY]);
-		vxi->vxi_mask |= VXLAN_HAS_PROXY;
+		vxi->ce_mask |= VXLAN_ATTR_PROXY;
 	}
 
 	if (tb[IFLA_VXLAN_RSC]) {
 		vxi->vxi_rsc = nla_get_u8(tb[IFLA_VXLAN_RSC]);
-		vxi->vxi_mask |= VXLAN_HAS_RSC;
+		vxi->ce_mask |= VXLAN_ATTR_RSC;
 	}
 
 	if (tb[IFLA_VXLAN_L2MISS]) {
 		vxi->vxi_l2miss = nla_get_u8(tb[IFLA_VXLAN_L2MISS]);
-		vxi->vxi_mask |= VXLAN_HAS_L2MISS;
+		vxi->ce_mask |= VXLAN_ATTR_L2MISS;
 	}
 
 	if (tb[IFLA_VXLAN_L3MISS]) {
 		vxi->vxi_l3miss = nla_get_u8(tb[IFLA_VXLAN_L3MISS]);
-		vxi->vxi_mask |= VXLAN_HAS_L3MISS;
+		vxi->ce_mask |= VXLAN_ATTR_L3MISS;
 	}
 
+	if (tb[IFLA_VXLAN_PORT]) {
+		vxi->vxi_port = nla_get_u16(tb[IFLA_VXLAN_PORT]);
+		vxi->ce_mask |= VXLAN_ATTR_PORT;
+	}
+
+	if (tb[IFLA_VXLAN_UDP_CSUM]) {
+		vxi->vxi_udp_csum = nla_get_u8(tb[IFLA_VXLAN_UDP_CSUM]);
+		vxi->ce_mask |= VXLAN_ATTR_UDP_CSUM;
+	}
+
+	if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]) {
+		vxi->vxi_udp_zero_csum6_tx = nla_get_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]);
+		vxi->ce_mask |= VXLAN_ATTR_UDP_ZERO_CSUM6_TX;
+	}
+
+	if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]) {
+		vxi->vxi_udp_zero_csum6_rx = nla_get_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]);
+		vxi->ce_mask |= VXLAN_ATTR_UDP_ZERO_CSUM6_RX;
+	}
+
+	if (tb[IFLA_VXLAN_REMCSUM_TX]) {
+		vxi->vxi_remcsum_tx = nla_get_u8(tb[IFLA_VXLAN_REMCSUM_TX]);
+		vxi->ce_mask |= VXLAN_ATTR_REMCSUM_TX;
+	}
+
+	if (tb[IFLA_VXLAN_REMCSUM_RX]) {
+		vxi->vxi_remcsum_rx = nla_get_u8(tb[IFLA_VXLAN_REMCSUM_RX]);
+		vxi->ce_mask |= VXLAN_ATTR_REMCSUM_RX;
+	}
+
+	if (tb[IFLA_VXLAN_GBP])
+		vxi->vxi_flags |= RTNL_LINK_VXLAN_F_GBP;
+
+	if (tb[IFLA_VXLAN_REMCSUM_NOPARTIAL])
+		vxi->vxi_flags |= RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL;
+
+	if (tb[IFLA_VXLAN_COLLECT_METADATA]) {
+		vxi->vxi_collect_metadata = nla_get_u8(tb[IFLA_VXLAN_COLLECT_METADATA]);
+		vxi->ce_mask |= VXLAN_ATTR_COLLECT_METADATA;
+	}
+
+	if (tb[IFLA_VXLAN_LABEL]) {
+		vxi->vxi_label = nla_get_u32(tb[IFLA_VXLAN_LABEL]);
+		vxi->ce_mask |= VXLAN_ATTR_LABEL;
+	}
+
+	if (tb[IFLA_VXLAN_GPE])
+		vxi->vxi_flags |= RTNL_LINK_VXLAN_F_GPE;
+
 	err = 0;
 
 errout:
@@ -213,36 +315,55 @@
 static void vxlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
 {
 	struct vxlan_info *vxi = link->l_info;
-	char *name, addr[INET_ADDRSTRLEN];
+	char *name, addr[INET6_ADDRSTRLEN];
+	struct rtnl_link *parent;
 
 	nl_dump_line(p, "    vxlan-id %u\n", vxi->vxi_id);
 
-	if (vxi->vxi_mask & VXLAN_HAS_GROUP) {
+	if (vxi->ce_mask & VXLAN_ATTR_GROUP) {
 		nl_dump(p, "      group ");
-		if(inet_ntop(AF_INET, &vxi->vxi_group, addr, sizeof(addr)))
+		if (inet_ntop(AF_INET, &vxi->vxi_group, addr, sizeof(addr)))
 			nl_dump_line(p, "%s\n", addr);
 		else
 			nl_dump_line(p, "%#x\n", ntohs(vxi->vxi_group));
+	} else if (vxi->ce_mask & VXLAN_ATTR_GROUP6) {
+		nl_dump(p, "      group ");
+		if (inet_ntop(AF_INET6, &vxi->vxi_group6, addr, sizeof(addr)))
+			nl_dump_line(p, "%s\n", addr);
+		else
+			nl_dump_line(p, "%#x\n", vxi->vxi_group6);
 	}
 
-	if (vxi->vxi_mask & VXLAN_HAS_LINK) {
+	if (vxi->ce_mask & VXLAN_ATTR_LINK) {
 		nl_dump(p, "      link ");
-		name = rtnl_link_get_name(link);
+
+		name = NULL;
+		parent = link_lookup(link->ce_cache, vxi->vxi_link);
+		if (parent)
+			name = rtnl_link_get_name(parent);
+
 		if (name)
 			nl_dump_line(p, "%s\n", name);
 		else
 			nl_dump_line(p, "%u\n", vxi->vxi_link);
 	}
 
-	if (vxi->vxi_mask & VXLAN_HAS_LOCAL) {
+	if (vxi->ce_mask & VXLAN_ATTR_LOCAL) {
 		nl_dump(p, "      local ");
-		if(inet_ntop(AF_INET, &vxi->vxi_local, addr, sizeof(addr)))
+		if (inet_ntop(AF_INET, &vxi->vxi_local, addr, sizeof(addr)))
 			nl_dump_line(p, "%s\n", addr);
 		else
 			nl_dump_line(p, "%#x\n", ntohs(vxi->vxi_local));
+	} else if (vxi->ce_mask & VXLAN_ATTR_LOCAL6) {
+		nl_dump(p, "      local ");
+		if (inet_ntop(AF_INET6, &vxi->vxi_local6, addr, sizeof(addr)))
+			nl_dump_line(p, "%s\n", addr);
+		else
+			nl_dump_line(p, "%#x\n", vxi->vxi_local6);
 	}
 
-	if (vxi->vxi_mask & VXLAN_HAS_TTL) {
+
+	if (vxi->ce_mask & VXLAN_ATTR_TTL) {
 		nl_dump(p, "      ttl ");
 		if(vxi->vxi_ttl)
 			nl_dump_line(p, "%u\n", vxi->vxi_ttl);
@@ -250,7 +371,7 @@
 			nl_dump_line(p, "inherit\n");
 	}
 
-	if (vxi->vxi_mask & VXLAN_HAS_TOS) {
+	if (vxi->ce_mask & VXLAN_ATTR_TOS) {
 		nl_dump(p, "      tos ");
 		if (vxi->vxi_tos == 1)
 			nl_dump_line(p, "inherit\n", vxi->vxi_tos);
@@ -258,7 +379,7 @@
 			nl_dump_line(p, "%#x\n", vxi->vxi_tos);
 	}
 
-	if (vxi->vxi_mask & VXLAN_HAS_LEARNING) {
+	if (vxi->ce_mask & VXLAN_ATTR_LEARNING) {
 		nl_dump(p, "      learning ");
 		if (vxi->vxi_learning)
 			nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_learning);
@@ -266,7 +387,7 @@
 			nl_dump_line(p, "disabled\n");
 	}
 
-	if (vxi->vxi_mask & VXLAN_HAS_AGEING) {
+	if (vxi->ce_mask & VXLAN_ATTR_AGEING) {
 		nl_dump(p, "      ageing ");
 		if (vxi->vxi_ageing)
 			nl_dump_line(p, "%u seconds\n", vxi->vxi_ageing);
@@ -274,7 +395,7 @@
 			nl_dump_line(p, "disabled\n");
 	}
 
-	if (vxi->vxi_mask & VXLAN_HAS_LIMIT) {
+	if (vxi->ce_mask & VXLAN_ATTR_LIMIT) {
 		nl_dump(p, "      limit ");
 		if (vxi->vxi_limit)
 			nl_dump_line(p, "%u\n", vxi->vxi_limit);
@@ -282,12 +403,12 @@
 			nl_dump_line(p, "unlimited\n");
 	}
 
-	if (vxi->vxi_mask & VXLAN_HAS_PORT_RANGE)
+	if (vxi->ce_mask & VXLAN_ATTR_PORT_RANGE)
 		nl_dump_line(p, "      port range %u - %u\n",
 					 ntohs(vxi->vxi_port_range.low),
 					 ntohs(vxi->vxi_port_range.high));
 
-	if (vxi->vxi_mask & VXLAN_HAS_PROXY) {
+	if (vxi->ce_mask & VXLAN_ATTR_PROXY) {
 		nl_dump(p, "      proxy ");
 		if (vxi->vxi_proxy)
 			nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_proxy);
@@ -295,7 +416,7 @@
 			nl_dump_line(p, "disabled\n");
 	}
 
-	if (vxi->vxi_mask & VXLAN_HAS_RSC) {
+	if (vxi->ce_mask & VXLAN_ATTR_RSC) {
 		nl_dump(p, "      rsc ");
 		if (vxi->vxi_rsc)
 			nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_rsc);
@@ -303,7 +424,7 @@
 			nl_dump_line(p, "disabled\n");
 	}
 
-	if (vxi->vxi_mask & VXLAN_HAS_L2MISS) {
+	if (vxi->ce_mask & VXLAN_ATTR_L2MISS) {
 		nl_dump(p, "      l2miss ");
 		if (vxi->vxi_l2miss)
 			nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_l2miss);
@@ -311,13 +432,80 @@
 			nl_dump_line(p, "disabled\n");
 	}
 
-	if (vxi->vxi_mask & VXLAN_HAS_L3MISS) {
+	if (vxi->ce_mask & VXLAN_ATTR_L3MISS) {
 		nl_dump(p, "      l3miss ");
 		if (vxi->vxi_l3miss)
 			nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_l3miss);
 		else
 			nl_dump_line(p, "disabled\n");
 	}
+
+	if (vxi->ce_mask & VXLAN_ATTR_PORT) {
+		nl_dump(p, "      port ");
+		nl_dump_line(p, "%u\n", ntohs(vxi->vxi_port));
+	}
+
+	if (vxi->ce_mask & VXLAN_ATTR_UDP_CSUM) {
+		nl_dump(p, "      UDP checksums ");
+		if (vxi->vxi_udp_csum)
+			nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_udp_csum);
+		else
+			nl_dump_line(p, "disabled\n");
+	}
+
+	if (vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_TX) {
+		nl_dump(p, "      udp-zero-csum6-tx ");
+		if (vxi->vxi_udp_zero_csum6_tx)
+			nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_udp_zero_csum6_tx);
+		else
+			nl_dump_line(p, "disabled\n");
+	}
+
+	if (vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_RX) {
+		nl_dump(p, "      udp-zero-csum6-rx ");
+		if (vxi->vxi_udp_zero_csum6_rx)
+			nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_udp_zero_csum6_rx);
+		else
+			nl_dump_line(p, "disabled\n");
+	}
+
+	if (vxi->ce_mask & VXLAN_ATTR_REMCSUM_TX) {
+		nl_dump(p, "      remcsum-tx ");
+		if (vxi->vxi_remcsum_tx)
+			nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_remcsum_tx);
+		else
+			nl_dump_line(p, "disabled\n");
+	}
+
+	if (vxi->ce_mask & VXLAN_ATTR_REMCSUM_RX) {
+		nl_dump(p, "      remcsum-rx ");
+		if (vxi->vxi_remcsum_rx)
+			nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_remcsum_rx);
+		else
+			nl_dump_line(p, "disabled\n");
+	}
+
+	if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_GBP)
+		nl_dump(p, "      gbp\n");
+
+	if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL)
+		nl_dump(p, "      rncsum-nopartial\n");
+
+	if (vxi->ce_mask & VXLAN_ATTR_COLLECT_METADATA) {
+		nl_dump(p, "      remcsum-rx ");
+		if (vxi->vxi_collect_metadata)
+			nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_collect_metadata);
+		else
+			nl_dump_line(p, "disabled\n");
+	}
+
+	if (vxi->ce_mask & VXLAN_ATTR_LABEL) {
+		nl_dump(p, "      label ");
+		nl_dump_line(p, "%u\n", ntohl(vxi->vxi_label));
+	}
+
+	if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_GPE)
+		nl_dump(p, "      gpe\n");
 }
 
 static int vxlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
@@ -346,49 +534,88 @@
 	if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
 		return -NLE_MSGSIZE;
 
-	if (vxi->vxi_mask & VXLAN_HAS_ID)
+	if (vxi->ce_mask & VXLAN_ATTR_ID)
 		NLA_PUT_U32(msg, IFLA_VXLAN_ID, vxi->vxi_id);
 
-	if (vxi->vxi_mask & VXLAN_HAS_GROUP)
+	if (vxi->ce_mask & VXLAN_ATTR_GROUP)
 		NLA_PUT(msg, IFLA_VXLAN_GROUP, sizeof(vxi->vxi_group), &vxi->vxi_group);
 
-	if (vxi->vxi_mask & VXLAN_HAS_LINK)
+	if (vxi->ce_mask & VXLAN_ATTR_GROUP6)
+		NLA_PUT(msg, IFLA_VXLAN_GROUP6, sizeof(vxi->vxi_group6), &vxi->vxi_group6);
+
+	if (vxi->ce_mask & VXLAN_ATTR_LINK)
 		NLA_PUT_U32(msg, IFLA_VXLAN_LINK, vxi->vxi_link);
 
-	if (vxi->vxi_mask & VXLAN_HAS_LOCAL)
+	if (vxi->ce_mask & VXLAN_ATTR_LOCAL)
 		NLA_PUT(msg, IFLA_VXLAN_LOCAL, sizeof(vxi->vxi_local), &vxi->vxi_local);
 
-	if (vxi->vxi_mask & VXLAN_HAS_TTL)
+	if (vxi->ce_mask & VXLAN_ATTR_LOCAL6)
+		NLA_PUT(msg, IFLA_VXLAN_LOCAL6, sizeof(vxi->vxi_local6), &vxi->vxi_local6);
+
+	if (vxi->ce_mask & VXLAN_ATTR_TTL)
 		NLA_PUT_U8(msg, IFLA_VXLAN_TTL, vxi->vxi_ttl);
 
-	if (vxi->vxi_mask & VXLAN_HAS_TOS)
+	if (vxi->ce_mask & VXLAN_ATTR_TOS)
 		NLA_PUT_U8(msg, IFLA_VXLAN_TOS, vxi->vxi_tos);
 
-	if (vxi->vxi_mask & VXLAN_HAS_LEARNING)
+	if (vxi->ce_mask & VXLAN_ATTR_LEARNING)
 		NLA_PUT_U8(msg, IFLA_VXLAN_LEARNING, vxi->vxi_learning);
 
-	if (vxi->vxi_mask & VXLAN_HAS_AGEING)
+	if (vxi->ce_mask & VXLAN_ATTR_AGEING)
 		NLA_PUT_U32(msg, IFLA_VXLAN_AGEING, vxi->vxi_ageing);
 
-	if (vxi->vxi_mask & VXLAN_HAS_LIMIT)
+	if (vxi->ce_mask & VXLAN_ATTR_LIMIT)
 		NLA_PUT_U32(msg, IFLA_VXLAN_LIMIT, vxi->vxi_limit);
 
-	if (vxi->vxi_mask & VXLAN_HAS_PORT_RANGE)
+	if (vxi->ce_mask & VXLAN_ATTR_PORT_RANGE)
 		NLA_PUT(msg, IFLA_VXLAN_PORT_RANGE, sizeof(vxi->vxi_port_range),
 				&vxi->vxi_port_range);
 
-	if (vxi->vxi_mask & VXLAN_HAS_PROXY)
+	if (vxi->ce_mask & VXLAN_ATTR_PROXY)
 		NLA_PUT_U8(msg, IFLA_VXLAN_PROXY, vxi->vxi_proxy);
 
-	if (vxi->vxi_mask & VXLAN_HAS_RSC)
+	if (vxi->ce_mask & VXLAN_ATTR_RSC)
 		NLA_PUT_U8(msg, IFLA_VXLAN_RSC, vxi->vxi_rsc);
 
-	if (vxi->vxi_mask & VXLAN_HAS_L2MISS)
+	if (vxi->ce_mask & VXLAN_ATTR_L2MISS)
 		NLA_PUT_U8(msg, IFLA_VXLAN_L2MISS, vxi->vxi_l2miss);
 
-	if (vxi->vxi_mask & VXLAN_HAS_L3MISS)
+	if (vxi->ce_mask & VXLAN_ATTR_L3MISS)
 		NLA_PUT_U8(msg, IFLA_VXLAN_L3MISS, vxi->vxi_l3miss);
 
+	if (vxi->ce_mask & VXLAN_ATTR_PORT)
+		NLA_PUT_U32(msg, IFLA_VXLAN_PORT, vxi->vxi_port);
+
+	if (vxi->ce_mask & VXLAN_ATTR_UDP_CSUM)
+		NLA_PUT_U8(msg, IFLA_VXLAN_UDP_CSUM, vxi->vxi_udp_csum);
+
+	if (vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_TX)
+		NLA_PUT_U8(msg, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, vxi->vxi_udp_zero_csum6_tx);
+
+	if (vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_RX)
+		NLA_PUT_U8(msg, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, vxi->vxi_udp_zero_csum6_rx);
+
+	if (vxi->ce_mask & VXLAN_ATTR_REMCSUM_TX)
+		NLA_PUT_U8(msg, IFLA_VXLAN_REMCSUM_TX, vxi->vxi_remcsum_tx);
+
+	if (vxi->ce_mask & VXLAN_ATTR_REMCSUM_RX)
+		NLA_PUT_U8(msg, IFLA_VXLAN_REMCSUM_RX, vxi->vxi_remcsum_rx);
+
+	if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_GBP)
+		NLA_PUT_FLAG(msg, IFLA_VXLAN_GBP);
+
+	if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL)
+		NLA_PUT_FLAG(msg, IFLA_VXLAN_REMCSUM_NOPARTIAL);
+
+	if (vxi->ce_mask & VXLAN_ATTR_COLLECT_METADATA)
+		NLA_PUT_U8(msg, IFLA_VXLAN_COLLECT_METADATA, vxi->vxi_collect_metadata);
+
+	if (vxi->ce_mask & VXLAN_ATTR_LABEL)
+		NLA_PUT_U32(msg, IFLA_VXLAN_LABEL, vxi->vxi_label);
+
+	if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_GPE)
+		NLA_PUT_FLAG(msg, IFLA_VXLAN_GPE);
+
 	nla_nest_end(msg, data);
 
 nla_put_failure:
@@ -396,6 +623,49 @@
 	return 0;
 }
 
+static int vxlan_compare(struct rtnl_link *link_a, struct rtnl_link *link_b,
+			 int flags)
+{
+	struct vxlan_info *a = link_a->l_info;
+	struct vxlan_info *b = link_b->l_info;
+	int diff = 0;
+	uint32_t attrs = flags & LOOSE_COMPARISON ? b->ce_mask : ~0;
+
+#define VXLAN_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, VXLAN_ATTR_##ATTR, a, b, EXPR)
+
+	diff |= VXLAN_DIFF(ID,    a->vxi_id    != b->vxi_id);
+	diff |= VXLAN_DIFF(GROUP, a->vxi_group != b->vxi_group);
+	diff |= VXLAN_DIFF(LINK,  a->vxi_link  != b->vxi_link);
+	diff |= VXLAN_DIFF(LOCAL, a->vxi_local != b->vxi_local);
+	diff |= VXLAN_DIFF(TOS,   a->vxi_tos   != b->vxi_tos);
+	diff |= VXLAN_DIFF(TTL,   a->vxi_ttl   != b->vxi_ttl);
+	diff |= VXLAN_DIFF(LEARNING, a->vxi_learning != b->vxi_learning);
+	diff |= VXLAN_DIFF(AGEING, a->vxi_ageing != b->vxi_ageing);
+	diff |= VXLAN_DIFF(LIMIT, a->vxi_limit != b->vxi_limit);
+	diff |= VXLAN_DIFF(PORT_RANGE,
+	                   a->vxi_port_range.low != b->vxi_port_range.low);
+	diff |= VXLAN_DIFF(PORT_RANGE,
+	                   a->vxi_port_range.high != b->vxi_port_range.high);
+	diff |= VXLAN_DIFF(PROXY, a->vxi_proxy != b->vxi_proxy);
+	diff |= VXLAN_DIFF(RSC, a->vxi_proxy != b->vxi_proxy);
+	diff |= VXLAN_DIFF(L2MISS, a->vxi_proxy != b->vxi_proxy);
+	diff |= VXLAN_DIFF(L3MISS, a->vxi_proxy != b->vxi_proxy);
+	diff |= VXLAN_DIFF(PORT, a->vxi_port != b->vxi_port);
+	diff |= VXLAN_DIFF(GROUP6, memcmp(&a->vxi_group6, &b->vxi_group6, sizeof(a->vxi_group6)) != 0);
+	diff |= VXLAN_DIFF(LOCAL6,  memcmp(&a->vxi_local6, &b->vxi_local6, sizeof(a->vxi_local6)) != 0);
+	diff |= VXLAN_DIFF(UDP_CSUM, a->vxi_proxy != b->vxi_proxy);
+	diff |= VXLAN_DIFF(UDP_ZERO_CSUM6_TX, a->vxi_proxy != b->vxi_proxy);
+	diff |= VXLAN_DIFF(UDP_ZERO_CSUM6_RX, a->vxi_proxy != b->vxi_proxy);
+	diff |= VXLAN_DIFF(REMCSUM_TX, a->vxi_proxy != b->vxi_proxy);
+	diff |= VXLAN_DIFF(REMCSUM_RX, a->vxi_proxy != b->vxi_proxy);
+	diff |= VXLAN_DIFF(COLLECT_METADATA, a->vxi_collect_metadata != b->vxi_collect_metadata);
+	diff |= VXLAN_DIFF(LABEL, a->vxi_label != b->vxi_label);
+	diff |= VXLAN_DIFF(FLAGS, a->vxi_flags != b->vxi_flags);
+#undef VXLAN_DIFF
+
+	return diff;
+}
+
 static struct rtnl_link_info_ops vxlan_info_ops = {
 	.io_name		= "vxlan",
 	.io_alloc		= vxlan_alloc,
@@ -407,6 +677,7 @@
 	.io_clone		= vxlan_clone,
 	.io_put_attrs		= vxlan_put_attrs,
 	.io_free		= vxlan_free,
+	.io_compare             = vxlan_compare,
 };
 
 /** @cond SKIP */
@@ -471,7 +742,7 @@
 		return -NLE_INVAL;
 
 	vxi->vxi_id = id;
-	vxi->vxi_mask |= VXLAN_HAS_ID;
+	vxi->ce_mask |= VXLAN_ATTR_ID;
 
 	return 0;
 }
@@ -492,7 +763,7 @@
 	if(!id)
 		return -NLE_INVAL;
 
-	if (vxi->vxi_mask & VXLAN_HAS_ID)
+	if (vxi->ce_mask & VXLAN_ATTR_ID)
 		*id = vxi->vxi_id;
 	else
 		return -NLE_AGAIN;
@@ -513,14 +784,21 @@
 
 	IS_VXLAN_LINK_ASSERT(link);
 
-	if ((nl_addr_get_family(addr) != AF_INET) ||
-		(nl_addr_get_len(addr) != sizeof(vxi->vxi_group)))
+	if ((nl_addr_get_family(addr) == AF_INET) &&
+	    (nl_addr_get_len(addr) == sizeof(vxi->vxi_group))) {
+		memcpy(&vxi->vxi_group, nl_addr_get_binary_addr(addr),
+		       sizeof(vxi->vxi_group));
+		vxi->ce_mask |= VXLAN_ATTR_GROUP;
+		vxi->ce_mask &= ~VXLAN_ATTR_GROUP6;
+	} else if ((nl_addr_get_family(addr) == AF_INET6) &&
+		   (nl_addr_get_len(addr) == sizeof(vxi->vxi_group6))) {
+		memcpy(&vxi->vxi_group6, nl_addr_get_binary_addr(addr),
+		       sizeof(vxi->vxi_group6));
+		vxi->ce_mask |= VXLAN_ATTR_GROUP6;
+		vxi->ce_mask &= ~VXLAN_ATTR_GROUP;
+	} else
 		return -NLE_INVAL;
 
-	memcpy(&vxi->vxi_group, nl_addr_get_binary_addr(addr),
-		   sizeof(vxi->vxi_group));
-	vxi->vxi_mask |= VXLAN_HAS_GROUP;
-
 	return 0;
 }
 
@@ -540,11 +818,13 @@
 	if (!addr)
 		return -NLE_INVAL;
 
-	if (!(vxi->vxi_mask & VXLAN_HAS_GROUP))
+	if (vxi->ce_mask & VXLAN_ATTR_GROUP)
+		*addr = nl_addr_build(AF_INET, &vxi->vxi_group, sizeof(vxi->vxi_group));
+	else if (vxi->ce_mask & VXLAN_ATTR_GROUP6)
+		*addr = nl_addr_build(AF_INET6, &vxi->vxi_group6, sizeof(vxi->vxi_group6));
+	else
 		return -NLE_AGAIN;
 
-	*addr = nl_addr_build(AF_INET, &vxi->vxi_group, sizeof(vxi->vxi_group));
-
 	return 0;
 }
 
@@ -562,7 +842,7 @@
 	IS_VXLAN_LINK_ASSERT(link);
 
 	vxi->vxi_link = index;
-	vxi->vxi_mask |= VXLAN_HAS_LINK;
+	vxi->ce_mask |= VXLAN_ATTR_LINK;
 
 	return 0;
 }
@@ -583,7 +863,7 @@
 	if (!index)
 		return -NLE_INVAL;
 
-	if (!(vxi->vxi_mask & VXLAN_HAS_LINK))
+	if (!(vxi->ce_mask & VXLAN_ATTR_LINK))
 		return -NLE_AGAIN;
 
 	*index = vxi->vxi_link;
@@ -604,14 +884,21 @@
 
 	IS_VXLAN_LINK_ASSERT(link);
 
-	if ((nl_addr_get_family(addr) != AF_INET) ||
-		(nl_addr_get_len(addr) != sizeof(vxi->vxi_local)))
+	if ((nl_addr_get_family(addr) == AF_INET) &&
+	    (nl_addr_get_len(addr) == sizeof(vxi->vxi_local))) {
+		memcpy(&vxi->vxi_local, nl_addr_get_binary_addr(addr),
+		       sizeof(vxi->vxi_local));
+		vxi->ce_mask |= VXLAN_ATTR_LOCAL;
+		vxi->ce_mask &= ~VXLAN_ATTR_LOCAL6;
+	} else if ((nl_addr_get_family(addr) == AF_INET6) &&
+		   (nl_addr_get_len(addr) == sizeof(vxi->vxi_local6))) {
+		memcpy(&vxi->vxi_local6, nl_addr_get_binary_addr(addr),
+		       sizeof(vxi->vxi_local6));
+		vxi->ce_mask |= VXLAN_ATTR_LOCAL6;
+		vxi->ce_mask &= ~VXLAN_ATTR_LOCAL;
+	} else
 		return -NLE_INVAL;
 
-	memcpy(&vxi->vxi_local, nl_addr_get_binary_addr(addr),
-		   sizeof(vxi->vxi_local));
-	vxi->vxi_mask |= VXLAN_HAS_LOCAL;
-
 	return 0;
 }
 
@@ -631,11 +918,13 @@
 	if (!addr)
 		return -NLE_INVAL;
 
-	if (!(vxi->vxi_mask & VXLAN_HAS_LOCAL))
+	if (vxi->ce_mask & VXLAN_ATTR_LOCAL)
+		*addr = nl_addr_build(AF_INET, &vxi->vxi_local, sizeof(vxi->vxi_local));
+	else if (vxi->ce_mask & VXLAN_ATTR_LOCAL6)
+		*addr = nl_addr_build(AF_INET6, &vxi->vxi_local6, sizeof(vxi->vxi_local6));
+	else
 		return -NLE_AGAIN;
 
-	*addr = nl_addr_build(AF_INET, &vxi->vxi_local, sizeof(vxi->vxi_local));
-
 	return 0;
 }
 
@@ -653,7 +942,7 @@
 	IS_VXLAN_LINK_ASSERT(link);
 
 	vxi->vxi_ttl = ttl;
-	vxi->vxi_mask |= VXLAN_HAS_TTL;
+	vxi->ce_mask |= VXLAN_ATTR_TTL;
 
 	return 0;
 }
@@ -670,7 +959,7 @@
 
 	IS_VXLAN_LINK_ASSERT(link);
 
-	if (!(vxi->vxi_mask & VXLAN_HAS_TTL))
+	if (!(vxi->ce_mask & VXLAN_ATTR_TTL))
 		return -NLE_AGAIN;
 
 	return vxi->vxi_ttl;
@@ -690,7 +979,7 @@
 	IS_VXLAN_LINK_ASSERT(link);
 
 	vxi->vxi_tos = tos;
-	vxi->vxi_mask |= VXLAN_HAS_TOS;
+	vxi->ce_mask |= VXLAN_ATTR_TOS;
 
 	return 0;
 }
@@ -707,7 +996,7 @@
 
 	IS_VXLAN_LINK_ASSERT(link);
 
-	if (!(vxi->vxi_mask & VXLAN_HAS_TOS))
+	if (!(vxi->ce_mask & VXLAN_ATTR_TOS))
 		return -NLE_AGAIN;
 
 	return vxi->vxi_tos;
@@ -727,7 +1016,7 @@
 	IS_VXLAN_LINK_ASSERT(link);
 
 	vxi->vxi_learning = learning;
-	vxi->vxi_mask |= VXLAN_HAS_LEARNING;
+	vxi->ce_mask |= VXLAN_ATTR_LEARNING;
 
 	return 0;
 }
@@ -744,7 +1033,7 @@
 
 	IS_VXLAN_LINK_ASSERT(link);
 
-	if (!(vxi->vxi_mask & VXLAN_HAS_LEARNING))
+	if (!(vxi->ce_mask & VXLAN_ATTR_LEARNING))
 		return -NLE_AGAIN;
 
 	return vxi->vxi_learning;
@@ -786,7 +1075,7 @@
 	IS_VXLAN_LINK_ASSERT(link);
 
 	vxi->vxi_ageing = expiry;
-	vxi->vxi_mask |= VXLAN_HAS_AGEING;
+	vxi->ce_mask |= VXLAN_ATTR_AGEING;
 
 	return 0;
 }
@@ -807,7 +1096,7 @@
 	if (!expiry)
 		return -NLE_INVAL;
 
-	if (vxi->vxi_mask & VXLAN_HAS_AGEING)
+	if (vxi->ce_mask & VXLAN_ATTR_AGEING)
 		*expiry = vxi->vxi_ageing;
 	else
 		return -NLE_AGAIN;
@@ -829,7 +1118,7 @@
 	IS_VXLAN_LINK_ASSERT(link);
 
 	vxi->vxi_limit = limit;
-	vxi->vxi_mask |= VXLAN_HAS_LIMIT;
+	vxi->ce_mask |= VXLAN_ATTR_LIMIT;
 
 	return 0;
 }
@@ -850,7 +1139,7 @@
 	if (!limit)
 		return -NLE_INVAL;
 
-	if (vxi->vxi_mask & VXLAN_HAS_LIMIT)
+	if (vxi->ce_mask & VXLAN_ATTR_LIMIT)
 		*limit = vxi->vxi_limit;
 	else
 		return -NLE_AGAIN;
@@ -866,7 +1155,7 @@
  * @return 0 on success or a negative error code
  */
 int rtnl_link_vxlan_set_port_range(struct rtnl_link *link,
-								   struct ifla_vxlan_port_range *range)
+                                   struct ifla_vxlan_port_range *range)
 {
 	struct vxlan_info *vxi = link->l_info;
 
@@ -876,7 +1165,7 @@
 		return -NLE_INVAL;
 
 	memcpy(&vxi->vxi_port_range, range, sizeof(vxi->vxi_port_range));
-	vxi->vxi_mask |= VXLAN_HAS_PORT_RANGE;
+	vxi->ce_mask |= VXLAN_ATTR_PORT_RANGE;
 
 	return 0;
 }
@@ -889,7 +1178,7 @@
  * @return 0 on success or a negative error code
  */
 int rtnl_link_vxlan_get_port_range(struct rtnl_link *link,
-								   struct ifla_vxlan_port_range *range)
+                                   struct ifla_vxlan_port_range *range)
 {
 	struct vxlan_info *vxi = link->l_info;
 
@@ -898,7 +1187,7 @@
 	if (!range)
 		return -NLE_INVAL;
 
-	if (vxi->vxi_mask & VXLAN_HAS_PORT_RANGE)
+	if (vxi->ce_mask & VXLAN_ATTR_PORT_RANGE)
 		memcpy(range, &vxi->vxi_port_range, sizeof(*range));
 	else
 		return -NLE_AGAIN;
@@ -920,7 +1209,7 @@
 	IS_VXLAN_LINK_ASSERT(link);
 
 	vxi->vxi_proxy = proxy;
-	vxi->vxi_mask |= VXLAN_HAS_PROXY;
+	vxi->ce_mask |= VXLAN_ATTR_PROXY;
 
 	return 0;
 }
@@ -937,7 +1226,7 @@
 
 	IS_VXLAN_LINK_ASSERT(link);
 
-	if (!(vxi->vxi_mask & VXLAN_HAS_PROXY))
+	if (!(vxi->ce_mask & VXLAN_ATTR_PROXY))
 		return -NLE_AGAIN;
 
 	return vxi->vxi_proxy;
@@ -979,7 +1268,7 @@
 	IS_VXLAN_LINK_ASSERT(link);
 
 	vxi->vxi_rsc = rsc;
-	vxi->vxi_mask |= VXLAN_HAS_RSC;
+	vxi->ce_mask |= VXLAN_ATTR_RSC;
 
 	return 0;
 }
@@ -996,7 +1285,7 @@
 
 	IS_VXLAN_LINK_ASSERT(link);
 
-	if (!(vxi->vxi_mask & VXLAN_HAS_RSC))
+	if (!(vxi->ce_mask & VXLAN_ATTR_RSC))
 		return -NLE_AGAIN;
 
 	return vxi->vxi_rsc;
@@ -1038,7 +1327,7 @@
 	IS_VXLAN_LINK_ASSERT(link);
 
 	vxi->vxi_l2miss = miss;
-	vxi->vxi_mask |= VXLAN_HAS_L2MISS;
+	vxi->ce_mask |= VXLAN_ATTR_L2MISS;
 
 	return 0;
 }
@@ -1055,7 +1344,7 @@
 
 	IS_VXLAN_LINK_ASSERT(link);
 
-	if (!(vxi->vxi_mask & VXLAN_HAS_L2MISS))
+	if (!(vxi->ce_mask & VXLAN_ATTR_L2MISS))
 		return -NLE_AGAIN;
 
 	return vxi->vxi_l2miss;
@@ -1097,7 +1386,7 @@
 	IS_VXLAN_LINK_ASSERT(link);
 
 	vxi->vxi_l3miss = miss;
-	vxi->vxi_mask |= VXLAN_HAS_L3MISS;
+	vxi->ce_mask |= VXLAN_ATTR_L3MISS;
 
 	return 0;
 }
@@ -1114,14 +1403,14 @@
 
 	IS_VXLAN_LINK_ASSERT(link);
 
-	if (!(vxi->vxi_mask & VXLAN_HAS_L3MISS))
+	if (!(vxi->ce_mask & VXLAN_ATTR_L3MISS))
 		return -NLE_AGAIN;
 
 	return vxi->vxi_l3miss;
 }
 
 /**
- * Enable netlink IP DDR miss notifications
+ * Enable netlink IP ADDR miss notifications
  * @arg link		Link object
  *
  * @return 0 on success or a negative error code
@@ -1142,6 +1431,356 @@
 	return rtnl_link_vxlan_set_l3miss(link, 0);
 }
 
+/**
+ * Set UDP destination port to use for VXLAN
+ * @arg link		Link object
+ * @arg port		Destination port
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_port(struct rtnl_link *link, uint32_t port)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	vxi->vxi_port = htons(port);
+	vxi->ce_mask |= VXLAN_ATTR_PORT;
+
+	return 0;
+}
+
+/**
+ * Get UDP destination port to use for VXLAN
+ * @arg link		Link object
+ * @arg port		Pointer to store destination port
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_get_port(struct rtnl_link *link, uint32_t *port)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	if (!port)
+		return -NLE_INVAL;
+
+	if (!(vxi->ce_mask & VXLAN_ATTR_PORT))
+		return -NLE_NOATTR;
+
+	*port = ntohs(vxi->vxi_port);
+
+	return 0;
+}
+
+/**
+ * Set UDP checksum status to use for VXLAN
+ * @arg link		Link object
+ * @arg csum		Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_udp_csum(struct rtnl_link *link, uint8_t csum)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	vxi->vxi_udp_csum = csum;
+	vxi->ce_mask |= VXLAN_ATTR_UDP_CSUM;
+
+	return 0;
+}
+
+/**
+ * Get UDP checksum status to use for VXLAN
+ * @arg link		Link object
+ *
+ * @return Status value on success or a negative error code
+ */
+int rtnl_link_vxlan_get_udp_csum(struct rtnl_link *link)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	if (!(vxi->ce_mask & VXLAN_ATTR_UDP_CSUM))
+		return -NLE_NOATTR;
+
+	return vxi->vxi_udp_csum;
+}
+
+/**
+ * Set skip UDP checksum transmitted over IPv6 status to use for VXLAN
+ * @arg link		Link object
+ * @arg csum		Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_udp_zero_csum6_tx(struct rtnl_link *link, uint8_t csum)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	vxi->vxi_udp_zero_csum6_tx = csum;
+	vxi->ce_mask |= VXLAN_ATTR_UDP_ZERO_CSUM6_TX;
+
+	return 0;
+}
+
+/**
+ * Get skip UDP checksum transmitted over IPv6 status to use for VXLAN
+ * @arg link		Link object
+ *
+ * @return Status value on success or a negative error code
+ */
+int rtnl_link_vxlan_get_udp_zero_csum6_tx(struct rtnl_link *link)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	if (!(vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_TX))
+		return -NLE_NOATTR;
+
+	return vxi->vxi_udp_zero_csum6_tx;
+}
+
+/**
+ * Set skip UDP checksum received over IPv6 status to use for VXLAN
+ * @arg link		Link object
+ * @arg csum		Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_udp_zero_csum6_rx(struct rtnl_link *link, uint8_t csum)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	vxi->vxi_udp_zero_csum6_rx = csum;
+	vxi->ce_mask |= VXLAN_ATTR_UDP_ZERO_CSUM6_RX;
+
+	return 0;
+}
+
+/**
+ * Get skip UDP checksum received over IPv6 status to use for VXLAN
+ * @arg link		Link object
+ *
+ * @return Status value on success or a negative error code
+ */
+int rtnl_link_vxlan_get_udp_zero_csum6_rx(struct rtnl_link *link)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	if (!(vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_RX))
+		return -NLE_NOATTR;
+
+	return vxi->vxi_udp_zero_csum6_rx;
+}
+
+/**
+ * Set remote offload transmit checksum status to use for VXLAN
+ * @arg link		Link object
+ * @arg csum		Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_remcsum_tx(struct rtnl_link *link, uint8_t csum)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	vxi->vxi_remcsum_tx = csum;
+	vxi->ce_mask |= VXLAN_ATTR_REMCSUM_TX;
+
+	return 0;
+}
+
+/**
+ * Get remote offload transmit checksum status to use for VXLAN
+ * @arg link		Link object
+ *
+ * @return Status value on success or a negative error code
+ */
+int rtnl_link_vxlan_get_remcsum_tx(struct rtnl_link *link)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	if (!(vxi->ce_mask & VXLAN_ATTR_REMCSUM_TX))
+		return -NLE_NOATTR;
+
+	return vxi->vxi_remcsum_tx;
+}
+
+/**
+ * Set remote offload receive checksum status to use for VXLAN
+ * @arg link		Link object
+ * @arg csum		Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_remcsum_rx(struct rtnl_link *link, uint8_t csum)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	vxi->vxi_remcsum_rx = csum;
+	vxi->ce_mask |= VXLAN_ATTR_REMCSUM_RX;
+
+	return 0;
+}
+
+/**
+ * Get remote offload receive checksum status to use for VXLAN
+ * @arg link		Link object
+ *
+ * @return Status value on success or a negative error code
+ */
+int rtnl_link_vxlan_get_remcsum_rx(struct rtnl_link *link)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	if (!(vxi->ce_mask & VXLAN_ATTR_REMCSUM_RX))
+		return -NLE_NOATTR;
+
+	return vxi->vxi_remcsum_rx;
+}
+
+/**
+ * Set collect metadata status to use for VXLAN
+ * @arg link		Link object
+ * @arg collect		Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_collect_metadata(struct rtnl_link *link, uint8_t collect)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	vxi->vxi_collect_metadata = collect;
+	vxi->ce_mask |= VXLAN_ATTR_COLLECT_METADATA;
+
+	return 0;
+}
+
+/**
+ * Get collect metadata status to use for VXLAN
+ * @arg link		Link object
+ *
+ * @return Status value on success or a negative error code
+ */
+int rtnl_link_vxlan_get_collect_metadata(struct rtnl_link *link)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	if (!(vxi->ce_mask & VXLAN_ATTR_COLLECT_METADATA))
+		return -NLE_NOATTR;
+
+	return vxi->vxi_collect_metadata;
+}
+
+/**
+ * Set flow label to use for VXLAN
+ * @arg link		Link object
+ * @arg label		Destination label
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_label(struct rtnl_link *link, uint32_t label)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	vxi->vxi_label = htonl(label);
+	vxi->ce_mask |= VXLAN_ATTR_LABEL;
+
+	return 0;
+}
+
+/**
+ * Get flow label to use for VXLAN
+ * @arg link		Link object
+ * @arg label		Pointer to store destination label
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_get_label(struct rtnl_link *link, uint32_t *label)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	if (!label)
+		return -NLE_INVAL;
+
+	if (!(vxi->ce_mask & VXLAN_ATTR_LABEL))
+		return -NLE_NOATTR;
+
+	*label = ntohl(vxi->vxi_label);
+
+	return 0;
+}
+
+/**
+ * Set VXLAN flags RTNL_LINK_VXLAN_F_*
+ * @arg link		Link object
+ * @flags               Which flags to set
+ * @arg enable		Boolean enabling or disabling flag
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_flags(struct rtnl_link *link, uint32_t flags, int enable)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	if (flags & ~(RTNL_LINK_VXLAN_F_GBP | RTNL_LINK_VXLAN_F_GPE | RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL))
+		return -NLE_INVAL;
+
+	if (enable)
+		vxi->vxi_flags |= flags;
+	else
+		vxi->vxi_flags &= ~flags;
+
+	return 0;
+}
+
+/**
+ * Get VXLAN flags RTNL_LINK_VXLAN_F_*
+ * @arg link		Link object
+ * @arg out_flags       Output value for flags. Must be present.
+ *
+ * @return Zero on success or a negative error code
+ */
+int rtnl_link_vxlan_get_flags(struct rtnl_link *link, uint32_t *out_flags)
+{
+	struct vxlan_info *vxi = link->l_info;
+
+	IS_VXLAN_LINK_ASSERT(link);
+
+	*out_flags = vxi->vxi_flags;
+	return 0;
+}
+
 /** @} */
 
 static void __init vxlan_init(void)
diff --git a/lib/route/link/xfrmi.c b/lib/route/link/xfrmi.c
new file mode 100644
index 0000000..5a4a563
--- /dev/null
+++ b/lib/route/link/xfrmi.c
@@ -0,0 +1,319 @@
+/*
+ * lib/route/link/xfrmi.c	 XFRMI Link Info
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2019 Eyal Birger <eyal.birger@gmail.com>
+ *
+ * Based on lib/route/link/ipvti.c
+ */
+
+/**
+ * @ingroup link
+ * @defgroup xfrmi XFRMI
+ * xfrmi link module
+ *
+ * @details
+ * \b Link Type Name: "xfrmi"
+ *
+ * @route_doc{link_xfrmi, XFRMI Documentation}
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink/route/link/xfrmi.h>
+#include <netlink-private/route/link/api.h>
+
+#define XFRMI_ATTR_LINK  (1 << 0)
+#define XFRMI_ATTR_IF_ID (1 << 1)
+
+#define XFRMI_LINK_TYPE_NAME "xfrm"
+
+struct xfrmi_info {
+	uint32_t link;
+	uint32_t if_id;
+	uint32_t xfrmi_mask;
+};
+
+static	struct nla_policy xfrmi_policy[IFLA_XFRM_MAX + 1] = {
+	[IFLA_XFRM_LINK]  = { .type = NLA_U32 },
+	[IFLA_XFRM_IF_ID] = { .type = NLA_U32 },
+};
+
+static int xfrmi_alloc(struct rtnl_link *link)
+{
+	struct xfrmi_info *xfrmi;
+
+	if (link->l_info)
+		memset(link->l_info, 0, sizeof(*xfrmi));
+	else {
+		xfrmi = calloc(1, sizeof(*xfrmi));
+		if (!xfrmi)
+			return -NLE_NOMEM;
+
+		link->l_info = xfrmi;
+	}
+
+	return 0;
+}
+
+static int xfrmi_parse(struct rtnl_link *link, struct nlattr *data,
+                       struct nlattr *xstats)
+{
+	struct nlattr *tb[IFLA_XFRM_MAX + 1];
+	struct xfrmi_info *xfrmi;
+	int err;
+
+	NL_DBG(3, "Parsing XFRMI link info\n");
+
+	err = nla_parse_nested(tb, IFLA_XFRM_MAX, data, xfrmi_policy);
+	if (err < 0)
+		return err;
+
+	err = xfrmi_alloc(link);
+	if (err < 0)
+		return err;
+
+	xfrmi = link->l_info;
+
+	if (tb[IFLA_XFRM_LINK]) {
+		xfrmi->link = nla_get_u32(tb[IFLA_XFRM_LINK]);
+		xfrmi->xfrmi_mask |= XFRMI_ATTR_LINK;
+	}
+
+	if (tb[IFLA_XFRM_IF_ID]) {
+		xfrmi->if_id = nla_get_u32(tb[IFLA_XFRM_IF_ID]);
+		xfrmi->xfrmi_mask |= XFRMI_ATTR_IF_ID;
+	}
+
+	return 0;
+}
+
+static int xfrmi_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+	struct xfrmi_info *xfrmi = link->l_info;
+	struct nlattr *data;
+
+	data = nla_nest_start(msg, IFLA_INFO_DATA);
+	if (!data)
+		return -NLE_MSGSIZE;
+
+	if (xfrmi->xfrmi_mask & XFRMI_ATTR_LINK)
+		NLA_PUT_U32(msg, IFLA_XFRM_LINK, xfrmi->link);
+
+	if (xfrmi->xfrmi_mask & XFRMI_ATTR_IF_ID)
+		NLA_PUT_U32(msg, IFLA_XFRM_IF_ID, xfrmi->if_id);
+
+	nla_nest_end(msg, data);
+
+nla_put_failure:
+	return 0;
+}
+
+static void xfrmi_free(struct rtnl_link *link)
+{
+	struct xfrmi_info *xfrmi = link->l_info;
+
+	free(xfrmi);
+	link->l_info = NULL;
+}
+
+static void xfrmi_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
+{
+	nl_dump(p, "xfrmi : %s", link->l_name);
+}
+
+static void xfrmi_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
+{
+	struct xfrmi_info *xfrmi = link->l_info;
+
+	if (xfrmi->xfrmi_mask & XFRMI_ATTR_LINK) {
+		struct rtnl_link *parent;
+		char *name;
+
+		nl_dump(p, "      link ");
+
+		name = NULL;
+		parent = link_lookup(link->ce_cache, xfrmi->link);
+		if (parent)
+			name = rtnl_link_get_name(parent);
+
+		if (name)
+			nl_dump_line(p, "%s\n", name);
+		else
+			nl_dump_line(p, "%u\n", xfrmi->link);
+	}
+
+	if (xfrmi->xfrmi_mask & XFRMI_ATTR_IF_ID) {
+		nl_dump(p, "      if_id   ");
+		nl_dump_line(p, "%x\n", xfrmi->if_id);
+	}
+}
+
+static int xfrmi_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+	struct xfrmi_info *xfrmi_dst, *xfrmi_src = src->l_info;
+	int err;
+
+	dst->l_info = NULL;
+
+	err = rtnl_link_set_type(dst, XFRMI_LINK_TYPE_NAME);
+	if (err < 0)
+		return err;
+
+	xfrmi_dst = dst->l_info;
+
+	if (!xfrmi_dst || !xfrmi_src)
+		BUG();
+
+	memcpy(xfrmi_dst, xfrmi_src, sizeof(struct xfrmi_info));
+
+	return 0;
+}
+
+static struct rtnl_link_info_ops xfrmi_info_ops = {
+	.io_name                = XFRMI_LINK_TYPE_NAME,
+	.io_alloc               = xfrmi_alloc,
+	.io_parse               = xfrmi_parse,
+	.io_dump = {
+		[NL_DUMP_LINE]  = xfrmi_dump_line,
+		[NL_DUMP_DETAILS] = xfrmi_dump_details,
+	},
+	.io_clone               = xfrmi_clone,
+	.io_put_attrs           = xfrmi_put_attrs,
+	.io_free                = xfrmi_free,
+};
+
+#define IS_XFRMI_LINK_ASSERT(link) do { \
+		if ((link)->l_info_ops != &xfrmi_info_ops) { \
+			APPBUG("Link is not a xfrmi link. set type \"xfrmi\" first."); \
+			return -NLE_OPNOTSUPP; \
+		} \
+	} while(0)
+
+struct rtnl_link *rtnl_link_xfrmi_alloc(void)
+{
+	struct rtnl_link *link;
+	int err;
+
+	link = rtnl_link_alloc();
+	if (!link)
+		return NULL;
+
+	err = rtnl_link_set_type(link, XFRMI_LINK_TYPE_NAME);
+	if (err < 0) {
+		rtnl_link_put(link);
+		return NULL;
+	}
+
+	return link;
+}
+
+/**
+ * Check if link is a XFRMI link
+ * @arg link            Link object
+ *
+ * @return True if link is a IXFRMI link, otherwise 0 is returned.
+ */
+int rtnl_link_is_xfrmi(struct rtnl_link *link)
+{
+	return link->l_info_ops && !strcmp(link->l_info_ops->io_name,
+					   XFRMI_LINK_TYPE_NAME);
+}
+
+/**
+ * Set XFRMI link interface index
+ * @arg link            Link object
+ * @arg index           interface index
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_xfrmi_set_link(struct rtnl_link *link, uint32_t index)
+{
+	struct xfrmi_info *xfrmi = link->l_info;
+
+	IS_XFRMI_LINK_ASSERT(link);
+
+	xfrmi->link = index;
+	xfrmi->xfrmi_mask |= XFRMI_ATTR_LINK;
+
+	return 0;
+}
+
+/**
+ * Get XFRMI link interface index
+ * @arg link            Link object
+ * @arg out_link        The output value on success
+ *
+ * @return 0 on sucess or a negative error code
+ */
+int rtnl_link_xfrmi_get_link(struct rtnl_link *link, uint32_t *out_link)
+{
+	struct xfrmi_info *xfrmi = link->l_info;
+
+	IS_XFRMI_LINK_ASSERT(link);
+
+	if (!(xfrmi->xfrmi_mask & XFRMI_ATTR_LINK))
+		return -NLE_NOATTR;
+
+	*out_link = xfrmi->link;
+	return 0;
+}
+
+/**
+ * Set XFRMI if_id
+ * @arg link            Link object
+ * @arg if_id            xfrm if_id
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_xfrmi_set_if_id(struct rtnl_link *link, uint32_t if_id)
+{
+	struct xfrmi_info *xfrmi = link->l_info;
+
+	IS_XFRMI_LINK_ASSERT(link);
+
+	xfrmi->if_id = if_id;
+	xfrmi->xfrmi_mask |= XFRMI_ATTR_IF_ID;
+
+	return 0;
+}
+
+/**
+ * Get XFRMI if_id
+ * @arg link            Link object
+ * @arg out_if_id       The output value on success
+ *
+ * @return 0 on sucess or a negative error code
+ */
+int rtnl_link_xfrmi_get_if_id(struct rtnl_link *link, uint32_t *out_if_id)
+{
+	struct xfrmi_info *xfrmi = link->l_info;
+
+	IS_XFRMI_LINK_ASSERT(link);
+
+	if (!(xfrmi->xfrmi_mask & XFRMI_ATTR_IF_ID))
+		return -NLE_NOATTR;
+
+	*out_if_id = xfrmi->if_id;
+	return 0;
+}
+
+static void __init xfrmi_init(void)
+{
+	rtnl_link_register_info(&xfrmi_info_ops);
+}
+
+static void __exit xfrmi_exit(void)
+{
+	rtnl_link_unregister_info(&xfrmi_info_ops);
+}
diff --git a/lib/route/neigh.c b/lib/route/neigh.c
index ad26b4d..ca4f2b6 100644
--- a/lib/route/neigh.c
+++ b/lib/route/neigh.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/neigh.c	Neighbours
  *
@@ -35,6 +36,7 @@
  * NTF_USE
  * NTF_PROXY
  * NTF_ROUTER
+ * NTF_SELF
  * @endcode
  *
  * @par Neighbour Identification
@@ -67,7 +69,7 @@
  * // Neighbours can then be looked up by the interface and destination
  * // address:
  * struct rtnl_neigh *neigh = rtnl_neigh_get(cache, ifindex, dst_addr);
- * 
+ *
  * // After successful usage, the object must be given back to the cache
  * rtnl_neigh_put(neigh);
  * @endcode
@@ -168,6 +170,7 @@
 #define NEIGH_ATTR_TYPE         0x80
 #define NEIGH_ATTR_PROBES       0x100
 #define NEIGH_ATTR_MASTER       0x200
+#define NEIGH_ATTR_VLAN         0x400
 
 static struct nl_cache_ops rtnl_neigh_ops;
 static struct nl_object_ops neigh_obj_ops;
@@ -209,6 +212,7 @@
 	struct neigh_hash_key {
 		uint32_t	n_family;
 		uint32_t	n_ifindex;
+		uint16_t	n_vlan;
 		char		n_addr[0];
 	} __attribute__((packed)) *nkey;
 #ifdef NL_DEBUG
@@ -232,10 +236,15 @@
 		return;
 	}
 	nkey->n_family = neigh->n_family;
-	if (neigh->n_family == AF_BRIDGE)
-		nkey->n_ifindex = neigh->n_master;
-	else
+	if (neigh->n_family == AF_BRIDGE) {
+		nkey->n_vlan = neigh->n_vlan;
+		if (neigh->n_flags & NTF_SELF)
+			nkey->n_ifindex = neigh->n_ifindex;
+		else
+			nkey->n_ifindex = neigh->n_master;
+	} else
 		nkey->n_ifindex = neigh->n_ifindex;
+
 	if (addr)
 		memcpy(nkey->n_addr,
 			nl_addr_get_binary_addr(addr),
@@ -253,12 +262,12 @@
 	return;
 }
 
-static int neigh_compare(struct nl_object *_a, struct nl_object *_b,
-			uint32_t attrs, int flags)
+static uint64_t neigh_compare(struct nl_object *_a, struct nl_object *_b,
+			      uint64_t attrs, int flags)
 {
 	struct rtnl_neigh *a = (struct rtnl_neigh *) _a;
 	struct rtnl_neigh *b = (struct rtnl_neigh *) _b;
-	int diff = 0;
+	uint64_t diff = 0;
 
 #define NEIGH_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NEIGH_ATTR_##ATTR, a, b, EXPR)
 
@@ -268,6 +277,7 @@
 	diff |= NEIGH_DIFF(LLADDR,	nl_addr_cmp(a->n_lladdr, b->n_lladdr));
 	diff |= NEIGH_DIFF(DST,		nl_addr_cmp(a->n_dst, b->n_dst));
 	diff |= NEIGH_DIFF(MASTER,	a->n_master != b->n_master);
+	diff |= NEIGH_DIFF(VLAN,	a->n_vlan != b->n_vlan);
 
 	if (flags & LOOSE_COMPARISON) {
 		diff |= NEIGH_DIFF(STATE,
@@ -285,15 +295,17 @@
 }
 
 static const struct trans_tbl neigh_attrs[] = {
-	__ADD(NEIGH_ATTR_FLAGS, flags)
-	__ADD(NEIGH_ATTR_STATE, state)
-	__ADD(NEIGH_ATTR_LLADDR, lladdr)
-	__ADD(NEIGH_ATTR_DST, dst)
-	__ADD(NEIGH_ATTR_CACHEINFO, cacheinfo)
-	__ADD(NEIGH_ATTR_IFINDEX, ifindex)
-	__ADD(NEIGH_ATTR_FAMILY, family)
-	__ADD(NEIGH_ATTR_TYPE, type)
-	__ADD(NEIGH_ATTR_PROBES, probes)
+	__ADD(NEIGH_ATTR_FLAGS, flags),
+	__ADD(NEIGH_ATTR_STATE, state),
+	__ADD(NEIGH_ATTR_LLADDR, lladdr),
+	__ADD(NEIGH_ATTR_DST, dst),
+	__ADD(NEIGH_ATTR_CACHEINFO, cacheinfo),
+	__ADD(NEIGH_ATTR_IFINDEX, ifindex),
+	__ADD(NEIGH_ATTR_FAMILY, family),
+	__ADD(NEIGH_ATTR_TYPE, type),
+	__ADD(NEIGH_ATTR_PROBES, probes),
+	__ADD(NEIGH_ATTR_MASTER, master),
+	__ADD(NEIGH_ATTR_VLAN, vlan),
 };
 
 static char *neigh_attrs2str(int attrs, char *buf, size_t len)
@@ -306,10 +318,15 @@
 {
 	struct rtnl_neigh *neigh = (struct rtnl_neigh *)obj;
 
-	if (neigh->n_family == AF_BRIDGE)
-		return (NEIGH_ATTR_LLADDR | NEIGH_ATTR_FAMILY | NEIGH_ATTR_MASTER);
-	else
-		return (NEIGH_ATTR_IFINDEX | NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY);
+	if (neigh->n_family == AF_BRIDGE) {
+		if (neigh->n_flags & NTF_SELF)
+			return (NEIGH_ATTR_LLADDR | NEIGH_ATTR_FAMILY | NEIGH_ATTR_IFINDEX |
+				       ((neigh->ce_mask & NEIGH_ATTR_DST) ? NEIGH_ATTR_DST: 0) |
+				       ((neigh->ce_mask & NEIGH_ATTR_VLAN) ? NEIGH_ATTR_VLAN : 0));
+		else
+			return (NEIGH_ATTR_LLADDR | NEIGH_ATTR_FAMILY | NEIGH_ATTR_MASTER | NEIGH_ATTR_VLAN);
+	} else
+		return neigh_obj_ops.oo_id_attrs;
 }
 
 static struct nla_policy neigh_policy[NDA_MAX+1] = {
@@ -375,11 +392,13 @@
 	}
 
 	if (tb[NDA_DST]) {
-		neigh->n_dst = nl_addr_alloc_attr(tb[NDA_DST], neigh->n_family);
+		neigh->n_dst = nl_addr_alloc_attr(tb[NDA_DST], AF_UNSPEC);
 		if (!neigh->n_dst) {
 			err = -NLE_NOMEM;
 			goto errout;
 		}
+		nl_addr_set_family(neigh->n_dst,
+				   nl_addr_guess_family(neigh->n_dst));
 		neigh->ce_mask |= NEIGH_ATTR_DST;
 	}
 
@@ -390,7 +409,7 @@
 		neigh->n_cacheinfo.nci_used = ci->ndm_used;
 		neigh->n_cacheinfo.nci_updated = ci->ndm_updated;
 		neigh->n_cacheinfo.nci_refcnt = ci->ndm_refcnt;
-		
+
 		neigh->ce_mask |= NEIGH_ATTR_CACHEINFO;
 	}
 
@@ -399,21 +418,30 @@
 		neigh->ce_mask |= NEIGH_ATTR_PROBES;
 	}
 
+	if (tb[NDA_VLAN]) {
+		neigh->n_vlan = nla_get_u16(tb[NDA_VLAN]);
+		neigh->ce_mask |= NEIGH_ATTR_VLAN;
+	}
+
 	/*
 	 * Get the bridge index for AF_BRIDGE family entries
 	 */
 	if (neigh->n_family == AF_BRIDGE) {
-		struct nl_cache *lcache = nl_cache_mngt_require_safe("route/link");
-		if (lcache ) {
-			struct rtnl_link *link = rtnl_link_get(lcache,
-							neigh->n_ifindex);
-			if (link) {
-				neigh->n_master = link->l_master;
-				rtnl_link_put(link);
-				neigh->ce_mask |= NEIGH_ATTR_MASTER;
+		if (tb[NDA_MASTER]) {
+			neigh->n_master = nla_get_u32(tb[NDA_MASTER]);
+			neigh->ce_mask |= NEIGH_ATTR_MASTER;
+		} else {
+			struct nl_cache *lcache = nl_cache_mngt_require_safe("route/link");
+			if (lcache ) {
+				struct rtnl_link *link = rtnl_link_get(lcache,
+								       neigh->n_ifindex);
+				if (link) {
+					neigh->n_master = link->l_master;
+					rtnl_link_put(link);
+					neigh->ce_mask |= NEIGH_ATTR_MASTER;
+				}
+				nl_cache_put(lcache);
 			}
-
-			nl_cache_put(lcache);
 		}
 	}
 
@@ -429,7 +457,31 @@
 {
 	int family = c->c_iarg1;
 
-	return nl_rtgen_request(h, RTM_GETNEIGH, family, NLM_F_DUMP);
+	if (family == AF_UNSPEC) {
+		return nl_rtgen_request(h, RTM_GETNEIGH, family, NLM_F_DUMP);
+	} else if (family == AF_BRIDGE) {
+		struct ifinfomsg hdr = {.ifi_family = family};
+		struct nl_msg *msg;
+		int err;
+
+		msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP);
+		if (!msg)
+			return -NLE_NOMEM;
+
+		err = -NLE_MSGSIZE;
+		if (nlmsg_append(msg, &hdr, sizeof(hdr), NLMSG_ALIGNTO) < 0)
+			goto nla_put_failure;
+
+		err = nl_send_auto(h, msg);
+		if (err > 0)
+			err = 0;
+
+	nla_put_failure:
+		nlmsg_free(msg);
+		return err;
+	}
+
+	return -NLE_INVAL;
 }
 
 
@@ -439,10 +491,14 @@
 	struct rtnl_neigh *n = (struct rtnl_neigh *) a;
 	struct nl_cache *link_cache;
 	char state[128], flags[64];
+	char buf[128];
 
 	link_cache = nl_cache_mngt_require_safe("route/link");
 
-	if (n->n_family != AF_BRIDGE)
+	if (n->n_family != AF_UNSPEC)
+		nl_dump_line(p, "%s ", nl_af2str(n->n_family, buf, sizeof(buf)));
+
+	if (n->ce_mask & NEIGH_ATTR_DST)
 		nl_dump_line(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst)));
 
 	if (link_cache)
@@ -456,6 +512,17 @@
 		nl_dump(p, "lladdr %s ",
 			nl_addr2str(n->n_lladdr, lladdr, sizeof(lladdr)));
 
+	if (n->ce_mask & NEIGH_ATTR_VLAN)
+		nl_dump(p, "vlan %d ", n->n_vlan);
+
+	if (n->ce_mask & NEIGH_ATTR_MASTER) {
+		if (link_cache)
+			nl_dump(p, "%s ", rtnl_link_i2name(link_cache, n->n_master,
+							   state, sizeof(state)));
+		else
+			nl_dump(p, "%d ", n->n_master);
+	}
+
 	rtnl_neigh_state2str(n->n_state, state, sizeof(state));
 	rtnl_neigh_flags2str(n->n_flags, flags, sizeof(flags));
 
@@ -530,6 +597,38 @@
 }
 
 /**
+ * Build a neighbour cache including all neighbours currently configured in the kernel.
+ * @arg sock		Netlink socket.
+ * @arg result		Pointer to store resulting cache.
+ * @arg flags		Flags to apply to cache before filling
+ *
+ * Allocates a new neighbour cache, initializes it properly and updates it
+ * to include all neighbours currently configured in the kernel.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_neigh_alloc_cache_flags(struct nl_sock *sock, struct nl_cache **result,
+				 unsigned int flags)
+{
+	struct nl_cache * cache;
+	int err;
+
+	cache = nl_cache_alloc(&rtnl_neigh_ops);
+	if (!cache)
+		return -NLE_NOMEM;
+
+	nl_cache_set_flags(cache, flags);
+
+	if (sock && (err = nl_cache_refill(sock, cache)) < 0) {
+		nl_cache_free(cache);
+		return err;
+	}
+
+	*result = cache;
+	return 0;
+}
+
+/**
  * Look up a neighbour by interface index and destination address
  * @arg cache		neighbour cache
  * @arg ifindex		interface index the neighbour is on
@@ -544,6 +643,7 @@
 
 	nl_list_for_each_entry(neigh, &cache->c_items, ce_list) {
 		if (neigh->n_ifindex == ifindex &&
+		    neigh->n_family == dst->a_family &&
 		    !nl_addr_cmp(neigh->n_dst, dst)) {
 			nl_object_get((struct nl_object *) neigh);
 			return neigh;
@@ -553,6 +653,32 @@
 	return NULL;
 }
 
+/**
+ * Look up a neighbour by interface index, link layer address and vlan id
+ * @arg cache		neighbour cache
+ * @arg ifindex 	interface index the neighbour is on
+ * @arg lladdr		link layer address of the neighbour
+ * @arg vlan		vlan id of the neighbour
+ *
+ * @return neighbour handle or NULL if no match was found.
+ */
+struct rtnl_neigh * rtnl_neigh_get_by_vlan(struct nl_cache *cache, int ifindex,
+					   struct nl_addr *lladdr, int vlan)
+{
+	struct rtnl_neigh *neigh;
+
+	nl_list_for_each_entry(neigh, &cache->c_items, ce_list) {
+		if (neigh->n_ifindex == ifindex &&
+		    neigh->n_vlan == vlan &&
+		    neigh->n_lladdr && !nl_addr_cmp(neigh->n_lladdr, lladdr)) {
+			nl_object_get((struct nl_object *) neigh);
+			return neigh;
+		}
+	}
+
+	return NULL;
+}
+
 /** @} */
 
 /**
@@ -596,6 +722,9 @@
 	if (tmpl->ce_mask & NEIGH_ATTR_LLADDR)
 		NLA_PUT_ADDR(msg, NDA_LLADDR, tmpl->n_lladdr);
 
+	if (tmpl->ce_mask & NEIGH_ATTR_VLAN)
+		NLA_PUT_U16(msg, NDA_VLAN, tmpl->n_vlan);
+
 	*result = msg;
 	return 0;
 
@@ -615,7 +744,7 @@
  * all relevant fields and must thus be sent out via nl_send_auto_complete()
  * or supplemented as needed. \a tmpl must contain the attributes of the new
  * neighbour set via \c rtnl_neigh_set_* functions.
- * 
+ *
  * The following attributes must be set in the template:
  *  - Interface index (rtnl_neigh_set_ifindex())
  *  - State (rtnl_neigh_set_state())
@@ -652,7 +781,7 @@
 {
 	int err;
 	struct nl_msg *msg;
-	
+
 	if ((err = rtnl_neigh_build_add_request(tmpl, flags, &msg)) < 0)
 		return err;
 
@@ -708,7 +837,7 @@
 {
 	struct nl_msg *msg;
 	int err;
-	
+
 	if ((err = rtnl_neigh_build_delete_request(neigh, flags, &msg)) < 0)
 		return err;
 
@@ -728,20 +857,25 @@
  */
 
 static const struct trans_tbl neigh_states[] = {
-	__ADD(NUD_INCOMPLETE, incomplete)
-	__ADD(NUD_REACHABLE, reachable)
-	__ADD(NUD_STALE, stale)
-	__ADD(NUD_DELAY, delay)
-	__ADD(NUD_PROBE, probe)
-	__ADD(NUD_FAILED, failed)
-	__ADD(NUD_NOARP, norarp)
-	__ADD(NUD_PERMANENT, permanent)
+	__ADD(NUD_INCOMPLETE, incomplete),
+	__ADD(NUD_REACHABLE, reachable),
+	__ADD(NUD_STALE, stale),
+	__ADD(NUD_DELAY, delay),
+	__ADD(NUD_PROBE, probe),
+	__ADD(NUD_FAILED, failed),
+	__ADD(NUD_NOARP, noarp),
+	__ADD(NUD_PERMANENT, permanent),
+
+	/* Accept this value for backward compatibility. Originally
+	 * there was a typo in the string value. This was fixed later,
+	 * but we still want to successfully parse "norarp". */
+	__ADD(NUD_NOARP, norarp),
 };
 
 char * rtnl_neigh_state2str(int state, char *buf, size_t len)
 {
 	return __flags2str(state, buf, len, neigh_states,
-	    ARRAY_SIZE(neigh_states));
+	    ARRAY_SIZE(neigh_states) - 1);
 }
 
 int rtnl_neigh_str2state(const char *name)
@@ -757,9 +891,13 @@
  */
 
 static const struct trans_tbl neigh_flags[] = {
-	__ADD(NTF_USE, use)
-	__ADD(NTF_PROXY, proxy)
-	__ADD(NTF_ROUTER, router)
+	__ADD(NTF_USE, use),
+	__ADD(NTF_PROXY, proxy),
+	__ADD(NTF_ROUTER, router),
+	__ADD(NTF_SELF, self),
+	__ADD(NTF_MASTER, master),
+	__ADD(NTF_EXT_LEARNED, ext_learned),
+	__ADD(NTF_OFFLOADED, offloaded),
 };
 
 char * rtnl_neigh_flags2str(int flags, char *buf, size_t len)
@@ -908,6 +1046,30 @@
 		return -1;
 }
 
+void rtnl_neigh_set_vlan(struct rtnl_neigh *neigh, int vlan)
+{
+	neigh->n_vlan = vlan;
+	neigh->ce_mask |= NEIGH_ATTR_VLAN;
+}
+
+int rtnl_neigh_get_vlan(struct rtnl_neigh *neigh)
+{
+	if (neigh->ce_mask & NEIGH_ATTR_VLAN)
+		return neigh->n_vlan;
+	else
+		return -1;
+}
+
+void rtnl_neigh_set_master(struct rtnl_neigh *neigh, int ifindex)
+{
+	neigh->n_master = ifindex;
+	neigh->ce_mask |= NEIGH_ATTR_MASTER;
+}
+
+int rtnl_neigh_get_master(struct rtnl_neigh *neigh) {
+	return neigh->n_master;
+}
+
 /** @} */
 
 static struct nl_object_ops neigh_obj_ops = {
diff --git a/lib/route/neightbl.c b/lib/route/neightbl.c
index f9c9c27..96ca44a 100644
--- a/lib/route/neightbl.c
+++ b/lib/route/neightbl.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/neightbl.c         neighbour tables
  *
@@ -54,12 +55,12 @@
 static struct nl_object_ops neightbl_obj_ops;
 /** @endcond */
 
-static int neightbl_compare(struct nl_object *_a, struct nl_object *_b,
-			uint32_t attrs, int flags)
+static uint64_t neightbl_compare(struct nl_object *_a, struct nl_object *_b,
+				 uint64_t attrs, int flags)
 {
 	struct rtnl_neightbl *a = (struct rtnl_neightbl *) _a;
 	struct rtnl_neightbl *b = (struct rtnl_neightbl *) _b;
-	int diff = 0;
+	uint64_t diff = 0;
 
 #define NT_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NEIGHTBL_ATTR_##ATTR, a, b, EXPR)
 
@@ -135,7 +136,7 @@
 
 	ntbl->ce_msgtype = n->nlmsg_type;
 	rtmsg = nlmsg_data(n);
-	
+
 	err = nlmsg_parse(n, sizeof(*rtmsg), tb, NDTA_MAX, neightbl_policy);
 	if (err < 0)
 		goto errout;
@@ -236,7 +237,7 @@
 
 	if (ntbl->nt_parms.ntp_mask & NEIGHTBLPARM_ATTR_IFINDEX) {
 		struct nl_cache *link_cache;
-		
+
 		link_cache = nl_cache_mngt_require_safe("route/link");
 
 		if (link_cache) {
@@ -278,10 +279,10 @@
 			ntbl->nt_config.ndtc_key_len,
 			ntbl->nt_config.ndtc_entry_size,
 			nl_msec2str(ntbl->nt_config.ndtc_last_flush,
-				      x, sizeof(x)));
+				    x, sizeof(x)));
 
 		nl_dump_line(p, "    gc threshold %u/%u/%u interval %s " \
-			    "chain-position %u\n",
+				"chain-position %u\n",
 			ntbl->nt_gc_thresh1, ntbl->nt_gc_thresh2,
 			ntbl->nt_gc_thresh3,
 			nl_msec2str(ntbl->nt_gc_interval, x, sizeof(x)),
@@ -291,33 +292,33 @@
 			ntbl->nt_config.ndtc_hash_rnd,
 			ntbl->nt_config.ndtc_hash_mask,
 			nl_msec2str(ntbl->nt_config.ndtc_last_rand,
-				      x, sizeof(x)));
+				    x, sizeof(x)));
 	}
 
 	if (ntbl->ce_mask & NEIGHTBL_ATTR_PARMS) {
 		struct rtnl_neightbl_parms *pa = &ntbl->nt_parms;
 
 		nl_dump_line(p, "    refcnt %u pending-queue-limit %u " \
-			    "proxy-delayed-queue-limit %u\n",
+				"proxy-delayed-queue-limit %u\n",
 			pa->ntp_refcnt,
 			pa->ntp_queue_len,
 			pa->ntp_proxy_qlen);
 
 		nl_dump_line(p, "    num-userspace-probes %u num-unicast-probes " \
-			    "%u num-multicast-probes %u\n",
+				"%u num-multicast-probes %u\n",
 			pa->ntp_app_probes,
 			pa->ntp_ucast_probes,
 			pa->ntp_mcast_probes);
 
 		nl_dump_line(p, "    min-age %s base-reachable-time %s " \
-			    "stale-check-interval %s\n",
+				"stale-check-interval %s\n",
 			nl_msec2str(pa->ntp_locktime, x, sizeof(x)),
 			nl_msec2str(pa->ntp_base_reachable_time,
-				      y, sizeof(y)),
+				    y, sizeof(y)),
 			nl_msec2str(pa->ntp_gc_stale_time, z, sizeof(z)));
 
 		nl_dump_line(p, "    initial-probe-delay %s answer-delay %s " \
-			    "proxy-answer-delay %s\n",
+				"proxy-answer-delay %s\n",
 			nl_msec2str(pa->ntp_probe_delay, x, sizeof(x)),
 			nl_msec2str(pa->ntp_anycast_delay, y, sizeof(y)),
 			nl_msec2str(pa->ntp_proxy_delay, z, sizeof(z)));
@@ -334,12 +335,12 @@
 		return;
 
 	nl_dump_line(p, "   " \
-                    " lookups %" PRIu64 \
-                    " hits %" PRIu64 \
-                    " failed %" PRIu64 \
-		    " allocations %" PRIu64 \
-                    " destroys %" PRIu64 \
-                    "\n",
+			" lookups %" PRIu64 \
+			" hits %" PRIu64 \
+			" failed %" PRIu64 \
+			" allocations %" PRIu64 \
+			" destroys %" PRIu64 \
+			"\n",
 		ntbl->nt_stats.ndts_lookups,
 		ntbl->nt_stats.ndts_hits,
 		ntbl->nt_stats.ndts_res_failed,
@@ -347,18 +348,18 @@
 		ntbl->nt_stats.ndts_destroys);
 
 	nl_dump_line(p, "   " \
-                        " hash-grows %" PRIu64 \
-                        " forced-gc-runs %" PRIu64 \
-                        " periodic-gc-runs %" PRIu64 \
-                        "\n",
+			" hash-grows %" PRIu64 \
+			" forced-gc-runs %" PRIu64 \
+			" periodic-gc-runs %" PRIu64 \
+			"\n",
 		ntbl->nt_stats.ndts_hash_grows,
 		ntbl->nt_stats.ndts_forced_gc_runs,
 		ntbl->nt_stats.ndts_periodic_gc_runs);
 
 	nl_dump_line(p, "   " \
-                        " rcv-unicast-probes %" PRIu64 \
-                        " rcv-multicast-probes %" PRIu64 \
-                        "\n",
+			" rcv-unicast-probes %" PRIu64 \
+			" rcv-multicast-probes %" PRIu64 \
+			"\n",
 		ntbl->nt_stats.ndts_rcv_probes_ucast,
 		ntbl->nt_stats.ndts_rcv_probes_mcast);
 }
diff --git a/lib/route/netconf.c b/lib/route/netconf.c
new file mode 100644
index 0000000..a11ad0e
--- /dev/null
+++ b/lib/route/netconf.c
@@ -0,0 +1,584 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * lib/route/netconf.c		netconf
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2017 David Ahern <dsa@cumulusnetworks.com>
+ */
+
+/**
+ * @ingroup rtnl
+ * @defgroup netconf Netconf
+ * @brief
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/route/netconf.h>
+#include <linux/netconf.h>
+#include <linux/socket.h>
+#include <netlink/hashtable.h>
+
+/** @cond SKIP */
+#define NETCONF_ATTR_FAMILY		0x0001
+#define NETCONF_ATTR_IFINDEX		0x0002
+#define NETCONF_ATTR_RP_FILTER		0x0004
+#define NETCONF_ATTR_FWDING		0x0008
+#define NETCONF_ATTR_MC_FWDING		0x0010
+#define NETCONF_ATTR_PROXY_NEIGH	0x0020
+#define NETCONF_ATTR_IGNORE_RT_LINKDWN	0x0040
+#define NETCONF_ATTR_INPUT		0x0080
+
+struct rtnl_netconf
+{
+	NLHDR_COMMON
+
+	int	family;
+	int	ifindex;
+	int	rp_filter;
+	int	forwarding;
+	int	mc_forwarding;
+	int	proxy_neigh;
+	int	ignore_routes_linkdown;
+	int	input;
+};
+
+static struct nl_cache_ops rtnl_netconf_ops;
+static struct nl_object_ops netconf_obj_ops;
+/** @endcond */
+
+static struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
+	[NETCONFA_IFINDEX]	 = { .type = NLA_S32 },
+	[NETCONFA_FORWARDING]	 = { .type = NLA_S32 },
+	[NETCONFA_MC_FORWARDING] = { .type = NLA_S32 },
+	[NETCONFA_RP_FILTER]	 = { .type = NLA_S32 },
+	[NETCONFA_PROXY_NEIGH]	 = { .type = NLA_S32 },
+	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]  = { .type = NLA_S32 },
+};
+
+static struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = {
+	[NETCONFA_IFINDEX]	 = { .type = NLA_S32 },
+	[NETCONFA_FORWARDING]	 = { .type = NLA_S32 },
+	[NETCONFA_MC_FORWARDING] = { .type = NLA_S32 },
+	[NETCONFA_PROXY_NEIGH]	 = { .type = NLA_S32 },
+	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]  = { .type = NLA_S32 },
+};
+
+static struct nla_policy devconf_mpls_policy[NETCONFA_MAX+1] = {
+	[NETCONFA_IFINDEX]	 = { .type = NLA_S32 },
+	[NETCONFA_INPUT]	 = { .type = NLA_S32 },
+};
+
+static struct rtnl_netconf *rtnl_netconf_alloc(void)
+{
+	return (struct rtnl_netconf *) nl_object_alloc(&netconf_obj_ops);
+}
+
+static int netconf_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+	struct rtnl_netconf *dst = nl_object_priv(_dst);
+	struct rtnl_netconf *src = nl_object_priv(_src);
+
+	*dst = *src;
+
+	return 0;
+}
+
+static int netconf_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+			      struct nlmsghdr *nlh, struct nl_parser_param *pp)
+{
+	struct nlattr *tb[NETCONFA_MAX+1], *attr;
+	struct rtnl_netconf *nc;
+	struct netconfmsg *ncm;
+	int err;
+
+	ncm = nlmsg_data(nlh);
+	switch (ncm->ncm_family) {
+	case AF_INET:
+		err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
+				  devconf_ipv4_policy);
+		if (err < 0)
+			return err;
+		break;
+	case AF_INET6:
+		err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
+				  devconf_ipv6_policy);
+		if (err < 0)
+			return err;
+		break;
+	case AF_MPLS:
+		err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
+				  devconf_mpls_policy);
+		if (err < 0)
+			return err;
+		break;
+	default:
+		printf("unexpected netconf family: %d\n", ncm->ncm_family);
+		return -1;
+	}
+
+	if (!tb[NETCONFA_IFINDEX])
+		return -1;
+
+	nc = rtnl_netconf_alloc();
+	if (!nc)
+		return -NLE_NOMEM;
+
+	nc->ce_msgtype = nlh->nlmsg_type;
+	nc->family = ncm->ncm_family;
+	nc->ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
+
+	nc->ce_mask = NETCONF_ATTR_FAMILY | NETCONF_ATTR_IFINDEX;
+
+
+	if (tb[NETCONFA_RP_FILTER]) {
+		attr = tb[NETCONFA_RP_FILTER];
+		nc->rp_filter = nla_get_s32(attr);
+		nc->ce_mask |= NETCONF_ATTR_RP_FILTER;
+	}
+
+	if (tb[NETCONFA_FORWARDING]) {
+		attr = tb[NETCONFA_FORWARDING];
+		nc->forwarding = nla_get_s32(attr);
+		nc->ce_mask |= NETCONF_ATTR_FWDING;
+	}
+
+	if (tb[NETCONFA_MC_FORWARDING]) {
+		attr = tb[NETCONFA_MC_FORWARDING];
+		nc->mc_forwarding = nla_get_s32(attr);
+		nc->ce_mask |= NETCONF_ATTR_MC_FWDING;
+	}
+
+	if (tb[NETCONFA_PROXY_NEIGH]) {
+		attr = tb[NETCONFA_PROXY_NEIGH];
+		nc->proxy_neigh = nla_get_s32(attr);
+		nc->ce_mask |= NETCONF_ATTR_PROXY_NEIGH;
+	}
+
+	if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]) {
+		attr = tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN];
+		nc->ignore_routes_linkdown = nla_get_s32(attr);
+		nc->ce_mask |= NETCONF_ATTR_IGNORE_RT_LINKDWN;
+	}
+
+	if (tb[NETCONFA_INPUT]) {
+		attr = tb[NETCONFA_INPUT];
+		nc->input = nla_get_s32(attr);
+		nc->ce_mask |= NETCONF_ATTR_INPUT;
+	}
+
+	err = pp->pp_cb((struct nl_object *) nc, pp);
+
+	rtnl_netconf_put(nc);
+	return err;
+}
+
+static int netconf_request_update(struct nl_cache *cache, struct nl_sock *sk)
+{
+	struct netconfmsg nc = {
+		.ncm_family = cache->c_iarg1,
+	};
+
+	return nl_send_simple(sk, RTM_GETNETCONF, NLM_F_DUMP, &nc, sizeof(nc));
+}
+
+static void netconf_dump_line(struct nl_object *obj, struct nl_dump_params *p)
+{
+	struct rtnl_netconf *nc = (struct rtnl_netconf *) obj;
+	struct nl_cache *link_cache;
+	char buf[64];
+
+	switch(nc->family) {
+	case AF_INET:
+		nl_dump(p, "ipv4 ");
+		break;
+	case AF_INET6:
+		nl_dump(p, "ipv6 ");
+		break;
+	case AF_MPLS:
+		nl_dump(p, "mpls ");
+		break;
+	default:
+		return;
+	}
+
+	switch(nc->ifindex) {
+	case NETCONFA_IFINDEX_ALL:
+		nl_dump(p, "all ");
+		break;
+	case NETCONFA_IFINDEX_DEFAULT:
+		nl_dump(p, "default ");
+		break;
+	default:
+		link_cache = nl_cache_mngt_require_safe("route/link");
+		if (link_cache) {
+			nl_dump(p, "dev %s ",
+				rtnl_link_i2name(link_cache, nc->ifindex,
+						 buf, sizeof(buf)));
+			nl_cache_put(link_cache);
+		} else
+			nl_dump(p, "dev %d ", nc->ifindex);
+	}
+
+	if (nc->ce_mask & NETCONF_ATTR_FWDING) {
+		nl_dump(p, "forwarding %s ",
+			nc->forwarding ? "on" : "off");
+	}
+
+	if (nc->ce_mask & NETCONF_ATTR_RP_FILTER) {
+		if (nc->rp_filter == 0)
+			nl_dump(p, "rp_filter off ");
+		else if (nc->rp_filter == 1)
+			nl_dump(p, "rp_filter strict ");
+		else if (nc->rp_filter == 2)
+			nl_dump(p, "rp_filter loose ");
+		else
+			nl_dump(p, "rp_filter unknown-mode ");
+	}
+
+	if (nc->ce_mask & NETCONF_ATTR_MC_FWDING) {
+		nl_dump(p, "mc_forwarding %s ",
+			nc->mc_forwarding ? "on" : "off");
+	}
+
+	if (nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH)
+		nl_dump(p, "proxy_neigh %d ", nc->proxy_neigh);
+
+	if (nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN) {
+		nl_dump(p, "ignore_routes_with_linkdown %s ",
+			nc->ignore_routes_linkdown ? "on" : "off");
+	}
+
+	if (nc->ce_mask & NETCONF_ATTR_INPUT)
+		nl_dump(p, "input %s ", nc->input ? "on" : "off");
+
+	nl_dump(p, "\n");
+}
+
+static const struct trans_tbl netconf_attrs[] = {
+	__ADD(NETCONF_ATTR_FAMILY, family),
+	__ADD(NETCONF_ATTR_IFINDEX, ifindex),
+	__ADD(NETCONF_ATTR_RP_FILTER, rp_filter),
+	__ADD(NETCONF_ATTR_FWDING, forwarding),
+	__ADD(NETCONF_ATTR_MC_FWDING, mc_forwarding),
+	__ADD(NETCONF_ATTR_PROXY_NEIGH, proxy_neigh),
+	__ADD(NETCONF_ATTR_IGNORE_RT_LINKDWN, ignore_routes_with_linkdown),
+	__ADD(NETCONF_ATTR_INPUT, input),
+};
+
+static char *netconf_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, netconf_attrs,
+			   ARRAY_SIZE(netconf_attrs));
+}
+
+static void netconf_keygen(struct nl_object *obj, uint32_t *hashkey,
+			   uint32_t table_sz)
+{
+	struct rtnl_netconf *nc = (struct rtnl_netconf *) obj;
+	unsigned int nckey_sz;
+	struct nc_hash_key {
+		int        nc_family;
+		int        nc_index;
+	} __attribute__((packed)) nckey;
+
+	nckey_sz = sizeof(nckey);
+	nckey.nc_family = nc->family;
+	nckey.nc_index = nc->ifindex;
+
+	*hashkey = nl_hash(&nckey, nckey_sz, 0) % table_sz;
+
+	NL_DBG(5, "netconf %p key (dev %d fam %d) keysz %d, hash 0x%x\n",
+	       nc, nckey.nc_index, nckey.nc_family, nckey_sz, *hashkey);
+}
+
+static uint64_t netconf_compare(struct nl_object *_a, struct nl_object *_b,
+			     uint64_t attrs, int flags)
+{
+	struct rtnl_netconf *a = (struct rtnl_netconf *) _a;
+	struct rtnl_netconf *b = (struct rtnl_netconf *) _b;
+	uint64_t diff = 0;
+
+#define NETCONF_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NETCONF_ATTR_##ATTR, a, b, EXPR)
+
+	diff |= NETCONF_DIFF(FAMILY,	a->family != b->family);
+	diff |= NETCONF_DIFF(IFINDEX,	a->ifindex != b->ifindex);
+	diff |= NETCONF_DIFF(RP_FILTER,	a->rp_filter != b->rp_filter);
+	diff |= NETCONF_DIFF(FWDING,	a->forwarding != b->forwarding);
+	diff |= NETCONF_DIFF(MC_FWDING,	a->mc_forwarding != b->mc_forwarding);
+	diff |= NETCONF_DIFF(PROXY_NEIGH, a->proxy_neigh != b->proxy_neigh);
+	diff |= NETCONF_DIFF(IGNORE_RT_LINKDWN,
+			a->ignore_routes_linkdown != b->ignore_routes_linkdown);
+	diff |= NETCONF_DIFF(INPUT,	a->input != b->input);
+
+#undef NETCONF_DIFF
+
+	return diff;
+}
+
+static int netconf_update(struct nl_object *old_obj, struct nl_object *new_obj)
+{
+	struct rtnl_netconf *new_nc = (struct rtnl_netconf *) new_obj;
+	struct rtnl_netconf *old_nc = (struct rtnl_netconf *) old_obj;
+	int action = new_obj->ce_msgtype;
+
+	switch(action) {
+	case RTM_NEWNETCONF:
+		if (new_nc->family != old_nc->family ||
+		    new_nc->ifindex != old_nc->ifindex)
+			return -NLE_OPNOTSUPP;
+
+		if (new_nc->ce_mask & NETCONF_ATTR_RP_FILTER)
+			old_nc->rp_filter = new_nc->rp_filter;
+		if (new_nc->ce_mask & NETCONF_ATTR_FWDING)
+			old_nc->forwarding = new_nc->forwarding;
+		if (new_nc->ce_mask & NETCONF_ATTR_MC_FWDING)
+			old_nc->mc_forwarding = new_nc->mc_forwarding;
+		if (new_nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH)
+			old_nc->proxy_neigh = new_nc->proxy_neigh;
+		if (new_nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN)
+			old_nc->ignore_routes_linkdown = new_nc->ignore_routes_linkdown;
+
+		break;
+	default:
+		return -NLE_OPNOTSUPP;
+	}
+
+	return NLE_SUCCESS;
+}
+
+/**
+ * @name Cache Management
+ * @{
+ */
+
+int rtnl_netconf_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
+{
+	return nl_cache_alloc_and_fill(&rtnl_netconf_ops, sk, result);
+}
+
+/**
+ * Search netconf in cache
+ * @arg cache		netconf cache
+ * @arg family		Address family of interest
+ * @arg ifindex		Interface index of interest
+ *
+ * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache()
+ * for given index and family
+ *
+ * The reference counter is incremented before returning the netconf entry,
+ * therefore the reference must be given back with rtnl_netconf_put() after
+ * usage.
+ *
+ * @return netconf object or NULL if no match was found.
+ */
+struct rtnl_netconf *rtnl_netconf_get_by_idx(struct nl_cache *cache, int family,
+					     int ifindex)
+{
+	struct rtnl_netconf *nc;
+
+	if (!ifindex || !family || cache->c_ops != &rtnl_netconf_ops)
+		return NULL;
+
+	nl_list_for_each_entry(nc, &cache->c_items, ce_list) {
+		if (nc->ifindex == ifindex &&
+		    nc->family == family) {
+			nl_object_get((struct nl_object *) nc);
+			return nc;
+		}
+	}
+
+	return NULL;
+}
+
+void rtnl_netconf_put(struct rtnl_netconf *nc)
+{
+	nl_object_put((struct nl_object *) nc);
+}
+
+/**
+ * Search netconf in cache
+ * @arg cache		netconf cache
+ * @arg family		Address family of interest
+ *
+ * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache()
+ * for "all" netconf settings for given family
+ *
+ * The reference counter is incremented before returning the netconf entry,
+ * therefore the reference must be given back with rtnl_netconf_put() after
+ * usage.
+ *
+ * @return netconf object or NULL if no match was found.
+ */
+struct rtnl_netconf *rtnl_netconf_get_all(struct nl_cache *cache, int family)
+{
+	return rtnl_netconf_get_by_idx(cache, family, NETCONFA_IFINDEX_ALL);
+}
+
+/**
+ * Search netconf in cache
+ * @arg cache		netconf cache
+ * @arg family		Address family of interest
+ *
+ * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache()
+ * for "default" netconf settings for given family
+ *
+ * The reference counter is incremented before returning the netconf entry,
+ * therefore the reference must be given back with rtnl_netconf_put() after
+ * usage.
+ *
+ * @return netconf object or NULL if no match was found.
+ */
+struct rtnl_netconf *rtnl_netconf_get_default(struct nl_cache *cache, int family)
+{
+	return rtnl_netconf_get_by_idx(cache, family, NETCONFA_IFINDEX_DEFAULT);
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+int rtnl_netconf_get_family(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_FAMILY))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->family;
+	return 0;
+}
+int rtnl_netconf_get_ifindex(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_IFINDEX))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->ifindex;
+	return 0;
+}
+int rtnl_netconf_get_forwarding(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_FWDING))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->forwarding;
+	return 0;
+}
+int rtnl_netconf_get_mc_forwarding(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_MC_FWDING))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->mc_forwarding;
+	return 0;
+}
+int rtnl_netconf_get_rp_filter(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_RP_FILTER))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->rp_filter;
+	return 0;
+}
+int rtnl_netconf_get_proxy_neigh(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->proxy_neigh;
+	return 0;
+}
+int rtnl_netconf_get_ignore_routes_linkdown(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->ignore_routes_linkdown;
+	return 0;
+}
+int rtnl_netconf_get_input(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_INPUT))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->input;
+	return 0;
+}
+
+
+/** @} */
+
+static struct nl_object_ops netconf_obj_ops = {
+	.oo_name		= "route/netconf",
+	.oo_size		= sizeof(struct rtnl_netconf),
+	.oo_clone		= netconf_clone,
+	.oo_dump = {
+	    [NL_DUMP_LINE] 	= netconf_dump_line,
+	    [NL_DUMP_DETAILS] 	= netconf_dump_line,
+	},
+	.oo_compare		= netconf_compare,
+	.oo_keygen		= netconf_keygen,
+	.oo_update		= netconf_update,
+	.oo_attrs2str		= netconf_attrs2str,
+	.oo_id_attrs		= (NETCONF_ATTR_FAMILY      |
+				   NETCONF_ATTR_IFINDEX)
+};
+
+static struct nl_af_group netconf_groups[] = {
+	{ AF_INET,	RTNLGRP_IPV4_NETCONF },
+	{ AF_INET6,	RTNLGRP_IPV6_NETCONF },
+	{ AF_MPLS,	RTNLGRP_MPLS_NETCONF },
+	{ END_OF_GROUP_LIST },
+};
+
+static struct nl_cache_ops rtnl_netconf_ops = {
+	.co_name		= "route/netconf",
+	.co_hdrsize		= sizeof(struct netconfmsg),
+	.co_msgtypes		= {
+					{ RTM_NEWNETCONF, NL_ACT_NEW, "new" },
+					{ RTM_DELNETCONF, NL_ACT_DEL, "del" },
+					{ RTM_GETNETCONF, NL_ACT_GET, "get" },
+					END_OF_MSGTYPES_LIST,
+				  },
+	.co_protocol		= NETLINK_ROUTE,
+	.co_groups		= netconf_groups,
+	.co_request_update      = netconf_request_update,
+	.co_msg_parser          = netconf_msg_parser,
+	.co_obj_ops		= &netconf_obj_ops,
+};
+
+static void __init netconf_init(void)
+{
+	nl_cache_mngt_register(&rtnl_netconf_ops);
+}
+
+static void __exit netconf_exit(void)
+{
+	nl_cache_mngt_unregister(&rtnl_netconf_ops);
+}
+
+/** @} */
diff --git a/lib/route/nexthop.c b/lib/route/nexthop.c
index d3ca499..7a9904c 100644
--- a/lib/route/nexthop.c
+++ b/lib/route/nexthop.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/nexthop.c	Routing Nexthop
  *
@@ -16,6 +17,7 @@
  */
 
 #include <netlink-private/netlink.h>
+#include <netlink-private/route/nexthop-encap.h>
 #include <netlink/netlink.h>
 #include <netlink/utils.h>
 #include <netlink/route/rtnl.h>
@@ -27,6 +29,9 @@
 #define NH_ATTR_IFINDEX 0x000004
 #define NH_ATTR_GATEWAY 0x000008
 #define NH_ATTR_REALMS  0x000010
+#define NH_ATTR_NEWDST  0x000020
+#define NH_ATTR_VIA     0x000040
+#define NH_ATTR_ENCAP   0x000080
 /** @endcond */
 
 /**
@@ -69,12 +74,39 @@
 		}
 	}
 
+	if (src->rtnh_newdst) {
+		nh->rtnh_newdst = nl_addr_clone(src->rtnh_newdst);
+		if (!nh->rtnh_newdst) {
+			nl_addr_put(nh->rtnh_gateway);
+			free(nh);
+			return NULL;
+		}
+	}
+
+	if (src->rtnh_via) {
+		nh->rtnh_via = nl_addr_clone(src->rtnh_via);
+		if (!nh->rtnh_via) {
+			nl_addr_put(nh->rtnh_gateway);
+			nl_addr_put(nh->rtnh_newdst);
+			free(nh);
+			return NULL;
+		}
+	}
+
 	return nh;
 }
 
 void rtnl_route_nh_free(struct rtnl_nexthop *nh)
 {
 	nl_addr_put(nh->rtnh_gateway);
+	nl_addr_put(nh->rtnh_newdst);
+	nl_addr_put(nh->rtnh_via);
+	if (nh->rtnh_encap) {
+		if (nh->rtnh_encap->ops && nh->rtnh_encap->ops->destructor)
+			nh->rtnh_encap->ops->destructor(nh->rtnh_encap->priv);
+		free(nh->rtnh_encap->priv);
+		free(nh->rtnh_encap);
+	}
 	free(nh);
 }
 
@@ -92,6 +124,12 @@
 	diff |= NH_DIFF(REALMS,		a->rtnh_realms != b->rtnh_realms);
 	diff |= NH_DIFF(GATEWAY,	nl_addr_cmp(a->rtnh_gateway,
 						    b->rtnh_gateway));
+	diff |= NH_DIFF(NEWDST,		nl_addr_cmp(a->rtnh_newdst,
+						    b->rtnh_newdst));
+	diff |= NH_DIFF(VIA,		nl_addr_cmp(a->rtnh_via,
+						    b->rtnh_via));
+	diff |= NH_DIFF(ENCAP,		nh_encap_compare(a->rtnh_encap,
+							 b->rtnh_encap));
 
 	if (loose)
 		diff |= NH_DIFF(FLAGS,
@@ -111,8 +149,19 @@
 
 	link_cache = nl_cache_mngt_require_safe("route/link");
 
+	if (nh->ce_mask & NH_ATTR_ENCAP)
+		nh_encap_dump(nh->rtnh_encap, dp);
+
+	if (nh->ce_mask & NH_ATTR_NEWDST)
+		nl_dump(dp, "as to %s ",
+			nl_addr2str(nh->rtnh_newdst, buf, sizeof(buf)));
+
 	nl_dump(dp, "via");
 
+	if (nh->ce_mask & NH_ATTR_VIA)
+		nl_dump(dp, " %s",
+			nl_addr2str(nh->rtnh_via, buf, sizeof(buf)));
+
 	if (nh->ce_mask & NH_ATTR_GATEWAY)
 		nl_dump(dp, " %s", nl_addr2str(nh->rtnh_gateway,
 						   buf, sizeof(buf)));
@@ -142,6 +191,17 @@
 
 	nl_dump(dp, "nexthop");
 
+	if (nh->ce_mask & NH_ATTR_ENCAP)
+		nh_encap_dump(nh->rtnh_encap, dp);
+
+	if (nh->ce_mask & NH_ATTR_NEWDST)
+		nl_dump(dp, " as to %s",
+			nl_addr2str(nh->rtnh_newdst, buf, sizeof(buf)));
+
+	if (nh->ce_mask & NH_ATTR_VIA)
+		nl_dump(dp, " via %s",
+			nl_addr2str(nh->rtnh_via, buf, sizeof(buf)));
+
 	if (nh->ce_mask & NH_ATTR_GATEWAY)
 		nl_dump(dp, " via %s", nl_addr2str(nh->rtnh_gateway,
 						   buf, sizeof(buf)));
@@ -190,6 +250,24 @@
 	}
 }
 
+void nh_set_encap(struct rtnl_nexthop *nh, struct rtnl_nh_encap *rtnh_encap)
+{
+	if (nh->rtnh_encap) {
+		if (nh->rtnh_encap->ops && nh->rtnh_encap->ops->destructor)
+			nh->rtnh_encap->ops->destructor(nh->rtnh_encap->priv);
+		free(nh->rtnh_encap->priv);
+		free(nh->rtnh_encap);
+	}
+
+	if (rtnh_encap) {
+		nh->rtnh_encap = rtnh_encap;
+		nh->ce_mask |= NH_ATTR_ENCAP;
+	} else {
+		nh->rtnh_encap = NULL;
+		nh->ce_mask &= ~NH_ATTR_ENCAP;
+	}
+}
+
 /**
  * @name Attributes
  * @{
@@ -269,6 +347,60 @@
 	return nh->rtnh_realms;
 }
 
+int rtnl_route_nh_set_newdst(struct rtnl_nexthop *nh, struct nl_addr *addr)
+{
+	struct nl_addr *old = nh->rtnh_newdst;
+
+	if (!nl_addr_valid(nl_addr_get_binary_addr(addr),
+			   nl_addr_get_len(addr)))
+		return -NLE_INVAL;
+
+	if (addr) {
+		nh->rtnh_newdst = nl_addr_get(addr);
+		nh->ce_mask |= NH_ATTR_NEWDST;
+	} else {
+		nh->ce_mask &= ~NH_ATTR_NEWDST;
+		nh->rtnh_newdst = NULL;
+	}
+
+	if (old)
+		nl_addr_put(old);
+
+	return 0;
+}
+
+struct nl_addr *rtnl_route_nh_get_newdst(struct rtnl_nexthop *nh)
+{
+	return nh->rtnh_newdst;
+}
+
+int rtnl_route_nh_set_via(struct rtnl_nexthop *nh, struct nl_addr *addr)
+{
+	struct nl_addr *old = nh->rtnh_via;
+
+	if (!nl_addr_valid(nl_addr_get_binary_addr(addr),
+			   nl_addr_get_len(addr)))
+		return -NLE_INVAL;
+
+	if (addr) {
+		nh->rtnh_via = nl_addr_get(addr);
+		nh->ce_mask |= NH_ATTR_VIA;
+	} else {
+		nh->ce_mask &= ~NH_ATTR_VIA;
+		nh->rtnh_via= NULL;
+	}
+
+	if (old)
+		nl_addr_put(old);
+
+	return 0;
+}
+
+struct nl_addr *rtnl_route_nh_get_via(struct rtnl_nexthop *nh)
+{
+	return nh->rtnh_via;
+}
+
 /** @} */
 
 /**
@@ -277,9 +409,9 @@
  */
 
 static const struct trans_tbl nh_flags[] = {
-	__ADD(RTNH_F_DEAD, dead)
-	__ADD(RTNH_F_PERVASIVE, pervasive)
-	__ADD(RTNH_F_ONLINK, onlink)
+	__ADD(RTNH_F_DEAD, dead),
+	__ADD(RTNH_F_PERVASIVE, pervasive),
+	__ADD(RTNH_F_ONLINK, onlink),
 };
 
 char *rtnl_route_nh_flags2str(int flags, char *buf, size_t len)
diff --git a/lib/route/nexthop_encap.c b/lib/route/nexthop_encap.c
new file mode 100644
index 0000000..21f647a
--- /dev/null
+++ b/lib/route/nexthop_encap.c
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/types.h>
+#include <netlink-private/route/nexthop-encap.h>
+#include <linux/lwtunnel.h>
+
+static struct lwtunnel_encap_type {
+	const char *name;
+	struct nh_encap_ops *ops;
+} lwtunnel_encap_types[__LWTUNNEL_ENCAP_MAX] = {
+	[LWTUNNEL_ENCAP_NONE] = { .name = "none" },
+	[LWTUNNEL_ENCAP_MPLS] = { .name = "mpls", .ops = &mpls_encap_ops },
+	[LWTUNNEL_ENCAP_IP]   = { .name = "ip" },
+	[LWTUNNEL_ENCAP_IP6]  = { .name = "ip6" },
+	[LWTUNNEL_ENCAP_ILA]  = { .name = "ila" },
+	[LWTUNNEL_ENCAP_BPF]  = { .name = "bpf" },
+};
+
+static const char *nh_encap_type2str(unsigned int type)
+{
+	const char *name;
+
+	if (type > LWTUNNEL_ENCAP_MAX)
+		return "unknown";
+
+	name = lwtunnel_encap_types[type].name;
+
+	return name ? name : "unknown";
+}
+
+void nh_encap_dump(struct rtnl_nh_encap *rtnh_encap, struct nl_dump_params *dp)
+{
+	nl_dump(dp, " encap %s ",
+		nh_encap_type2str(rtnh_encap->ops->encap_type));
+
+	if (rtnh_encap->ops && rtnh_encap->ops->dump)
+		rtnh_encap->ops->dump(rtnh_encap->priv, dp);
+}
+
+int nh_encap_build_msg(struct nl_msg *msg, struct rtnl_nh_encap *rtnh_encap)
+{
+	struct nlattr *encap;
+	int err;
+
+	if (!rtnh_encap->ops || !rtnh_encap->ops->build_msg) {
+		NL_DBG(2, "Nexthop encap type not implemented\n");
+		return -NLE_INVAL;
+	}
+
+	NLA_PUT_U16(msg, RTA_ENCAP_TYPE, rtnh_encap->ops->encap_type);
+
+	encap = nla_nest_start(msg, RTA_ENCAP);
+	if (!encap)
+		goto nla_put_failure;
+
+	err = rtnh_encap->ops->build_msg(msg, rtnh_encap->priv);
+	if (err)
+		return err;
+
+	nla_nest_end(msg, encap);
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_MSGSIZE;
+}
+
+int nh_encap_parse_msg(struct nlattr *encap, struct nlattr *encap_type,
+		       struct rtnl_nexthop *rtnh)
+{
+	uint16_t e_type = nla_get_u16(encap_type);
+
+	if (e_type == LWTUNNEL_ENCAP_NONE) {
+		NL_DBG(2, "RTA_ENCAP_TYPE should not be LWTUNNEL_ENCAP_NONE\n");
+		return -NLE_INVAL;
+	}
+	if (e_type > LWTUNNEL_ENCAP_MAX) {
+		NL_DBG(2, "Unknown RTA_ENCAP_TYPE: %d\n", e_type);
+		return -NLE_INVAL;
+	}
+
+	if (!lwtunnel_encap_types[e_type].ops) {
+		NL_DBG(2, "RTA_ENCAP_TYPE %s is not implemented\n",
+		       lwtunnel_encap_types[e_type].name);
+		return -NLE_MSGTYPE_NOSUPPORT;
+	}
+
+	return lwtunnel_encap_types[e_type].ops->parse_msg(encap, rtnh);
+}
+
+int nh_encap_compare(struct rtnl_nh_encap *a, struct rtnl_nh_encap *b)
+{
+	if (!a && !b)
+		return 0;
+
+	if ((a && !b) || (!a && b) || (a->ops != b->ops))
+		return 1;
+
+	if (!a->ops || !a->ops->compare)
+		return 0;
+
+	return a->ops->compare(a->priv, b->priv);
+}
diff --git a/lib/route/nh_encap_mpls.c b/lib/route/nh_encap_mpls.c
new file mode 100644
index 0000000..081661e
--- /dev/null
+++ b/lib/route/nh_encap_mpls.c
@@ -0,0 +1,135 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/types.h>
+#include <netlink-private/route/nexthop-encap.h>
+#include <netlink/route/nexthop.h>
+#include <linux/mpls_iptunnel.h>
+#include <linux/lwtunnel.h>
+
+struct mpls_iptunnel_encap {
+	struct nl_addr *dst;
+	uint8_t ttl;
+};
+
+static void mpls_encap_dump(void *priv, struct nl_dump_params *dp)
+{
+	struct mpls_iptunnel_encap *encap_info = priv;
+	char buf[256];
+
+	nl_dump(dp, "%s ", nl_addr2str(encap_info->dst, buf, sizeof(buf)));
+
+	if (encap_info->ttl)
+		nl_dump(dp, "ttl %u ", encap_info->ttl);
+}
+
+static int mpls_encap_build_msg(struct nl_msg *msg, void *priv)
+{
+	struct mpls_iptunnel_encap *encap_info = priv;
+
+	NLA_PUT_ADDR(msg, MPLS_IPTUNNEL_DST, encap_info->dst);
+	if (encap_info->ttl)
+		NLA_PUT_U8(msg, MPLS_IPTUNNEL_TTL, encap_info->ttl);
+
+	return 0;
+
+nla_put_failure:
+        return -NLE_MSGSIZE;
+}
+
+static void mpls_encap_destructor(void *priv)
+{
+	struct mpls_iptunnel_encap *encap_info = priv;
+
+	nl_addr_put(encap_info->dst);
+}
+
+static struct nla_policy mpls_encap_policy[MPLS_IPTUNNEL_MAX + 1] = {
+	[MPLS_IPTUNNEL_DST]     = { .type = NLA_U32 },
+	[MPLS_IPTUNNEL_TTL]     = { .type = NLA_U8 },
+};
+
+static int mpls_encap_parse_msg(struct nlattr *nla, struct rtnl_nexthop *nh)
+{
+	struct nlattr *tb[MPLS_IPTUNNEL_MAX + 1];
+	struct nl_addr *labels;
+	uint8_t ttl = 0;
+	int err;
+
+
+	err = nla_parse_nested(tb, MPLS_IPTUNNEL_MAX, nla, mpls_encap_policy);
+	if (err)
+		return err;
+
+	if (!tb[MPLS_IPTUNNEL_DST])
+		return -NLE_INVAL;
+
+	labels = nl_addr_alloc_attr(tb[MPLS_IPTUNNEL_DST], AF_MPLS);
+	if (!labels)
+		return -NLE_NOMEM;
+
+	if (tb[MPLS_IPTUNNEL_TTL])
+		ttl = nla_get_u8(tb[MPLS_IPTUNNEL_TTL]);
+
+	err = rtnl_route_nh_encap_mpls(nh, labels, ttl);
+
+	nl_addr_put(labels);
+
+	return err;
+}
+
+static int mpls_encap_compare(void *_a, void *_b)
+{
+	struct mpls_iptunnel_encap *a = _a;
+	struct mpls_iptunnel_encap *b = _b;
+	int diff = 0;
+
+	diff |= (a->ttl != b->ttl);
+	diff |= nl_addr_cmp(a->dst, b->dst);
+
+	return diff;
+}
+
+struct nh_encap_ops mpls_encap_ops = {
+	.encap_type	= LWTUNNEL_ENCAP_MPLS,
+	.build_msg	= mpls_encap_build_msg,
+	.parse_msg	= mpls_encap_parse_msg,
+	.compare	= mpls_encap_compare,
+	.dump		= mpls_encap_dump,
+	.destructor	= mpls_encap_destructor,
+};
+
+int rtnl_route_nh_encap_mpls(struct rtnl_nexthop *nh,
+			     struct nl_addr *addr,
+			     uint8_t ttl)
+{
+	struct mpls_iptunnel_encap *mpls_encap;
+	struct rtnl_nh_encap *rtnh_encap;
+
+	if (!addr)
+		return -NLE_INVAL;
+
+	if (!nl_addr_valid(nl_addr_get_binary_addr(addr),
+			   nl_addr_get_len(addr)))
+		return -NLE_INVAL;
+
+	rtnh_encap = calloc(1, sizeof(*rtnh_encap));
+	if (!rtnh_encap)
+		return -NLE_NOMEM;
+
+	mpls_encap = calloc(1, sizeof(*mpls_encap));
+	if (!mpls_encap) {
+		free(rtnh_encap);
+		return -NLE_NOMEM;
+	}
+
+	mpls_encap->dst = nl_addr_get(addr);
+	mpls_encap->ttl = ttl;
+
+	rtnh_encap->priv = mpls_encap;
+	rtnh_encap->ops = &mpls_encap_ops;
+
+	nh_set_encap(nh, rtnh_encap);
+
+	return 0;
+}
diff --git a/lib/route/pktloc.c b/lib/route/pktloc.c
index 27d63be..9462c6e 100644
--- a/lib/route/pktloc.c
+++ b/lib/route/pktloc.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/pktloc.c     Packet Location Aliasing
  *
@@ -101,13 +102,15 @@
 	/* if stat fails, just try to read the file */
 	if (stat(path, &st) == 0) {
 		/* Don't re-read file if file is unchanged */
-		if (last_read == st.st_mtime)
-			return 0;
+		if (last_read == st.st_mtime) {
+			err = 0;
+			goto errout;
+		}
 	}
 
 	NL_DBG(2, "Reading packet location file \"%s\"\n", path);
 
-	if (!(fd = fopen(path, "r"))) {
+	if (!(fd = fopen(path, "re"))) {
 		err = -NLE_PKTLOC_FILE;
 		goto errout;
 	}
diff --git a/lib/route/pktloc_grammar.l b/lib/route/pktloc_grammar.l
index cbb42b3..ab592d1 100644
--- a/lib/route/pktloc_grammar.l
+++ b/lib/route/pktloc_grammar.l
@@ -4,7 +4,11 @@
  #include <netlink/netlink.h>
  #include <netlink/utils.h>
  #include <netlink/route/pktloc.h>
+ #include <linux/tc_ematch/tc_em_cmp.h>
  #include "pktloc_syntax.h"
+
+ int pktloc_get_column(yyscan_t);
+ void pktloc_set_column(int, yyscan_t);
 %}
 
 %option 8bit
diff --git a/lib/route/qdisc.c b/lib/route/qdisc.c
index b8b6fa5..7413cf7 100644
--- a/lib/route/qdisc.c
+++ b/lib/route/qdisc.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/qdisc.c            Queueing Disciplines
  *
@@ -520,7 +521,7 @@
 {
 	struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
 
-	nl_dump(p, "refcnt %u ", qdisc->q_info);
+	nl_dump(p, "refcnt %u", qdisc->q_info);
 }
 
 static struct rtnl_tc_type_ops qdisc_ops = {
diff --git a/lib/route/qdisc/cbq.c b/lib/route/qdisc/cbq.c
index 95f1761..118f893 100644
--- a/lib/route/qdisc/cbq.c
+++ b/lib/route/qdisc/cbq.c
@@ -28,11 +28,11 @@
  */
 
 static const struct trans_tbl ovl_strategies[] = {
-	__ADD(TC_CBQ_OVL_CLASSIC,classic)
-	__ADD(TC_CBQ_OVL_DELAY,delay)
-	__ADD(TC_CBQ_OVL_LOWPRIO,lowprio)
-	__ADD(TC_CBQ_OVL_DROP,drop)
-	__ADD(TC_CBQ_OVL_RCLASSIC,rclassic)
+	__ADD(TC_CBQ_OVL_CLASSIC,classic),
+	__ADD(TC_CBQ_OVL_DELAY,delay),
+	__ADD(TC_CBQ_OVL_LOWPRIO,lowprio),
+	__ADD(TC_CBQ_OVL_DROP,drop),
+	__ADD(TC_CBQ_OVL_RCLASSIC,rclassic),
 };
 
 /**
diff --git a/lib/route/qdisc/hfsc.c b/lib/route/qdisc/hfsc.c
new file mode 100644
index 0000000..ddd1242
--- /dev/null
+++ b/lib/route/qdisc/hfsc.c
@@ -0,0 +1,351 @@
+/*
+ * lib/route/qdisc/hfsc.c	HFSC Qdisc
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2014 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+/**
+ * @ingroup qdisc
+ * @ingroup class
+ * @defgroup qdisc_hfsc Hierarchical Fair Service Curve (HFSC)
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/class.h>
+#include <netlink/route/link.h>
+#include <netlink/route/qdisc/hfsc.h>
+
+/** @cond SKIP */
+#define SCH_HFSC_CLS_HAS_RSC		0x001
+#define SCH_HFSC_CLS_HAS_FSC		0x002
+#define SCH_HFSC_CLS_HAS_USC		0x004
+
+#define SCH_HFSC_QD_HAS_DEFCLS		0x01
+/** @endcond */
+
+static struct nla_policy hfsc_policy[TCA_HFSC_MAX + 1] = {
+	[TCA_HFSC_RSC]  = { .minlen = sizeof(struct tc_service_curve) },
+	[TCA_HFSC_FSC]  = { .minlen = sizeof(struct tc_service_curve) },
+	[TCA_HFSC_USC]  = { .minlen = sizeof(struct tc_service_curve) },
+};
+
+static int hfsc_qdisc_msg_parser(struct rtnl_tc *tc, void *data)
+{
+	struct rtnl_hfsc_qdisc *hfsc = data;
+	struct tc_hfsc_qopt *opts;
+
+	opts = (struct tc_hfsc_qopt *) tc->tc_opts->d_data;
+	hfsc->qh_defcls = opts->defcls;
+	hfsc->qh_mask |= SCH_HFSC_QD_HAS_DEFCLS;
+	return 0;
+}
+
+static int hfsc_class_msg_parser(struct rtnl_tc *tc, void *data)
+{
+	struct nlattr *tb[TCA_HFSC_MAX + 1];
+	struct rtnl_hfsc_class *hfsc = data;
+	int err;
+
+	if ((err = tca_parse(tb, TCA_HFSC_MAX, tc, hfsc_policy)) < 0)
+		return err;
+
+	if (tb[TCA_HFSC_RSC]) {
+		struct tc_service_curve tsc;
+
+		nla_memcpy(&tsc, tb[TCA_HFSC_RSC], sizeof(tsc));
+		hfsc->ch_rsc = tsc;
+		hfsc->ch_mask |= SCH_HFSC_CLS_HAS_RSC;
+	}
+
+	if (tb[TCA_HFSC_FSC]) {
+		struct tc_service_curve tsc;
+
+		nla_memcpy(&tsc, tb[TCA_HFSC_FSC], sizeof(tsc));
+		hfsc->ch_fsc = tsc;
+		hfsc->ch_mask |= SCH_HFSC_CLS_HAS_FSC;
+	}
+
+	if (tb[TCA_HFSC_USC]) {
+		struct tc_service_curve tsc;
+
+		nla_memcpy(&tsc, tb[TCA_HFSC_USC], sizeof(tsc));
+		hfsc->ch_usc = tsc;
+		hfsc->ch_mask |= SCH_HFSC_CLS_HAS_USC;
+	}
+
+	return 0;
+}
+
+static void hfsc_qdisc_dump_line(struct rtnl_tc *tc, void *data,
+				struct nl_dump_params *p)
+{
+	struct rtnl_hfsc_qdisc *hfsc = data;
+
+	if (!hfsc)
+		return;
+
+	if (hfsc->qh_mask & SCH_HFSC_QD_HAS_DEFCLS) {
+		char buf[64];
+		nl_dump(p, " default-class %s",
+			rtnl_tc_handle2str(hfsc->qh_defcls, buf, sizeof(buf)));
+	}
+}
+
+static void hfsc_dump_tsc(struct nl_dump_params *p, struct tc_service_curve *tsc)
+{
+	nl_dump(p, " m1 %u d %u m2 %u\n", tsc->m1, tsc->d, tsc->m2);
+}
+
+static void hfsc_class_dump_line(struct rtnl_tc *tc, void *data,
+				struct nl_dump_params *p)
+{
+	struct rtnl_hfsc_class *hfsc = data;
+
+	if (!hfsc)
+		return;
+	if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_RSC)
+		hfsc_dump_tsc(p, &hfsc->ch_rsc);
+	if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_FSC)
+		hfsc_dump_tsc(p, &hfsc->ch_fsc);
+	if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_USC)
+		hfsc_dump_tsc(p, &hfsc->ch_usc);
+}
+
+static void hfsc_class_dump_details(struct rtnl_tc *tc, void *data,
+				   struct nl_dump_params *p)
+{
+	return;
+}
+
+static int hfsc_qdisc_msg_fill(struct rtnl_tc *tc, void *data,
+			      struct nl_msg *msg)
+{
+	struct rtnl_hfsc_qdisc *hfsc = data;
+	struct tc_hfsc_qopt opts = {0};
+
+	if (!hfsc)
+		BUG();
+
+	opts.defcls = hfsc->qh_defcls;
+	return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD);
+}
+
+static int hfsc_class_msg_fill(struct rtnl_tc *tc, void *data,
+			      struct nl_msg *msg)
+{
+	struct rtnl_hfsc_class *hfsc = data;
+	struct tc_service_curve tsc;
+
+	if (!hfsc)
+		BUG();
+
+	if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_RSC) {
+		tsc = hfsc->ch_rsc;
+		NLA_PUT(msg, TCA_HFSC_RSC, sizeof(tsc), &tsc);
+	}
+
+	if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_FSC) {
+		tsc = hfsc->ch_fsc;
+		NLA_PUT(msg, TCA_HFSC_FSC, sizeof(tsc), &tsc);
+	}
+
+	if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_USC) {
+		tsc = hfsc->ch_usc;
+		NLA_PUT(msg, TCA_HFSC_USC, sizeof(tsc), &tsc);
+	}
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_MSGSIZE;
+}
+
+static struct rtnl_tc_ops hfsc_qdisc_ops;
+static struct rtnl_tc_ops hfsc_class_ops;
+
+static struct rtnl_hfsc_qdisc *hfsc_qdisc_data(const struct rtnl_qdisc *qdisc, int *err)
+{
+	return rtnl_tc_data_check(TC_CAST(qdisc), &hfsc_qdisc_ops, err);
+}
+
+static struct rtnl_hfsc_class *hfsc_class_data(const struct rtnl_class *class, int *err)
+{
+	return rtnl_tc_data_check(TC_CAST(class), &hfsc_class_ops, err);
+}
+
+/**
+ * @name Attribute Modifications
+ * @{
+ */
+
+/**
+ * Return default class of HFSC qdisc
+ * @arg qdisc		hfsc qdisc object
+ *
+ * Returns the classid of the class where all unclassified traffic
+ * goes to.
+ *
+ * @return classid or TC_H_UNSPEC if unspecified.
+ */
+uint32_t rtnl_qdisc_hfsc_get_defcls(const struct rtnl_qdisc *qdisc)
+{
+	struct rtnl_hfsc_qdisc *hfsc;
+
+	if ((hfsc = hfsc_qdisc_data(qdisc, NULL)) &&
+	    (hfsc->qh_mask & SCH_HFSC_QD_HAS_DEFCLS))
+		return hfsc->qh_defcls;
+
+	return TC_H_UNSPEC;
+}
+
+/**
+ * Set default class of the hfsc qdisc to the specified value
+ * @arg qdisc		qdisc to change
+ * @arg defcls		new default class
+ */
+int rtnl_qdisc_hfsc_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls)
+{
+	struct rtnl_hfsc_qdisc *hfsc;
+	int err;
+
+	if (!(hfsc = hfsc_qdisc_data(qdisc, &err)))
+		return err;
+
+	hfsc->qh_defcls = defcls;
+	hfsc->qh_mask |= SCH_HFSC_QD_HAS_DEFCLS;
+
+	return 0;
+}
+
+int rtnl_class_hfsc_get_rsc(const struct rtnl_class *class, struct tc_service_curve *tsc)
+{
+	struct rtnl_hfsc_class *hfsc;
+	int err = -NLE_OPNOTSUPP;
+
+	if ((hfsc = hfsc_class_data(class, &err)) &&
+	    (hfsc->ch_mask & SCH_HFSC_CLS_HAS_RSC)) {
+		*tsc = hfsc->ch_rsc;
+		return 0;
+	}
+
+	return err;
+}
+
+int rtnl_class_hfsc_set_rsc(struct rtnl_class *class, const struct tc_service_curve *tsc)
+{
+	struct rtnl_hfsc_class *hfsc;
+	int err;
+
+	if (!(hfsc = hfsc_class_data(class, &err)))
+		return err;
+
+	hfsc->ch_rsc = *tsc;
+	hfsc->ch_mask |= SCH_HFSC_CLS_HAS_RSC;
+
+	return 0;
+}
+
+int rtnl_class_hfsc_get_fsc(const struct rtnl_class *class, struct tc_service_curve *tsc)
+{
+	struct rtnl_hfsc_class *hfsc;
+	int err = -NLE_OPNOTSUPP;
+
+	if ((hfsc = hfsc_class_data(class, &err)) &&
+	    (hfsc->ch_mask & SCH_HFSC_CLS_HAS_FSC)) {
+		*tsc = hfsc->ch_fsc;
+		return 0;
+	}
+
+	return err;
+}
+
+int rtnl_class_hfsc_set_fsc(struct rtnl_class *class, const struct tc_service_curve *tsc)
+{
+	struct rtnl_hfsc_class *hfsc;
+	int err;
+
+	if (!(hfsc = hfsc_class_data(class, &err)))
+		return err;
+
+	hfsc->ch_fsc = *tsc;
+	hfsc->ch_mask |= SCH_HFSC_CLS_HAS_FSC;
+
+	return 0;
+}
+
+int rtnl_class_hfsc_get_usc(const struct rtnl_class *class, struct tc_service_curve *tsc)
+{
+	struct rtnl_hfsc_class *hfsc;
+	int err = -NLE_OPNOTSUPP;
+
+	if ((hfsc = hfsc_class_data(class, &err)) &&
+	    (hfsc->ch_mask & SCH_HFSC_CLS_HAS_USC)) {
+		*tsc = hfsc->ch_usc;
+		return 0;
+	}
+
+	return err;
+}
+
+int rtnl_class_hfsc_set_usc(struct rtnl_class *class, const struct tc_service_curve *tsc)
+{
+	struct rtnl_hfsc_class *hfsc;
+	int err;
+
+	if (!(hfsc = hfsc_class_data(class, &err)))
+		return err;
+
+	hfsc->ch_usc = *tsc;
+	hfsc->ch_mask |= SCH_HFSC_CLS_HAS_USC;
+
+	return 0;
+}
+
+/** @} */
+
+static struct rtnl_tc_ops hfsc_qdisc_ops = {
+	.to_kind		= "hfsc",
+	.to_type		= RTNL_TC_TYPE_QDISC,
+	.to_size		= sizeof(struct rtnl_hfsc_qdisc),
+	.to_msg_parser		= hfsc_qdisc_msg_parser,
+	.to_dump[NL_DUMP_LINE]	= hfsc_qdisc_dump_line,
+	.to_msg_fill		= hfsc_qdisc_msg_fill,
+};
+
+static struct rtnl_tc_ops hfsc_class_ops = {
+	.to_kind		= "hfsc",
+	.to_type		= RTNL_TC_TYPE_CLASS,
+	.to_size		= sizeof(struct rtnl_hfsc_class),
+	.to_msg_parser		= hfsc_class_msg_parser,
+	.to_dump = {
+	    [NL_DUMP_LINE]	= hfsc_class_dump_line,
+	    [NL_DUMP_DETAILS]	= hfsc_class_dump_details,
+	},
+	.to_msg_fill		= hfsc_class_msg_fill,
+};
+
+static void __init hfsc_init(void)
+{
+	rtnl_tc_register(&hfsc_qdisc_ops);
+	rtnl_tc_register(&hfsc_class_ops);
+}
+
+static void __exit hfsc_exit(void)
+{
+	rtnl_tc_unregister(&hfsc_qdisc_ops);
+	rtnl_tc_unregister(&hfsc_class_ops);
+}
+
+/** @} */
diff --git a/lib/route/qdisc/htb.c b/lib/route/qdisc/htb.c
index 5a61a4e..e426a14 100644
--- a/lib/route/qdisc/htb.c
+++ b/lib/route/qdisc/htb.c
@@ -45,6 +45,8 @@
 static struct nla_policy htb_policy[TCA_HTB_MAX+1] = {
 	[TCA_HTB_INIT]	= { .minlen = sizeof(struct tc_htb_glob) },
 	[TCA_HTB_PARMS] = { .minlen = sizeof(struct tc_htb_opt) },
+	[TCA_HTB_RATE64] = { .minlen = sizeof(uint64_t) },
+	[TCA_HTB_CEIL64] = { .minlen = sizeof(uint64_t) },
 };
 
 static int htb_qdisc_msg_parser(struct rtnl_tc *tc, void *data)
@@ -55,7 +57,7 @@
 
 	if ((err = tca_parse(tb, TCA_HTB_MAX, tc, htb_policy)) < 0)
 		return err;
-	
+
 	if (tb[TCA_HTB_INIT]) {
 		struct tc_htb_glob opts;
 
@@ -78,7 +80,7 @@
 
 	if ((err = tca_parse(tb, TCA_HTB_MAX, tc, htb_policy)) < 0)
 		return err;
-	
+
 	if (tb[TCA_HTB_PARMS]) {
 		struct tc_htb_opt opts;
 
@@ -86,10 +88,16 @@
 		htb->ch_prio = opts.prio;
 		rtnl_copy_ratespec(&htb->ch_rate, &opts.rate);
 		rtnl_copy_ratespec(&htb->ch_ceil, &opts.ceil);
-		htb->ch_rbuffer = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer),
-						       opts.rate.rate);
-		htb->ch_cbuffer = rtnl_tc_calc_bufsize(nl_ticks2us(opts.cbuffer),
-						       opts.ceil.rate);
+
+		if (tb[TCA_HTB_RATE64])
+		        nla_memcpy(&htb->ch_rate.rs_rate64, tb[TCA_HTB_RATE64], sizeof(uint64_t));
+		if (tb[TCA_HTB_CEIL64])
+		        nla_memcpy(&htb->ch_ceil.rs_rate64, tb[TCA_HTB_CEIL64], sizeof(uint64_t));
+
+		htb->ch_rbuffer = rtnl_tc_calc_bufsize64(nl_ticks2us(opts.buffer),
+		                                         htb->ch_rate.rs_rate64);
+		htb->ch_cbuffer = rtnl_tc_calc_bufsize64(nl_ticks2us(opts.cbuffer),
+		                                         htb->ch_ceil.rs_rate64);
 		htb->ch_quantum = opts.quantum;
 		htb->ch_level = opts.level;
 
@@ -135,8 +143,8 @@
 		double r, rbit;
 		char *ru, *rubit;
 
-		r = nl_cancel_down_bytes(htb->ch_rate.rs_rate, &ru);
-		rbit = nl_cancel_down_bits(htb->ch_rate.rs_rate*8, &rubit);
+		r = nl_cancel_down_bytes(htb->ch_rate.rs_rate64, &ru);
+		rbit = nl_cancel_down_bits(htb->ch_rate.rs_rate64*8, &rubit);
 
 		nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u",
 			r, ru, rbit, rubit, 1<<htb->ch_rate.rs_cell_log);
@@ -156,8 +164,8 @@
 		double r, rbit;
 		char *ru, *rubit;
 
-		r = nl_cancel_down_bytes(htb->ch_ceil.rs_rate, &ru);
-		rbit = nl_cancel_down_bits(htb->ch_ceil.rs_rate*8, &rubit);
+		r = nl_cancel_down_bytes(htb->ch_ceil.rs_rate64, &ru);
+		rbit = nl_cancel_down_bits(htb->ch_ceil.rs_rate64*8, &rubit);
 
 		nl_dump(p, " ceil %.2f%s/s (%.0f%s) log %u",
 			r, ru, rbit, rubit, 1<<htb->ch_ceil.rs_cell_log);
@@ -191,9 +199,9 @@
 {
 	struct rtnl_htb_qdisc *htb = data;
 	struct tc_htb_glob opts = {
-        	.version = TC_HTB_PROTOVER,
-	        .rate2quantum = 10,
-        };
+		.version = TC_HTB_PROTOVER,
+		.rate2quantum = 10,
+	};
 
 	if (htb) {
 		if (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
@@ -213,6 +221,8 @@
 	uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE];
 	struct tc_htb_opt opts;
 	int buffer, cbuffer;
+	uint64_t rate64;
+	uint64_t ceil64;
 
 	if (!htb || !(htb->ch_mask & SCH_HTB_HAS_RATE))
 		BUG();
@@ -227,36 +237,43 @@
 
 	rtnl_tc_build_rate_table(tc, &htb->ch_rate, rtable);
 	rtnl_rcopy_ratespec(&opts.rate, &htb->ch_rate);
+	rate64 = htb->ch_rate.rs_rate64;
 
 	if (htb->ch_mask & SCH_HTB_HAS_CEIL) {
 		rtnl_tc_build_rate_table(tc, &htb->ch_ceil, ctable);
 		rtnl_rcopy_ratespec(&opts.ceil, &htb->ch_ceil);
+		ceil64 = htb->ch_ceil.rs_rate64;
 	} else {
 		/*
 		 * If not set, configured rate is used as ceil, which implies
 		 * no borrowing.
 		 */
 		memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec));
+		ceil64 = rate64;
 	}
 
 	if (htb->ch_mask & SCH_HTB_HAS_RBUFFER)
 		buffer = htb->ch_rbuffer;
 	else
-		buffer = opts.rate.rate / nl_get_psched_hz() + mtu; /* XXX */
+		buffer = rate64 / nl_get_psched_hz() + mtu; /* XXX */
 
-	opts.buffer = nl_us2ticks(rtnl_tc_calc_txtime(buffer, opts.rate.rate));
+	opts.buffer = nl_us2ticks(rtnl_tc_calc_txtime64(buffer, rate64));
 
 	if (htb->ch_mask & SCH_HTB_HAS_CBUFFER)
 		cbuffer = htb->ch_cbuffer;
 	else
-		cbuffer = opts.ceil.rate / nl_get_psched_hz() + mtu; /* XXX */
+		cbuffer = ceil64 / nl_get_psched_hz() + mtu; /* XXX */
 
-	opts.cbuffer = nl_us2ticks(rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate));
+	opts.cbuffer = nl_us2ticks(rtnl_tc_calc_txtime64(cbuffer, ceil64));
 
 	if (htb->ch_mask & SCH_HTB_HAS_QUANTUM)
 		opts.quantum = htb->ch_quantum;
 
 	NLA_PUT(msg, TCA_HTB_PARMS, sizeof(opts), &opts);
+	if (rate64 > 0xFFFFFFFFull)
+		NLA_PUT(msg, TCA_HTB_RATE64, sizeof(uint64_t), &rate64);
+	if (ceil64 > 0xFFFFFFFFull)
+		NLA_PUT(msg, TCA_HTB_CEIL64, sizeof(uint64_t), &ceil64);
 	NLA_PUT(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable);
 	NLA_PUT(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable);
 
@@ -269,14 +286,14 @@
 static struct rtnl_tc_ops htb_qdisc_ops;
 static struct rtnl_tc_ops htb_class_ops;
 
-static struct rtnl_htb_qdisc *htb_qdisc_data(struct rtnl_qdisc *qdisc)
+static struct rtnl_htb_qdisc *htb_qdisc_data(struct rtnl_qdisc *qdisc, int *err)
 {
-	return rtnl_tc_data_check(TC_CAST(qdisc), &htb_qdisc_ops);
+	return rtnl_tc_data_check(TC_CAST(qdisc), &htb_qdisc_ops, err);
 }
 
-static struct rtnl_htb_class *htb_class_data(struct rtnl_class *class)
+static struct rtnl_htb_class *htb_class_data(struct rtnl_class *class, int *err)
 {
-	return rtnl_tc_data_check(TC_CAST(class), &htb_class_ops);
+	return rtnl_tc_data_check(TC_CAST(class), &htb_class_ops, err);
 }
 
 /**
@@ -294,8 +311,8 @@
 {
 	struct rtnl_htb_qdisc *htb;
 
-	if ((htb = htb_qdisc_data(qdisc)) &&
-	    htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
+	if ((htb = htb_qdisc_data(qdisc, NULL)) &&
+	    (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM))
 		return htb->qh_rate2quantum;
 
 	return 0;
@@ -304,9 +321,10 @@
 int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum)
 {
 	struct rtnl_htb_qdisc *htb;
+	int err;
 
-	if (!(htb = htb_qdisc_data(qdisc)))
-		return -NLE_OPNOTSUPP;
+	if (!(htb = htb_qdisc_data(qdisc, &err)))
+		return err;
 
 	htb->qh_rate2quantum = rate2quantum;
 	htb->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM;
@@ -327,7 +345,7 @@
 {
 	struct rtnl_htb_qdisc *htb;
 
-	if ((htb = htb_qdisc_data(qdisc)) &&
+	if ((htb = htb_qdisc_data(qdisc, NULL)) &&
 	    htb->qh_mask & SCH_HTB_HAS_DEFCLS)
 		return htb->qh_defcls;
 
@@ -342,9 +360,10 @@
 int rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls)
 {
 	struct rtnl_htb_qdisc *htb;
+	int err;
 
-	if (!(htb = htb_qdisc_data(qdisc)))
-		return -NLE_OPNOTSUPP;
+	if (!(htb = htb_qdisc_data(qdisc, &err)))
+		return err;
 
 	htb->qh_defcls = defcls;
 	htb->qh_mask |= SCH_HTB_HAS_DEFCLS;
@@ -356,7 +375,8 @@
 {
 	struct rtnl_htb_class *htb;
 
-	if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_PRIO)
+	if ((htb = htb_class_data(class, NULL)) &&
+	    (htb->ch_mask & SCH_HTB_HAS_PRIO))
 		return htb->ch_prio;
 
 	return 0;
@@ -365,9 +385,10 @@
 int rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio)
 {
 	struct rtnl_htb_class *htb;
+	int err;
 
-	if (!(htb = htb_class_data(class)))
-		return -NLE_OPNOTSUPP;
+	if (!(htb = htb_class_data(class, &err)))
+		return err;
 
 	htb->ch_prio = prio;
 	htb->ch_mask |= SCH_HTB_HAS_PRIO;
@@ -379,15 +400,41 @@
  * Return rate of HTB class
  * @arg class		htb class object
  *
- * @return Rate in bytes/s or 0 if unspecified.
+ * @return Rate in bytes/s or 0 if unspecified. If the value
+ *   cannot be represented as 32 bit integer, (1<<32) is returned.
+ *   Use rtnl_htb_get_rate64() instead.
  */
 uint32_t rtnl_htb_get_rate(struct rtnl_class *class)
 {
 	struct rtnl_htb_class *htb;
 
-	if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_RATE)
-		return htb->ch_rate.rs_rate;
+	if (   !(htb = htb_class_data(class, NULL))
+	    || !(htb->ch_mask & SCH_HTB_HAS_RATE))
+	    return 0;
 
+	if (htb->ch_rate.rs_rate64 > 0xFFFFFFFFull)
+		return 0xFFFFFFFFull;
+
+	return htb->ch_rate.rs_rate64;
+}
+
+/**
+ * Return rate of HTB class
+ * @arg class		htb class object
+ * @arg out_rate64      on success, the set rate.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_htb_get_rate64(struct rtnl_class *class, uint64_t *out_rate64)
+{
+	struct rtnl_htb_class *htb;
+
+	if (!(htb = htb_class_data(class, NULL)))
+		return -NLE_INVAL;
+	if (!(htb->ch_mask & SCH_HTB_HAS_RATE))
+		return -NLE_NOATTR;
+
+	*out_rate64 = htb->ch_rate.rs_rate64;
 	return 0;
 }
 
@@ -400,13 +447,26 @@
  */
 int rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate)
 {
-	struct rtnl_htb_class *htb;
+	return rtnl_htb_set_rate64(class, rate);
+}
 
-	if (!(htb = htb_class_data(class)))
-		return -NLE_OPNOTSUPP;
+/**
+ * Set rate of HTB class
+ * @arg class		htb class object
+ * @arg rate		new rate in bytes per second
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_htb_set_rate64(struct rtnl_class *class, uint64_t rate)
+{
+	struct rtnl_htb_class *htb;
+	int err;
+
+	if (!(htb = htb_class_data(class, &err)))
+		return err;
 
 	htb->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */
-	htb->ch_rate.rs_rate = rate;
+	htb->ch_rate.rs_rate64 = rate;
 	htb->ch_mask |= SCH_HTB_HAS_RATE;
 
 	return 0;
@@ -416,15 +476,41 @@
  * Return ceil rate of HTB class
  * @arg class		htb class object
  *
- * @return Ceil rate in bytes/s or 0 if unspecified
+ * @return Ceil rate in bytes/s or 0 if unspecified.  If the value
+ *   cannot be represented as 32 bit integer, (1<<32) is returned.
+ *   Use rtnl_htb_get_ceil64() instead.
  */
 uint32_t rtnl_htb_get_ceil(struct rtnl_class *class)
 {
 	struct rtnl_htb_class *htb;
 
-	if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_CEIL)
-		return htb->ch_ceil.rs_rate;
+	if (   !(htb = htb_class_data(class, NULL))
+	    || !(htb->ch_mask & SCH_HTB_HAS_CEIL))
+		return 0;
 
+	if (htb->ch_ceil.rs_rate64 > 0xFFFFFFFFull)
+		return 0xFFFFFFFFull;
+
+	return htb->ch_ceil.rs_rate64;
+}
+
+/**
+ * Return ceil rate of HTB class
+ * @arg class		htb class object
+ * @arg out_ceil64      on success, the set ceil value.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_htb_get_ceil64(struct rtnl_class *class, uint64_t *out_ceil64)
+{
+	struct rtnl_htb_class *htb;
+
+	if (!(htb = htb_class_data(class, NULL)))
+		return -NLE_INVAL;
+	if (!(htb->ch_mask & SCH_HTB_HAS_CEIL))
+		return -NLE_NOATTR;
+
+	*out_ceil64 = htb->ch_ceil.rs_rate64;
 	return 0;
 }
 
@@ -437,13 +523,26 @@
  */
 int rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil)
 {
-	struct rtnl_htb_class *htb;
+	return rtnl_htb_set_ceil64(class, ceil);
+}
 
-	if (!(htb = htb_class_data(class)))
-		return -NLE_OPNOTSUPP;
+/**
+ * Set ceil rate of HTB class
+ * @arg class		htb class object
+ * @arg ceil64		new ceil rate number of bytes per second
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_htb_set_ceil64(struct rtnl_class *class, uint64_t ceil64)
+{
+	struct rtnl_htb_class *htb;
+	int err;
+
+	if (!(htb = htb_class_data(class, &err)))
+		return err;
 
 	htb->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */
-	htb->ch_ceil.rs_rate = ceil;
+	htb->ch_ceil.rs_rate64 = ceil64;
 	htb->ch_mask |= SCH_HTB_HAS_CEIL;
 
 	return 0;
@@ -459,7 +558,7 @@
 {
 	struct rtnl_htb_class *htb;
 
-	if ((htb = htb_class_data(class)) &&
+	if ((htb = htb_class_data(class, NULL)) &&
 	     htb->ch_mask & SCH_HTB_HAS_RBUFFER)
 		return htb->ch_rbuffer;
 
@@ -474,9 +573,10 @@
 int rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer)
 {
 	struct rtnl_htb_class *htb;
+	int err;
 
-	if (!(htb = htb_class_data(class)))
-		return -NLE_OPNOTSUPP;
+	if (!(htb = htb_class_data(class, &err)))
+		return err;
 
 	htb->ch_rbuffer = rbuffer;
 	htb->ch_mask |= SCH_HTB_HAS_RBUFFER;
@@ -494,7 +594,7 @@
 {
 	struct rtnl_htb_class *htb;
 
-	if ((htb = htb_class_data(class)) &&
+	if ((htb = htb_class_data(class, NULL)) &&
 	     htb->ch_mask & SCH_HTB_HAS_CBUFFER)
 		return htb->ch_cbuffer;
 
@@ -509,9 +609,10 @@
 int rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer)
 {
 	struct rtnl_htb_class *htb;
+	int err;
 
-	if (!(htb = htb_class_data(class)))
-		return -NLE_OPNOTSUPP;
+	if (!(htb = htb_class_data(class, &err)))
+		return err;
 
 	htb->ch_cbuffer = cbuffer;
 	htb->ch_mask |= SCH_HTB_HAS_CBUFFER;
@@ -531,7 +632,7 @@
 {
 	struct rtnl_htb_class *htb;
 
-	if ((htb = htb_class_data(class)) &&
+	if ((htb = htb_class_data(class, NULL)) &&
 	    htb->ch_mask & SCH_HTB_HAS_QUANTUM)
 		return htb->ch_quantum;
 
@@ -550,9 +651,10 @@
 int rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum)
 {
 	struct rtnl_htb_class *htb;
+	int err;
 
-	if (!(htb = htb_class_data(class)))
-		return -NLE_OPNOTSUPP;
+	if (!(htb = htb_class_data(class, &err)))
+		return err;
 
 	htb->ch_quantum = quantum;
 	htb->ch_mask |= SCH_HTB_HAS_QUANTUM;
@@ -568,16 +670,18 @@
  * 0, root classes have level (TC_HTB_MAXDEPTH - 1). Interior classes
  * have a level of one less than their parent.
  *
- * @return Level or -NLE_OPNOTSUPP
+ * @return Level or a negative error code.
  */
 int rtnl_htb_get_level(struct rtnl_class *class)
 {
 	struct rtnl_htb_class *htb;
+	int err = -NLE_OPNOTSUPP;
 
-	if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_LEVEL)
+	if ((htb = htb_class_data(class, &err)) &&
+	    (htb->ch_mask & SCH_HTB_HAS_LEVEL))
 		return htb->ch_level;
 
-	return -NLE_OPNOTSUPP;
+	return err;
 }
 
 /**
@@ -595,9 +699,10 @@
 int rtnl_htb_set_level(struct rtnl_class *class, int level)
 {
 	struct rtnl_htb_class *htb;
+	int err;
 
-	if (!(htb = htb_class_data(class)))
-		return -NLE_OPNOTSUPP;
+	if (!(htb = htb_class_data(class, &err)))
+		return err;
 
 	htb->ch_level = level;
 	htb->ch_mask |= SCH_HTB_HAS_LEVEL;
diff --git a/lib/route/qdisc/mqprio.c b/lib/route/qdisc/mqprio.c
new file mode 100644
index 0000000..0d07247
--- /dev/null
+++ b/lib/route/qdisc/mqprio.c
@@ -0,0 +1,605 @@
+/*
+ * lib/route/qdisc/mqprio.c             MQPRIO Qdisc/Class
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2018 Volodymyr Bendiuga <volodymyr.bendiuga@westermo.se>
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/qdisc/mqprio.h>
+
+/** @cond SKIP */
+#define SCH_MQPRIO_ATTR_NUMTC           (1 << 0)
+#define SCH_MQPRIO_ATTR_PRIOMAP         (1 << 1)
+#define SCH_MQPRIO_ATTR_HW              (1 << 2)
+#define SCH_MQPRIO_ATTR_QUEUE           (1 << 3)
+#define SCH_MQPRIO_ATTR_MODE            (1 << 4)
+#define SCH_MQPRIO_ATTR_SHAPER          (1 << 5)
+#define SCH_MQPRIO_ATTR_MIN_RATE        (1 << 6)
+#define SCH_MQPRIO_ATTR_MAX_RATE        (1 << 7)
+/** @endcond */
+
+static struct nla_policy mqprio_policy[TCA_MQPRIO_MAX + 1] = {
+	[TCA_MQPRIO_MODE]       = { .minlen = sizeof(uint16_t) },
+	[TCA_MQPRIO_SHAPER]     = { .minlen = sizeof(uint16_t) },
+	[TCA_MQPRIO_MIN_RATE64] = { .type = NLA_NESTED },
+	[TCA_MQPRIO_MAX_RATE64] = { .type = NLA_NESTED },
+};
+
+static int mqprio_msg_parser(struct rtnl_tc *tc, void *data)
+{
+	struct rtnl_mqprio *mqprio = data;
+	struct tc_mqprio_qopt *qopt;
+	struct nlattr *attr;
+	int len, rem, i, err;
+
+	if (tc->tc_opts->d_size < sizeof(*qopt))
+		return -NLE_INVAL;
+
+	qopt = (struct tc_mqprio_qopt *) tc->tc_opts->d_data;
+	mqprio->qm_num_tc = qopt->num_tc;
+	mqprio->qm_hw = qopt->hw;
+	memcpy(mqprio->qm_prio_map, qopt->prio_tc_map,
+	       TC_QOPT_MAX_QUEUE * sizeof(uint8_t));
+	memcpy(mqprio->qm_count, qopt->count,
+	       TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
+	memcpy(mqprio->qm_offset, qopt->offset,
+	       TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
+	mqprio->qm_mask = (SCH_MQPRIO_ATTR_NUMTC | SCH_MQPRIO_ATTR_PRIOMAP |
+	                   SCH_MQPRIO_ATTR_QUEUE | SCH_MQPRIO_ATTR_HW);
+
+	len = tc->tc_opts->d_size - NLA_ALIGN(sizeof(*qopt));
+
+	if (len > 0) {
+		struct nlattr *tb[TCA_MQPRIO_MAX + 1];
+
+		err = nla_parse(tb, TCA_MQPRIO_MAX, (struct nlattr *)
+		                ((char *) tc->tc_opts->d_data + NLA_ALIGN(sizeof(*qopt))),
+		                len, mqprio_policy);
+		if (err < 0)
+			return err;
+
+		if (tb[TCA_MQPRIO_MODE]) {
+			mqprio->qm_mode = nla_get_u16(tb[TCA_MQPRIO_MODE]);
+			mqprio->qm_mask |= SCH_MQPRIO_ATTR_MODE;
+		}
+
+		if (tb[TCA_MQPRIO_SHAPER]) {
+			mqprio->qm_shaper = nla_get_u16(tb[TCA_MQPRIO_SHAPER]);
+			mqprio->qm_mask |= SCH_MQPRIO_ATTR_SHAPER;
+		}
+
+		if (tb[TCA_MQPRIO_MIN_RATE64]) {
+			i = 0;
+			nla_for_each_nested(attr, tb[TCA_MQPRIO_MIN_RATE64], rem) {
+				if (nla_type(attr) != TCA_MQPRIO_MIN_RATE64)
+					return -EINVAL;
+
+				if (i >= mqprio->qm_num_tc)
+					break;
+
+				mqprio->qm_min_rate[i] = nla_get_u64(attr);
+			}
+
+			mqprio->qm_mask |= SCH_MQPRIO_ATTR_MIN_RATE;
+		}
+
+		if (tb[TCA_MQPRIO_MAX_RATE64]) {
+			i = 0;
+			nla_for_each_nested(attr, tb[TCA_MQPRIO_MAX_RATE64], rem) {
+				if (nla_type(attr) != TCA_MQPRIO_MAX_RATE64)
+					return -EINVAL;
+
+				if (i >= mqprio->qm_num_tc)
+					break;
+
+				mqprio->qm_max_rate[i] = nla_get_u64(attr);
+			}
+
+			mqprio->qm_mask |= SCH_MQPRIO_ATTR_MAX_RATE;
+		}
+	}
+
+	return 0;
+}
+
+static int mqprio_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
+{
+	struct rtnl_mqprio *mqprio = data;
+	struct tc_mqprio_qopt qopt = { 0 };
+	struct nlattr *nest = NULL;
+	int i;
+
+	if (!mqprio ||
+	    !(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC) ||
+	    !(mqprio->qm_mask & SCH_MQPRIO_ATTR_PRIOMAP) ||
+	    !(mqprio->qm_mask & SCH_MQPRIO_ATTR_QUEUE))
+		return -NLE_INVAL;
+
+	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW))
+		qopt.hw = 0;
+	else
+		qopt.hw = mqprio->qm_hw;
+
+	qopt.num_tc = mqprio->qm_num_tc;
+	memcpy(qopt.count, mqprio->qm_count, TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
+	memcpy(qopt.offset, mqprio->qm_offset, TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
+	memcpy(qopt.prio_tc_map, mqprio->qm_prio_map, TC_QOPT_MAX_QUEUE * sizeof(uint8_t));
+
+	nlmsg_append(msg, &qopt, sizeof(qopt), NL_DONTPAD);
+
+	if (mqprio->qm_hw) {
+		if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MODE)
+			NLA_PUT_U16(msg, TCA_MQPRIO_MODE, mqprio->qm_mode);
+
+		if (mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER)
+			NLA_PUT_U16(msg, TCA_MQPRIO_SHAPER, mqprio->qm_shaper);
+
+		if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MIN_RATE) {
+			nest = nla_nest_start(msg, TCA_MQPRIO_MIN_RATE64);
+			if (!nest)
+				goto nla_put_failure;
+
+			for (i = 0; i < mqprio->qm_num_tc; i++) {
+				if (nla_put(msg, TCA_MQPRIO_MIN_RATE64,
+				            sizeof(mqprio->qm_min_rate[i]),
+				            &mqprio->qm_min_rate[i]) < 0)
+					goto nla_nest_cancel;
+			}
+			nla_nest_end(msg, nest);
+		}
+
+		if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MAX_RATE) {
+			nest = nla_nest_start(msg, TCA_MQPRIO_MAX_RATE64);
+			if (!nest)
+				goto nla_put_failure;
+
+			for (i = 0; i < mqprio->qm_num_tc; i++) {
+				if (nla_put(msg, TCA_MQPRIO_MAX_RATE64,
+				            sizeof(mqprio->qm_max_rate[i]),
+				            &mqprio->qm_max_rate[i]) < 0)
+					goto nla_nest_cancel;
+			}
+			nla_nest_end(msg, nest);
+		}
+	}
+
+	return 0;
+
+nla_nest_cancel:
+	nla_nest_cancel(msg, nest);
+	return -NLE_MSGSIZE;
+
+nla_put_failure:
+	return -NLE_MSGSIZE;
+}
+
+static void mqprio_dump_line(struct rtnl_tc *tc, void *data,
+                             struct nl_dump_params *p)
+{
+	struct rtnl_mqprio *mqprio = data;
+
+	if (mqprio)
+		nl_dump(p, " num_tc %u", mqprio->qm_num_tc);
+}
+
+static void mqprio_dump_details(struct rtnl_tc *tc, void *data,
+                                struct nl_dump_params *p)
+{
+	struct rtnl_mqprio *mqprio = data;
+	int i;
+
+	if (!mqprio)
+		return;
+
+	nl_dump(p, "map [");
+
+	for (i = 0; i <= TC_QOPT_BITMASK; i++)
+		nl_dump(p, "%u%s", mqprio->qm_prio_map[i],
+			i < TC_QOPT_BITMASK ? " " : "");
+
+	nl_dump(p, "]\n");
+	nl_new_line(p);
+}
+
+/**
+ * @name Attribute Modification
+ * @{
+ */
+
+/**
+ * Set number of traffic classes.
+ * @arg qdisc           MQPRIO qdisc to be modified.
+ * @arg num_tc          Number of traffic classes to create.
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_set_num_tc(struct rtnl_qdisc *qdisc, int num_tc)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+		return -NLE_NOMEM;
+
+	mqprio->qm_num_tc = num_tc;
+	mqprio->qm_mask |= SCH_MQPRIO_ATTR_NUMTC;
+	return 0;
+}
+
+/**
+ * Get number of traffic classes of MQPRIO qdisc.
+ * @arg qdisc           MQPRIO qdisc.
+ * @return Number of traffic classes or a negative error code.
+ */
+int rtnl_qdisc_mqprio_get_num_tc(struct rtnl_qdisc *qdisc)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+		return -NLE_INVAL;
+
+	if (mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC)
+		return mqprio->qm_num_tc;
+	else
+		return -NLE_MISSING_ATTR;
+}
+
+/**
+ * Set priomap of the MQPRIO qdisc.
+ * @arg qdisc           MQPRIO qdisc to be modified.
+ * @arg priomap         New priority mapping.
+ * @arg len             Length of priomap (# of elements).
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[],
+                                int len)
+{
+	struct rtnl_mqprio *mqprio;
+	int i;
+
+	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+		return -NLE_NOMEM;
+
+	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC))
+		return -NLE_MISSING_ATTR;
+
+	if ((len / sizeof(uint8_t)) > (TC_QOPT_BITMASK+1))
+		return -NLE_RANGE;
+
+	for (i = 0; i <= TC_QOPT_BITMASK; i++) {
+		if (priomap[i] > mqprio->qm_num_tc)
+			return -NLE_RANGE;
+	}
+
+	memcpy(mqprio->qm_prio_map, priomap, len * sizeof(uint8_t));
+	mqprio->qm_mask |= SCH_MQPRIO_ATTR_PRIOMAP;
+
+	return 0;
+}
+
+/**
+ * Get priomap of MQPRIO qdisc.
+ * @arg qdisc           MQPRIO qdisc.
+ * @return Priority mapping as array of size TC_QOPT_BANDS+1
+ *         or NULL if an error occured.
+ */
+uint8_t *rtnl_qdisc_mqprio_get_priomap(struct rtnl_qdisc *qdisc)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+		return NULL;
+
+	if (mqprio->qm_mask & SCH_MQPRIO_ATTR_PRIOMAP)
+		return mqprio->qm_prio_map;
+	else
+		return NULL;
+}
+
+/**
+ * Offload to HW or run in SW (default).
+ * @arg qdisc           MQPRIO qdisc to be modified.
+ * @arg offload         1 - offload to HW, 0 - run in SW only (default).
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_hw_offload(struct rtnl_qdisc *qdisc, int offload)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+		return -NLE_NOMEM;
+
+	switch (offload) {
+	case 0:
+	case 1:
+		mqprio->qm_hw = offload;
+		break;
+	default:
+		return -NLE_INVAL;
+	}
+
+	mqprio->qm_mask |= SCH_MQPRIO_ATTR_HW;
+	return 0;
+}
+
+/**
+ * Check whether running in HW or SW.
+ * @arg qdisc           MQPRIO qdisc to be modified.
+ * @return 0 if running in SW, otherwise 1 (HW)
+ */
+int rtnl_qdisc_mqprio_get_hw_offload(struct rtnl_qdisc *qdisc)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+		return -NLE_INVAL;
+
+	if (mqprio->qm_mask & SCH_MQPRIO_ATTR_HW)
+		return mqprio->qm_hw;
+
+	return 0;
+}
+
+/**
+ * Set tc queue of the MQPRIO qdisc.
+ * @arg qdisc           MQPRIO qdisc to be modified.
+ * @arg count           count of queue range for each traffic class
+ * @arg offset          offset of queue range for each traffic class
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_set_queue(struct rtnl_qdisc *qdisc, uint16_t count[],
+                                uint16_t offset[], int len)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+		return -NLE_NOMEM;
+
+	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC))
+		return -NLE_MISSING_ATTR;
+
+	if ((len / sizeof(uint16_t)) > TC_QOPT_MAX_QUEUE)
+		return -NLE_RANGE;
+
+	memcpy(mqprio->qm_count, count, len * sizeof(uint16_t));
+	memcpy(mqprio->qm_offset, offset, len * sizeof(uint16_t));
+	mqprio->qm_mask |= SCH_MQPRIO_ATTR_QUEUE;
+
+	return 0;
+}
+
+/**
+ * Get tc queue of the MQPRIO qdisc.
+ * @arg qdisc           MQPRIO qdisc to be modified.
+ * @arg count           count of queue range for each traffic class
+ * @arg offset          offset of queue range for each traffic class
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_get_queue(struct rtnl_qdisc *qdisc, uint16_t *count,
+                                uint16_t *offset)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+		return -NLE_INVAL;
+
+	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_QUEUE))
+		return -NLE_MISSING_ATTR;
+
+	memcpy(count, mqprio->qm_count, TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
+	memcpy(offset, mqprio->qm_offset, TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
+
+	return 0;
+}
+
+/**
+ * Set mode of mqprio Qdisc
+ * @arg qdisc           MQPRIO qdisc to be modified.
+ * @arg mode            one of: TC_MQPRIO_MODE_DCB, TC_MQPRIO_MODE_CHANNEL
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_set_mode(struct rtnl_qdisc *qdisc, uint16_t mode)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+		return -NLE_NOMEM;
+
+	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW))
+		return -NLE_MISSING_ATTR;
+
+	mqprio->qm_mode = mode;
+	mqprio->qm_mask |= SCH_MQPRIO_ATTR_MODE;
+
+	return 0;
+}
+
+/**
+ * Get mode of mqprio Qdisc
+ * @arg qdisc           MQPRIO qdisc.
+ * @return mode on success or negative error code.
+ */
+int rtnl_qdisc_mqprio_get_mode(struct rtnl_qdisc *qdisc)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+		return -NLE_INVAL;
+
+	if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MODE)
+		return mqprio->qm_mode;
+	else
+		return -NLE_MISSING_ATTR;
+}
+
+/**
+ * Set shaper of mqprio Qdisc
+ * @arg qdisc           MQPRIO qdisc to be modified.
+ * @arg shaper          one of: TC_MQPRIO_SHAPER_DCB, TC_MQPRIO_SHAPER_BW_RATE
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_set_shaper(struct rtnl_qdisc *qdisc, uint16_t shaper)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+		return -NLE_NOMEM;
+
+	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW))
+		return -NLE_MISSING_ATTR;
+
+	mqprio->qm_shaper = shaper;
+	mqprio->qm_mask |= SCH_MQPRIO_ATTR_SHAPER;
+
+	return 0;
+}
+
+/**
+ * Get shaper of mqprio Qdisc
+ * @arg qdisc           MQPRIO qdisc.
+ * @return shaper on success or negative error code.
+ */
+int rtnl_qdisc_mqprio_get_shaper(struct rtnl_qdisc *qdisc)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+		return -NLE_INVAL;
+
+	if (mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER)
+		return mqprio->qm_shaper;
+	else
+		return -NLE_MISSING_ATTR;
+}
+
+/**
+ * Set minimum value of bandwidth rate limit for each traffic class
+ * @arg qdisc           MQPRIO qdisc.
+ * @arg min             minimum rate for each traffic class
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_set_min_rate(struct rtnl_qdisc *qdisc, uint64_t min[], int len)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+		return -NLE_NOMEM;
+
+	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER))
+		return -NLE_MISSING_ATTR;
+
+	if (mqprio->qm_shaper != TC_MQPRIO_SHAPER_BW_RATE)
+		return -NLE_INVAL;
+
+	if ((len / sizeof(uint64_t)) > TC_QOPT_MAX_QUEUE)
+		return -NLE_RANGE;
+
+	memcpy(mqprio->qm_min_rate, min, len * sizeof(uint64_t));
+	mqprio->qm_mask |= SCH_MQPRIO_ATTR_MIN_RATE;
+
+	return 0;
+}
+
+/**
+ * Get minimum value of bandwidth rate limit for each traffic class
+ * @arg qdisc           MQPRIO qdisc.
+ * @arg min             minimum rate for each traffic class
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_get_min_rate(struct rtnl_qdisc *qdisc, uint64_t *min)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+		return -NLE_INVAL;
+
+	if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MIN_RATE) {
+		memcpy(min, mqprio->qm_min_rate, TC_QOPT_MAX_QUEUE * sizeof(uint64_t));
+		return 0;
+	}
+
+	return -NLE_MISSING_ATTR;
+}
+
+/**
+ * Set maximum value of bandwidth rate limit for each traffic class
+ * @arg qdisc           MQPRIO qdisc.
+ * @arg max             maximum rate for each traffic class
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_set_max_rate(struct rtnl_qdisc *qdisc, uint64_t max[], int len)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
+		return -NLE_NOMEM;
+
+	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER))
+		return -NLE_MISSING_ATTR;
+
+	if (mqprio->qm_shaper != TC_MQPRIO_SHAPER_BW_RATE)
+		return -NLE_INVAL;
+
+	if ((len / sizeof(uint64_t)) > TC_QOPT_MAX_QUEUE)
+		return -NLE_RANGE;
+
+	memcpy(mqprio->qm_max_rate, max, len * sizeof(uint64_t));
+	mqprio->qm_mask |= SCH_MQPRIO_ATTR_MAX_RATE;
+
+	return 0;
+}
+
+/**
+ * Get maximum value of bandwidth rate limit for each traffic class
+ * @arg qdisc           MQPRIO qdisc.
+ * @arg min             maximum rate for each traffic class
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_mqprio_get_max_rate(struct rtnl_qdisc *qdisc, uint64_t *max)
+{
+	struct rtnl_mqprio *mqprio;
+
+	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
+		return -NLE_INVAL;
+
+	if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MAX_RATE) {
+		memcpy(max, mqprio->qm_max_rate, TC_QOPT_MAX_QUEUE * sizeof(uint64_t));
+		return 0;
+	}
+
+	return -NLE_MISSING_ATTR;
+}
+
+/** @} */
+
+static struct rtnl_tc_ops mqprio_ops = {
+	.to_kind                = "mqprio",
+	.to_type                = RTNL_TC_TYPE_QDISC,
+	.to_size                = sizeof(struct rtnl_mqprio),
+	.to_msg_parser          = mqprio_msg_parser,
+	.to_dump = {
+	    [NL_DUMP_LINE]      = mqprio_dump_line,
+	    [NL_DUMP_DETAILS]   = mqprio_dump_details,
+	},
+	.to_msg_fill            = mqprio_msg_fill,
+};
+
+static void __init mqprio_init(void)
+{
+	rtnl_tc_register(&mqprio_ops);
+}
+
+static void __exit mqprio_exit(void)
+{
+	rtnl_tc_unregister(&mqprio_ops);
+}
+
+/** @} */
diff --git a/lib/route/qdisc/netem.c b/lib/route/qdisc/netem.c
index 06d9fe8..17dee3b 100644
--- a/lib/route/qdisc/netem.c
+++ b/lib/route/qdisc/netem.c
@@ -29,8 +29,8 @@
 /** @cond SKIP */
 #define SCH_NETEM_ATTR_LATENCY		0x0001
 #define SCH_NETEM_ATTR_LIMIT		0x0002
-#define SCH_NETEM_ATTR_LOSS			0x0004
-#define SCH_NETEM_ATTR_GAP			0x0008
+#define SCH_NETEM_ATTR_LOSS		0x0004
+#define SCH_NETEM_ATTR_GAP		0x0008
 #define SCH_NETEM_ATTR_DUPLICATE	0x0010
 #define SCH_NETEM_ATTR_JITTER		0x0020
 #define SCH_NETEM_ATTR_DELAY_CORR	0x0040
@@ -40,7 +40,7 @@
 #define SCH_NETEM_ATTR_RO_CORR		0x0400
 #define SCH_NETEM_ATTR_CORRUPT_PROB	0x0800
 #define SCH_NETEM_ATTR_CORRUPT_CORR	0x1000
-#define SCH_NETEM_ATTR_DIST         0x2000
+#define SCH_NETEM_ATTR_DIST		0x2000
 /** @endcond */
 
 static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = {
@@ -76,7 +76,7 @@
 		struct nlattr *tb[TCA_NETEM_MAX+1];
 
 		err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *)
-				(tc->tc_opts->d_data + sizeof(*opts)),
+				((char *) tc->tc_opts->d_data + sizeof(*opts)),
 				len, netem_policy);
 		if (err < 0) {
 			free(netem);
@@ -106,18 +106,18 @@
 			netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB |
 					    SCH_NETEM_ATTR_RO_CORR);
 		}
-			
+
 		if (tb[TCA_NETEM_CORRUPT]) {
 			struct tc_netem_corrupt corrupt;
-						
+
 			nla_memcpy(&corrupt, tb[TCA_NETEM_CORRUPT], sizeof(corrupt));
 			netem->qnm_crpt.nmcr_probability = corrupt.probability;
 			netem->qnm_crpt.nmcr_correlation = corrupt.correlation;
-	
+
 			netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB |
 						SCH_NETEM_ATTR_CORRUPT_CORR);
 		}
-		
+
 		/* sch_netem does not currently dump TCA_NETEM_DELAY_DIST */
 		netem->qnm_dist.dist_data = NULL;
 		netem->qnm_dist.dist_size = 0;
@@ -129,10 +129,10 @@
 static void netem_free_data(struct rtnl_tc *tc, void *data)
 {
 	struct rtnl_netem *netem = data;
-	
+
 	if (!netem)
 		return;
-	
+
 	free(netem->qnm_dist.dist_data);
 }
 
@@ -141,12 +141,69 @@
 {
 	struct rtnl_netem *netem = data;
 
-	if (netem)
-		nl_dump(p, "limit %d", netem->qnm_limit);
+	if (netem) {
+		if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT && netem->qnm_limit > 0)
+			nl_dump(p, " limit %dpkts", netem->qnm_limit);
+		else
+			nl_dump(p, " no limit");
+	}
+}
+
+static void netem_dump_details(struct rtnl_tc *tc, void *data,
+                               struct nl_dump_params *p)
+{
+	struct rtnl_netem *netem = data;
+	char buf[32];
+
+	if (netem) {
+		if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY && netem->qnm_latency > 0) {
+			nl_msec2str(nl_ticks2us(netem->qnm_latency) / 1000, buf, sizeof(buf));
+			nl_dump(p, " latency %s", buf);
+
+			if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER && netem->qnm_jitter > 0) {
+				nl_msec2str(nl_ticks2us(netem->qnm_jitter) / 1000, buf, sizeof(buf));
+				nl_dump(p, " jitter %s", buf);
+
+				if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR && netem->qnm_corr.nmc_delay > 0)
+					nl_dump(p, " %d%", netem->qnm_corr.nmc_delay);
+			}
+		}
+
+		if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS && netem->qnm_loss > 0) {
+			nl_dump(p, " loss %d%", netem->qnm_loss);
+
+			if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR && netem->qnm_corr.nmc_loss > 0)
+				nl_dump(p, " %d%", netem->qnm_corr.nmc_loss);
+		}
+
+		if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE && netem->qnm_duplicate > 0) {
+			nl_dump(p, " duplicate %d%", netem->qnm_duplicate);
+
+			if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR && netem->qnm_corr.nmc_duplicate > 0)
+				nl_dump(p, " %d%", netem->qnm_corr.nmc_duplicate);
+		}
+
+		if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB && netem->qnm_ro.nmro_probability > 0) {
+			nl_dump(p, " reorder %d%", netem->qnm_ro.nmro_probability);
+
+			if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR && netem->qnm_ro.nmro_correlation > 0)
+				nl_dump(p, " %d%", netem->qnm_ro.nmro_correlation);
+
+			if (netem->qnm_mask & SCH_NETEM_ATTR_GAP && netem->qnm_gap > 0)
+				nl_dump(p, " gap %d", netem->qnm_gap);
+		}
+
+		if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB && netem->qnm_crpt.nmcr_probability > 0) {
+			nl_dump(p, " reorder %d%", netem->qnm_crpt.nmcr_probability);
+
+			if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR && netem->qnm_crpt.nmcr_correlation > 0)
+				nl_dump(p, " %d%", netem->qnm_crpt.nmcr_correlation);
+		}
+	}
 }
 
 static int netem_msg_fill_raw(struct rtnl_tc *tc, void *data,
-			      struct nl_msg *msg)
+                              struct nl_msg *msg)
 {
 	int err = 0;
 	struct tc_netem_qopt opts;
@@ -154,9 +211,13 @@
 	struct tc_netem_reorder reorder;
 	struct tc_netem_corrupt corrupt;
 	struct rtnl_netem *netem = data;
-	
-	unsigned char set_correlation = 0, set_reorder = 0,
-		set_corrupt = 0, set_dist = 0;
+
+	unsigned char set_correlation = 0, set_reorder = 0;
+	unsigned char set_corrupt = 0, set_dist = 0;
+
+	struct nlattr* head;
+	struct nlattr* tail;
+	int old_len;
 
 	if (!netem)
 		BUG();
@@ -167,116 +228,111 @@
 	memset(&corrupt, 0, sizeof(corrupt));
 
 	msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST;
-	
-	if ( netem->qnm_ro.nmro_probability != 0 ) {
-		if (netem->qnm_latency == 0) {
+
+	if (netem->qnm_ro.nmro_probability != 0) {
+		if (netem->qnm_latency == 0)
 			return -NLE_MISSING_ATTR;
-		}
-		if (netem->qnm_gap == 0) netem->qnm_gap = 1;
-	}
-	else if ( netem->qnm_gap ) { 
+		if (netem->qnm_gap == 0)
+			netem->qnm_gap = 1;
+	} else if (netem->qnm_gap)
 		return -NLE_MISSING_ATTR;
-	}
 
-	if ( netem->qnm_corr.nmc_delay != 0 ) {
-		if ( netem->qnm_latency == 0 || netem->qnm_jitter == 0) {
+	if (netem->qnm_corr.nmc_delay != 0) {
+		if (netem->qnm_latency == 0 || netem->qnm_jitter == 0)
 			return -NLE_MISSING_ATTR;
-		}
-		set_correlation = 1;
-	}
-	
-	if ( netem->qnm_corr.nmc_loss != 0 ) {
-		if ( netem->qnm_loss == 0 ) {
-			return -NLE_MISSING_ATTR;
-		}
 		set_correlation = 1;
 	}
 
-	if ( netem->qnm_corr.nmc_duplicate != 0 ) {
-		if ( netem->qnm_duplicate == 0 ) {
+	if (netem->qnm_corr.nmc_loss != 0) {
+		if (netem->qnm_loss == 0)
 			return -NLE_MISSING_ATTR;
-		}
 		set_correlation = 1;
 	}
-	
-	if ( netem->qnm_ro.nmro_probability != 0 ) set_reorder = 1;
-	else if ( netem->qnm_ro.nmro_correlation != 0 ) {
+
+	if (netem->qnm_corr.nmc_duplicate != 0) {
+		if (netem->qnm_duplicate == 0)
 			return -NLE_MISSING_ATTR;
+		set_correlation = 1;
 	}
-	
-	if ( netem->qnm_crpt.nmcr_probability != 0 ) set_corrupt = 1;
-	else if ( netem->qnm_crpt.nmcr_correlation != 0 ) {
+
+	if (netem->qnm_ro.nmro_probability != 0)
+		set_reorder = 1;
+	else if (netem->qnm_ro.nmro_correlation != 0)
+		return -NLE_MISSING_ATTR;
+
+	if (netem->qnm_crpt.nmcr_probability != 0)
+		set_corrupt = 1;
+	else if (netem->qnm_crpt.nmcr_correlation != 0)
+		return -NLE_MISSING_ATTR;
+
+	if (netem->qnm_dist.dist_data && netem->qnm_dist.dist_size) {
+		if (netem->qnm_latency == 0 || netem->qnm_jitter == 0)
 			return -NLE_MISSING_ATTR;
-	}
-	
-	if ( netem->qnm_dist.dist_data && netem->qnm_dist.dist_size ) {
-		if (netem->qnm_latency == 0 || netem->qnm_jitter == 0) {
-			return -NLE_MISSING_ATTR;
-	}
-	else {
-		/* Resize to accomodate the large distribution table */
-		int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size *
-			sizeof(netem->qnm_dist.dist_data[0]);
-		
-		msg->nm_nlh = (struct nlmsghdr *) realloc(msg->nm_nlh, new_msg_len);
-		if ( msg->nm_nlh == NULL )
-			return -NLE_NOMEM;
-		msg->nm_size = new_msg_len;
+		else {
+			/* Resize to accomodate the large distribution table */
+			int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size *
+			                  sizeof(netem->qnm_dist.dist_data[0]);
+			struct nlmsghdr *new_nlh = realloc(msg->nm_nlh, new_msg_len);
+
+			if (new_nlh == NULL)
+				return -NLE_NOMEM;
+			msg->nm_nlh = new_nlh;
+			msg->nm_size = new_msg_len;
 			set_dist = 1;
 		}
 	}
-	
+
 	opts.latency = netem->qnm_latency;
 	opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000;
 	opts.loss = netem->qnm_loss;
 	opts.gap = netem->qnm_gap;
 	opts.duplicate = netem->qnm_duplicate;
 	opts.jitter = netem->qnm_jitter;
-	
+
 	NLA_PUT(msg, TCA_OPTIONS, sizeof(opts), &opts);
-	
-	if ( set_correlation ) {
+
+	if (set_correlation) {
 		cor.delay_corr = netem->qnm_corr.nmc_delay;
 		cor.loss_corr = netem->qnm_corr.nmc_loss;
 		cor.dup_corr = netem->qnm_corr.nmc_duplicate;
 
 		NLA_PUT(msg, TCA_NETEM_CORR, sizeof(cor), &cor);
 	}
-	
-	if ( set_reorder ) {
+
+	if (set_reorder) {
 		reorder.probability = netem->qnm_ro.nmro_probability;
 		reorder.correlation = netem->qnm_ro.nmro_correlation;
 
 		NLA_PUT(msg, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
 	}
-	
-	if ( set_corrupt ) {
+
+	if (set_corrupt) {
 		corrupt.probability = netem->qnm_crpt.nmcr_probability;
 		corrupt.correlation = netem->qnm_crpt.nmcr_correlation;
 
 		NLA_PUT(msg, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
 	}
-	
-	if ( set_dist ) {
+
+	if (set_dist) {
 		NLA_PUT(msg, TCA_NETEM_DELAY_DIST,
-			netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]),
-			netem->qnm_dist.dist_data);
+		        netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]),
+		        netem->qnm_dist.dist_data);
 	}
 
 	/* Length specified in the TCA_OPTIONS section must span the entire
 	 * remainder of the message. That's just the way that sch_netem expects it.
 	 * Maybe there's a more succinct way to do this at a higher level.
 	 */
-	struct nlattr* head = (struct nlattr *)(NLMSG_DATA(msg->nm_nlh) +
-		NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO);
-		
-	struct nlattr* tail = (struct nlattr *)(((void *) (msg->nm_nlh)) +
-		NLMSG_ALIGN(msg->nm_nlh->nlmsg_len));
-	
-	int old_len = head->nla_len;
-	head->nla_len = (void *)tail - (void *)head;
+	head = (struct nlattr *)(((char *) NLMSG_DATA(msg->nm_nlh)) +
+	                         NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO);
+
+	tail = (struct nlattr *)(((char *) (msg->nm_nlh)) +
+	                         NLMSG_ALIGN(msg->nm_nlh->nlmsg_len));
+
+	old_len = head->nla_len;
+	head->nla_len = (char *)tail - (char *)head;
 	msg->nm_nlh->nlmsg_len += (head->nla_len - old_len);
-	
+
 	return err;
 nla_put_failure:
 	return -NLE_MSGSIZE;
@@ -299,7 +355,7 @@
 
 	if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
 		BUG();
-	
+
 	netem->qnm_limit = limit;
 	netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT;
 }
@@ -440,7 +496,7 @@
  * @name Corruption
  * @{
  */
- 
+
 /**
  * Set corruption probability of netem qdisc.
  * @arg qdisc		Netem qdisc to be modified.
@@ -815,17 +871,42 @@
 }
 
 /**
- * Set the delay distribution. Latency/jitter must be set before applying.
+ * Set the delay distribution data. Latency/jitter must be set before applying.
+ * @arg qdisc Netem qdisc.
+ * @return 0 on success, error code on failure.
+ */
+int rtnl_netem_set_delay_distribution_data(struct rtnl_qdisc *qdisc, const int16_t *data, size_t len) {
+	struct rtnl_netem *netem;
+	int16_t *new_data;
+
+	if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+		BUG();
+
+	if (len > MAXDIST)
+		return -NLE_INVAL;
+
+	new_data = (int16_t *) calloc(len, sizeof(int16_t));
+	if (!new_data)
+		return -NLE_NOMEM;
+
+	free (netem->qnm_dist.dist_data);
+	netem->qnm_dist.dist_data = new_data;
+
+	memcpy(netem->qnm_dist.dist_data, data, len * sizeof(int16_t));
+
+	netem->qnm_dist.dist_size = len;
+	netem->qnm_mask |= SCH_NETEM_ATTR_DIST;
+
+	return 0;
+}
+
+/**
+ * Load the delay distribution from a file. Latency/jitter must be set before applying.
  * @arg qdisc Netem qdisc.
  * @arg dist_type The name of the distribution (type, file, path/file).
  * @return 0 on success, error code on failure.
  */
 int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) {
-	struct rtnl_netem *netem;
-
-	if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
-		BUG();
-		
 	FILE *f;
 	int n = 0;
 	size_t i;
@@ -833,31 +914,39 @@
 	char *line;
 	char name[NAME_MAX];
 	char dist_suffix[] = ".dist";
-	
+	int16_t *data;
+	char *test_suffix;
+
+	/* Check several locations for the dist file */
+	char *test_path[] = {
+		"",
+		"./",
+		"/usr/lib/tc/",
+		"/usr/lib64/tc/",
+		"/usr/local/lib/tc/",
+	};
+
 	/* If the given filename already ends in .dist, don't append it later */
-	char *test_suffix = strstr(dist_type, dist_suffix);
+	test_suffix = strstr(dist_type, dist_suffix);
 	if (test_suffix != NULL && strlen(test_suffix) == 5)
 		strcpy(dist_suffix, "");
-	
-	/* Check several locations for the dist file */
-	char *test_path[] = { "", "./", "/usr/lib/tc/", "/usr/local/lib/tc/" };
-	
+
 	for (i = 0; i < ARRAY_SIZE(test_path); i++) {
 		snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix);
-		if ((f = fopen(name, "r")))
+		if ((f = fopen(name, "re")))
 			break;
 	}
-	
-	if ( f == NULL )
+
+	if (f == NULL)
 		return -nl_syserr2nlerr(errno);
-	
-	netem->qnm_dist.dist_data = (int16_t *) calloc (MAXDIST, sizeof(int16_t));
-	
+
+	data = (int16_t *) calloc (MAXDIST, sizeof(int16_t));
+
 	line = (char *) calloc (sizeof(char), len + 1);
-	
+
 	while (getline(&line, &len, f) != -1) {
 		char *p, *endp;
-		
+
 		if (*line == '\n' || *line == '#')
 			continue;
 
@@ -870,17 +959,16 @@
 				fclose(f);
 				return -NLE_INVAL;
 			}
-			netem->qnm_dist.dist_data[n++] = x;
-		}		
+			data[n++] = x;
+		}
 	}
-	
+
 	free(line);
-	
-	netem->qnm_dist.dist_size = n;
-	netem->qnm_mask |= SCH_NETEM_ATTR_DIST;
-	
 	fclose(f);
-	return 0;	
+
+	i = rtnl_netem_set_delay_distribution_data(qdisc, data, n);
+	free(data);
+	return i;
 }
 
 /** @} */
@@ -892,6 +980,7 @@
 	.to_msg_parser		= netem_msg_parser,
 	.to_free_data		= netem_free_data,
 	.to_dump[NL_DUMP_LINE]	= netem_dump_line,
+	.to_dump[NL_DUMP_DETAILS] = netem_dump_details,
 	.to_msg_fill_raw	= netem_msg_fill_raw,
 };
 
diff --git a/lib/route/qdisc/prio.c b/lib/route/qdisc/prio.c
index 54a46f0..5a21729 100644
--- a/lib/route/qdisc/prio.c
+++ b/lib/route/qdisc/prio.c
@@ -215,12 +215,12 @@
  */
 
 static const struct trans_tbl prios[] = {
-	__ADD(TC_PRIO_BESTEFFORT,besteffort)
-	__ADD(TC_PRIO_FILLER,filler)
-	__ADD(TC_PRIO_BULK,bulk)
-	__ADD(TC_PRIO_INTERACTIVE_BULK,interactive_bulk)
-	__ADD(TC_PRIO_INTERACTIVE,interactive)
-	__ADD(TC_PRIO_CONTROL,control)
+	__ADD(TC_PRIO_BESTEFFORT,besteffort),
+	__ADD(TC_PRIO_FILLER,filler),
+	__ADD(TC_PRIO_BULK,bulk),
+	__ADD(TC_PRIO_INTERACTIVE_BULK,interactive_bulk),
+	__ADD(TC_PRIO_INTERACTIVE,interactive),
+	__ADD(TC_PRIO_CONTROL,control),
 };
 
 /**
diff --git a/lib/route/qdisc/tbf.c b/lib/route/qdisc/tbf.c
index eb574d9..23cc845 100644
--- a/lib/route/qdisc/tbf.c
+++ b/lib/route/qdisc/tbf.c
@@ -44,24 +44,24 @@
 
 	if ((err = tca_parse(tb, TCA_TBF_MAX, tc, tbf_policy)) < 0)
 		return err;
-	
+
 	if (tb[TCA_TBF_PARMS]) {
 		struct tc_tbf_qopt opts;
 		int bufsize;
 
 		nla_memcpy(&opts, tb[TCA_TBF_PARMS], sizeof(opts));
 		tbf->qt_limit = opts.limit;
-	
+
 		rtnl_copy_ratespec(&tbf->qt_rate, &opts.rate);
 		tbf->qt_rate_txtime = opts.buffer;
-		bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer),
-					       opts.rate.rate);
+		bufsize = rtnl_tc_calc_bufsize64(nl_ticks2us(opts.buffer),
+		                                 tbf->qt_rate.rs_rate64);
 		tbf->qt_rate_bucket = bufsize;
 
 		rtnl_copy_ratespec(&tbf->qt_peakrate, &opts.peakrate);
 		tbf->qt_peakrate_txtime = opts.mtu;
-		bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.mtu),
-					       opts.peakrate.rate);
+		bufsize = rtnl_tc_calc_bufsize64(nl_ticks2us(opts.mtu),
+		                                 tbf->qt_peakrate.rs_rate64);
 		tbf->qt_peakrate_bucket = bufsize;
 
 		rtnl_tc_set_mpu(tc, tbf->qt_rate.rs_mpu);
@@ -83,8 +83,8 @@
 	if (!tbf)
 		return;
 
-	r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru);
-	rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit);
+	r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate64, &ru);
+	rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate64*8, &rubit);
 	lim = nl_cancel_down_bytes(tbf->qt_limit, &limu);
 
 	nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s",
@@ -114,9 +114,9 @@
 	if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
 		char *pru, *prbu, *bsu, *clu;
 		double pr, prb, bs, cl;
-		
-		pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate, &pru);
-		prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate * 8, &prbu);
+
+		pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate64, &pru);
+		prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate64 * 8, &prbu);
 		bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu);
 		cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log,
 					 &clu);
@@ -191,7 +191,7 @@
 {
 	double limit;
 
-	limit = (double) spec->rs_rate * ((double) latency / 1000000.);
+	limit = (double) spec->rs_rate64 * ((double) latency / 1000000.);
 	limit += bucket;
 
 	return limit;
@@ -278,7 +278,7 @@
 {
 	struct rtnl_tbf *tbf;
 	int cell_log;
-	
+
 	if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
 		BUG();
 
@@ -287,10 +287,10 @@
 	else
 		cell_log = rtnl_tc_calc_cell_log(cell);
 
-	tbf->qt_rate.rs_rate = rate;
+	tbf->qt_rate.rs_rate64 = (uint32_t)rate;
 	tbf->qt_rate_bucket = bucket;
 	tbf->qt_rate.rs_cell_log = cell_log;
-	tbf->qt_rate_txtime = nl_us2ticks(rtnl_tc_calc_txtime(bucket, rate));
+	tbf->qt_rate_txtime = nl_us2ticks(rtnl_tc_calc_txtime64(bucket, tbf->qt_rate.rs_rate64));
 	tbf->qt_mask |= TBF_ATTR_RATE;
 }
 
@@ -307,7 +307,7 @@
 		BUG();
 
 	if (tbf->qt_mask & TBF_ATTR_RATE)
-		return tbf->qt_rate.rs_rate;
+		return tbf->qt_rate.rs_rate64;
 	else
 		return -1;
 }
@@ -361,7 +361,7 @@
 {
 	struct rtnl_tbf *tbf;
 	int cell_log;
-	
+
 	if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
 		BUG();
 
@@ -369,11 +369,11 @@
 	if (cell_log < 0)
 		return cell_log;
 
-	tbf->qt_peakrate.rs_rate = rate;
+	tbf->qt_peakrate.rs_rate64 = (uint32_t)rate;
 	tbf->qt_peakrate_bucket = bucket;
 	tbf->qt_peakrate.rs_cell_log = cell_log;
-	tbf->qt_peakrate_txtime = nl_us2ticks(rtnl_tc_calc_txtime(bucket, rate));
-	
+	tbf->qt_peakrate_txtime = nl_us2ticks(rtnl_tc_calc_txtime64(bucket, tbf->qt_peakrate.rs_rate64));
+
 	tbf->qt_mask |= TBF_ATTR_PEAKRATE;
 
 	return 0;
@@ -392,7 +392,7 @@
 		BUG();
 
 	if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
-		return tbf->qt_peakrate.rs_rate;
+		return tbf->qt_peakrate.rs_rate64;
 	else
 		return -1;
 }
diff --git a/lib/route/route.c b/lib/route/route.c
index 2985187..0900b77 100644
--- a/lib/route/route.c
+++ b/lib/route/route.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/route.c	Routes
  *
@@ -173,6 +174,7 @@
 static struct nl_af_group route_groups[] = {
 	{ AF_INET,	RTNLGRP_IPV4_ROUTE },
 	{ AF_INET6,	RTNLGRP_IPV6_ROUTE },
+	{ AF_MPLS,	RTNLGRP_MPLS_ROUTE },
 	{ AF_DECnet,	RTNLGRP_DECnet_ROUTE },
 	{ END_OF_GROUP_LIST },
 };
diff --git a/lib/route/route_obj.c b/lib/route/route_obj.c
index dd4bf49..bacabe8 100644
--- a/lib/route/route_obj.c
+++ b/lib/route/route_obj.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/route_obj.c	Route Object
  *
@@ -31,6 +32,8 @@
  */
 
 #include <netlink-private/netlink.h>
+#include <netlink-private/utils.h>
+#include <netlink-private/route/nexthop-encap.h>
 #include <netlink/netlink.h>
 #include <netlink/cache.h>
 #include <netlink/utils.h>
@@ -40,6 +43,7 @@
 #include <netlink/route/route.h>
 #include <netlink/route/link.h>
 #include <netlink/route/nexthop.h>
+#include <linux/in_route.h>
 
 /** @cond SKIP */
 #define ROUTE_ATTR_FAMILY    0x000001
@@ -60,6 +64,7 @@
 #define ROUTE_ATTR_MULTIPATH 0x008000
 #define ROUTE_ATTR_REALMS    0x010000
 #define ROUTE_ATTR_CACHEINFO 0x020000
+#define ROUTE_ATTR_TTL_PROPAGATE 0x040000
 /** @endcond */
 
 static void route_constructor(struct nl_object *c)
@@ -243,6 +248,11 @@
 	if (r->ce_mask & ROUTE_ATTR_SRC)
 		nl_dump(p, "src %s ", nl_addr2str(r->rt_src, buf, sizeof(buf)));
 
+	if (r->ce_mask & ROUTE_ATTR_TTL_PROPAGATE) {
+		nl_dump(p, " ttl-propagate %s",
+			r->rt_ttl_propagate ? "enabled" : "disabled");
+	}
+
 	nl_dump(p, "\n");
 
 	if (r->ce_mask & ROUTE_ATTR_MULTIPATH) {
@@ -259,7 +269,7 @@
 	if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && r->rt_cacheinfo.rtci_error) {
 		nl_dump_line(p, "    cacheinfo error %d (%s)\n",
 			r->rt_cacheinfo.rtci_error,
-			strerror_r(-r->rt_cacheinfo.rtci_error, buf, sizeof(buf)));
+			nl_strerror_l(-r->rt_cacheinfo.rtci_error));
 	}
 
 	if (r->ce_mask & ROUTE_ATTR_METRICS) {
@@ -343,13 +353,27 @@
 	return;
 }
 
-static int route_compare(struct nl_object *_a, struct nl_object *_b,
-			uint32_t attrs, int flags)
+static uint32_t route_id_attrs_get(struct nl_object *obj)
+{
+	struct rtnl_route *route = (struct rtnl_route *)obj;
+	struct nl_object_ops *ops = obj->ce_ops;
+	uint32_t rv = ops->oo_id_attrs;
+
+	/* MPLS address family does not allow RTA_PRIORITY to be set */
+	if (route->rt_family == AF_MPLS)
+		rv &= ~ROUTE_ATTR_PRIO;
+
+	return rv;
+}
+
+static uint64_t route_compare(struct nl_object *_a, struct nl_object *_b,
+			      uint64_t attrs, int flags)
 {
 	struct rtnl_route *a = (struct rtnl_route *) _a;
 	struct rtnl_route *b = (struct rtnl_route *) _b;
 	struct rtnl_nexthop *nh_a, *nh_b;
-	int i, diff = 0, found;
+	int i, found;
+	uint64_t diff = 0;
 
 #define ROUTE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ROUTE_ATTR_##ATTR, a, b, EXPR)
 
@@ -365,6 +389,8 @@
 	diff |= ROUTE_DIFF(IIF,		a->rt_iif != b->rt_iif);
 	diff |= ROUTE_DIFF(PREF_SRC,	nl_addr_cmp(a->rt_pref_src,
 						    b->rt_pref_src));
+	diff |= ROUTE_DIFF(TTL_PROPAGATE,
+			   a->rt_ttl_propagate != b->rt_ttl_propagate);
 
 	if (flags & LOOSE_COMPARISON) {
 		nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) {
@@ -544,24 +570,25 @@
 }
 
 static const struct trans_tbl route_attrs[] = {
-	__ADD(ROUTE_ATTR_FAMILY, family)
-	__ADD(ROUTE_ATTR_TOS, tos)
-	__ADD(ROUTE_ATTR_TABLE, table)
-	__ADD(ROUTE_ATTR_PROTOCOL, protocol)
-	__ADD(ROUTE_ATTR_SCOPE, scope)
-	__ADD(ROUTE_ATTR_TYPE, type)
-	__ADD(ROUTE_ATTR_FLAGS, flags)
-	__ADD(ROUTE_ATTR_DST, dst)
-	__ADD(ROUTE_ATTR_SRC, src)
-	__ADD(ROUTE_ATTR_IIF, iif)
-	__ADD(ROUTE_ATTR_OIF, oif)
-	__ADD(ROUTE_ATTR_GATEWAY, gateway)
-	__ADD(ROUTE_ATTR_PRIO, prio)
-	__ADD(ROUTE_ATTR_PREF_SRC, pref_src)
-	__ADD(ROUTE_ATTR_METRICS, metrics)
-	__ADD(ROUTE_ATTR_MULTIPATH, multipath)
-	__ADD(ROUTE_ATTR_REALMS, realms)
-	__ADD(ROUTE_ATTR_CACHEINFO, cacheinfo)
+	__ADD(ROUTE_ATTR_FAMILY, family),
+	__ADD(ROUTE_ATTR_TOS, tos),
+	__ADD(ROUTE_ATTR_TABLE, table),
+	__ADD(ROUTE_ATTR_PROTOCOL, protocol),
+	__ADD(ROUTE_ATTR_SCOPE, scope),
+	__ADD(ROUTE_ATTR_TYPE, type),
+	__ADD(ROUTE_ATTR_FLAGS, flags),
+	__ADD(ROUTE_ATTR_DST, dst),
+	__ADD(ROUTE_ATTR_SRC, src),
+	__ADD(ROUTE_ATTR_IIF, iif),
+	__ADD(ROUTE_ATTR_OIF, oif),
+	__ADD(ROUTE_ATTR_GATEWAY, gateway),
+	__ADD(ROUTE_ATTR_PRIO, prio),
+	__ADD(ROUTE_ATTR_PREF_SRC, pref_src),
+	__ADD(ROUTE_ATTR_METRICS, metrics),
+	__ADD(ROUTE_ATTR_MULTIPATH, multipath),
+	__ADD(ROUTE_ATTR_REALMS, realms),
+	__ADD(ROUTE_ATTR_CACHEINFO, cacheinfo),
+	__ADD(ROUTE_ATTR_TTL_PROPAGATE, ttl_propagate),
 };
 
 static char *route_attrs2str(int attrs, char *buf, size_t len)
@@ -654,13 +681,17 @@
 
 int rtnl_route_set_family(struct rtnl_route *route, uint8_t family)
 {
-	if (family != AF_INET && family != AF_INET6 && family != AF_DECnet)
-		return -NLE_AF_NOSUPPORT;
+	switch(family) {
+	case AF_INET:
+	case AF_INET6:
+	case AF_DECnet:
+	case AF_MPLS:
+		route->rt_family = family;
+		route->ce_mask |= ROUTE_ATTR_FAMILY;
+		return 0;
+	}
 
-	route->rt_family = family;
-	route->ce_mask |= ROUTE_ATTR_FAMILY;
-
-	return 0;
+	return -NLE_AF_NOSUPPORT;
 }
 
 uint8_t rtnl_route_get_family(struct rtnl_route *route)
@@ -681,7 +712,7 @@
 
 	nl_addr_get(addr);
 	route->rt_dst = addr;
-	
+
 	route->ce_mask |= (ROUTE_ATTR_DST | ROUTE_ATTR_FAMILY);
 
 	return 0;
@@ -867,10 +898,10 @@
                                 void *arg)
 {
 	struct rtnl_nexthop *nh;
-    
+
 	if (r->ce_mask & ROUTE_ATTR_MULTIPATH) {
 		nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
-                        cb(nh, arg);
+			cb(nh, arg);
 		}
 	}
 }
@@ -879,15 +910,30 @@
 {
 	struct rtnl_nexthop *nh;
 	uint32_t i;
-    
+
 	if (r->ce_mask & ROUTE_ATTR_MULTIPATH && r->rt_nr_nh > n) {
 		i = 0;
 		nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
-                        if (i == n) return nh;
+			if (i == n) return nh;
 			i++;
 		}
 	}
-        return NULL;
+	return NULL;
+}
+
+void rtnl_route_set_ttl_propagate(struct rtnl_route *route, uint8_t ttl_prop)
+{
+	route->rt_ttl_propagate = ttl_prop;
+	route->ce_mask |= ROUTE_ATTR_TTL_PROPAGATE;
+}
+
+int rtnl_route_get_ttl_propagate(struct rtnl_route *route)
+{
+	if (!route)
+		return -NLE_INVAL;
+	if (!(route->ce_mask & ROUTE_ATTR_TTL_PROPAGATE))
+		return -NLE_MISSING_ATTR;
+	return route->rt_ttl_propagate;
 }
 
 /** @} */
@@ -915,6 +961,9 @@
 	if (route->rt_type == RTN_LOCAL)
 		return RT_SCOPE_HOST;
 
+	if (route->rt_family == AF_MPLS)
+		return RT_SCOPE_UNIVERSE;
+
 	if (!nl_list_empty(&route->rt_nexthops)) {
 		struct rtnl_nexthop *nh;
 
@@ -933,6 +982,31 @@
 
 /** @} */
 
+static struct nl_addr *rtnl_route_parse_via(struct nlattr *nla)
+{
+	int alen = nla_len(nla) - offsetof(struct rtvia, rtvia_addr);
+	struct rtvia *via = nla_data(nla);
+
+	return nl_addr_build(via->rtvia_family, via->rtvia_addr, alen);
+}
+
+static int rtnl_route_put_via(struct nl_msg *msg, struct nl_addr *addr)
+{
+	unsigned int alen = nl_addr_get_len(addr);
+	struct nlattr *nla;
+	struct rtvia *via;
+
+	nla = nla_reserve(msg, RTA_VIA, alen + sizeof(*via));
+	if (!nla)
+		return -EMSGSIZE;
+
+	via = nla_data(nla);
+	via->rtvia_family = nl_addr_get_family(addr);
+	memcpy(via->rtvia_addr, nl_addr_get_binary_addr(addr), alen);
+
+	return 0;
+}
+
 static struct nla_policy route_policy[RTA_MAX+1] = {
 	[RTA_IIF]	= { .type = NLA_U32 },
 	[RTA_OIF]	= { .type = NLA_U32 },
@@ -941,6 +1015,9 @@
 	[RTA_CACHEINFO]	= { .minlen = sizeof(struct rta_cacheinfo) },
 	[RTA_METRICS]	= { .type = NLA_NESTED },
 	[RTA_MULTIPATH]	= { .type = NLA_NESTED },
+	[RTA_TTL_PROPAGATE] = { .type = NLA_U8 },
+	[RTA_ENCAP]	= { .type = NLA_NESTED },
+	[RTA_ENCAP_TYPE] = { .type = NLA_U16 },
 };
 
 static int parse_multipath(struct rtnl_route *route, struct nlattr *attr)
@@ -985,10 +1062,45 @@
 
 			if (ntb[RTA_FLOW]) {
 				uint32_t realms;
-				
+
 				realms = nla_get_u32(ntb[RTA_FLOW]);
 				rtnl_route_nh_set_realms(nh, realms);
 			}
+
+			if (ntb[RTA_NEWDST]) {
+				struct nl_addr *addr;
+
+				addr = nl_addr_alloc_attr(ntb[RTA_NEWDST],
+							  route->rt_family);
+				if (!addr)
+					goto errout;
+
+				err = rtnl_route_nh_set_newdst(nh, addr);
+				nl_addr_put(addr);
+				if (err)
+					goto errout;
+			}
+
+			if (ntb[RTA_VIA]) {
+				struct nl_addr *addr;
+
+				addr = rtnl_route_parse_via(ntb[RTA_VIA]);
+				if (!addr)
+					goto errout;
+
+				err = rtnl_route_nh_set_via(nh, addr);
+				nl_addr_put(addr);
+				if (err)
+					goto errout;
+			}
+
+			if (ntb[RTA_ENCAP] && ntb[RTA_ENCAP_TYPE]) {
+				err = nh_encap_parse_msg(ntb[RTA_ENCAP],
+							 ntb[RTA_ENCAP_TYPE],
+							 nh);
+				if (err)
+					goto errout;
+			}
 		}
 
 		rtnl_route_add_nexthop(route, nh);
@@ -1014,10 +1126,8 @@
 	int err, family;
 
 	route = rtnl_route_alloc();
-	if (!route) {
-		err = -NLE_NOMEM;
-		goto errout;
-	}
+	if (!route)
+		goto errout_nomem;
 
 	route->ce_msgtype = nlh->nlmsg_type;
 
@@ -1038,7 +1148,13 @@
 	route->ce_mask |= ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
 			  ROUTE_ATTR_TABLE | ROUTE_ATTR_TYPE |
 			  ROUTE_ATTR_SCOPE | ROUTE_ATTR_PROTOCOL |
-			  ROUTE_ATTR_FLAGS | ROUTE_ATTR_PRIO;
+			  ROUTE_ATTR_FLAGS;
+
+	/* right now MPLS does not allow rt_prio to be set, so don't
+	 * assume it is unless it comes from an attribute
+	 */
+	if (family != AF_MPLS)
+		route->ce_mask |= ROUTE_ATTR_PRIO;
 
 	if (tb[RTA_DST]) {
 		if (!(dst = nl_addr_alloc_attr(tb[RTA_DST], family)))
@@ -1096,7 +1212,9 @@
 		for (i = 1; i <= RTAX_MAX; i++) {
 			if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) {
 				uint32_t m = nla_get_u32(mtb[i]);
-				if (rtnl_route_set_metric(route, i, m) < 0)
+
+				err = rtnl_route_set_metric(route, i, m);
+				if (err < 0)
 					goto errout;
 			}
 		}
@@ -1114,14 +1232,14 @@
 
 	if (tb[RTA_OIF]) {
 		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
-			goto errout;
+			goto errout_nomem;
 
 		rtnl_route_nh_set_ifindex(old_nh, nla_get_u32(tb[RTA_OIF]));
 	}
 
 	if (tb[RTA_GATEWAY]) {
 		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
-			goto errout;
+			goto errout_nomem;
 
 		if (!(addr = nl_addr_alloc_attr(tb[RTA_GATEWAY], family)))
 			goto errout_nomem;
@@ -1132,11 +1250,59 @@
 
 	if (tb[RTA_FLOW]) {
 		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
-			goto errout;
+			goto errout_nomem;
 
 		rtnl_route_nh_set_realms(old_nh, nla_get_u32(tb[RTA_FLOW]));
 	}
 
+	if (tb[RTA_NEWDST]) {
+		struct nl_addr *addr;
+
+		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
+			goto errout_nomem;
+
+		addr = nl_addr_alloc_attr(tb[RTA_NEWDST], route->rt_family);
+		if (!addr)
+			goto errout_nomem;
+
+		err = rtnl_route_nh_set_newdst(old_nh, addr);
+		nl_addr_put(addr);
+		if (err)
+			goto errout;
+	}
+
+	if (tb[RTA_VIA]) {
+		int alen = nla_len(tb[RTA_VIA]) - offsetof(struct rtvia, rtvia_addr);
+		struct rtvia *via = nla_data(tb[RTA_VIA]);
+
+		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
+			goto errout_nomem;
+
+		addr = nl_addr_build(via->rtvia_family, via->rtvia_addr, alen);
+		if (!addr)
+			goto errout_nomem;
+
+		err = rtnl_route_nh_set_via(old_nh, addr);
+		nl_addr_put(addr);
+		if (err)
+			goto errout;
+	}
+
+	if (tb[RTA_TTL_PROPAGATE]) {
+		rtnl_route_set_ttl_propagate(route,
+					     nla_get_u8(tb[RTA_TTL_PROPAGATE]));
+	}
+
+	if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE]) {
+		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
+			goto errout_nomem;
+
+		err = nh_encap_parse_msg(tb[RTA_ENCAP],
+					 tb[RTA_ENCAP_TYPE], old_nh);
+		if (err)
+			goto errout;
+	}
+
 	if (old_nh) {
 		rtnl_route_nh_set_flags(old_nh, rtm->rtm_flags & 0xff);
 		if (route->rt_nr_nh == 0) {
@@ -1163,12 +1329,15 @@
 
 			rtnl_route_nh_free(old_nh);
 		}
+		old_nh = NULL;
 	}
 
 	*result = route;
 	return 0;
 
 errout:
+	if (old_nh)
+		rtnl_route_nh_free(old_nh);
 	rtnl_route_put(route);
 	return err;
 
@@ -1211,12 +1380,17 @@
 		goto nla_put_failure;
 
 	/* Additional table attribute replacing the 8bit in the header, was
-	 * required to allow more than 256 tables. */
-	NLA_PUT_U32(msg, RTA_TABLE, route->rt_table);
+	 * required to allow more than 256 tables. MPLS does not allow the
+	 * table attribute to be set
+	 */
+	if (route->rt_family != AF_MPLS)
+		NLA_PUT_U32(msg, RTA_TABLE, route->rt_table);
 
 	if (nl_addr_get_len(route->rt_dst))
 		NLA_PUT_ADDR(msg, RTA_DST, route->rt_dst);
-	NLA_PUT_U32(msg, RTA_PRIORITY, route->rt_prio);
+
+	if (route->ce_mask & ROUTE_ATTR_PRIO)
+		NLA_PUT_U32(msg, RTA_PRIORITY, route->rt_prio);
 
 	if (route->ce_mask & ROUTE_ATTR_SRC)
 		NLA_PUT_ADDR(msg, RTA_SRC, route->rt_src);
@@ -1227,6 +1401,9 @@
 	if (route->ce_mask & ROUTE_ATTR_IIF)
 		NLA_PUT_U32(msg, RTA_IIF, route->rt_iif);
 
+	if (route->ce_mask & ROUTE_ATTR_TTL_PROPAGATE)
+		NLA_PUT_U8(msg, RTA_TTL_PROPAGATE, route->rt_ttl_propagate);
+
 	if (route->rt_nmetrics > 0) {
 		uint32_t val;
 
@@ -1252,6 +1429,13 @@
 			NLA_PUT_U32(msg, RTA_OIF, nh->rtnh_ifindex);
 		if (nh->rtnh_realms)
 			NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms);
+		if (nh->rtnh_newdst)
+			NLA_PUT_ADDR(msg, RTA_NEWDST, nh->rtnh_newdst);
+		if (nh->rtnh_via && rtnl_route_put_via(msg, nh->rtnh_via) < 0)
+			goto nla_put_failure;
+		if (nh->rtnh_encap &&
+		    nh_encap_build_msg(msg, nh->rtnh_encap) < 0)
+			goto nla_put_failure;
 	} else if (rtnl_route_get_nnexthops(route) > 1) {
 		struct nlattr *multipath;
 		struct rtnl_nexthop *nh;
@@ -1274,11 +1458,22 @@
 				NLA_PUT_ADDR(msg, RTA_GATEWAY,
 					     nh->rtnh_gateway);
 
+			if (nh->rtnh_newdst)
+				NLA_PUT_ADDR(msg, RTA_NEWDST, nh->rtnh_newdst);
+
+			if (nh->rtnh_via &&
+			    rtnl_route_put_via(msg, nh->rtnh_via) < 0)
+				goto nla_put_failure;
+
 			if (nh->rtnh_realms)
 				NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms);
 
-			rtnh->rtnh_len = nlmsg_tail(msg->nm_nlh) -
-						(void *) rtnh;
+			if (nh->rtnh_encap &&
+			    nh_encap_build_msg(msg, nh->rtnh_encap) < 0)
+				goto nla_put_failure;
+
+			rtnh->rtnh_len = (char *) nlmsg_tail(msg->nm_nlh) -
+						(char *) rtnh;
 		}
 
 		nla_nest_end(msg, multipath);
@@ -1309,6 +1504,7 @@
 	.oo_id_attrs		= (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
 				   ROUTE_ATTR_TABLE | ROUTE_ATTR_DST |
 				   ROUTE_ATTR_PRIO),
+	.oo_id_attrs_get	= route_id_attrs_get,
 };
 /** @endcond */
 
diff --git a/lib/route/route_utils.c b/lib/route/route_utils.c
index a5b3966..6337f72 100644
--- a/lib/route/route_utils.c
+++ b/lib/route/route_utils.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/route_utils.c	Routing Utilities
  *
@@ -62,7 +63,7 @@
 	add_routing_table_name(RT_TABLE_DEFAULT, "default");
 	add_routing_table_name(RT_TABLE_MAIN, "main");
 	add_routing_table_name(RT_TABLE_LOCAL, "local");
-};
+}
 
 static void __exit release_routing_table_names(void)
 {
@@ -108,7 +109,7 @@
 	add_proto_name(RTPROT_KERNEL, "kernel");
 	add_proto_name(RTPROT_BOOT, "boot");
 	add_proto_name(RTPROT_STATIC, "static");
-};
+}
 
 static void __exit release_proto_names(void)
 {
@@ -140,19 +141,19 @@
  */
 
 static const struct trans_tbl route_metrices[] = {
-	__ADD(RTAX_UNSPEC, unspec)
-	__ADD(RTAX_LOCK, lock)
-	__ADD(RTAX_MTU, mtu)
-	__ADD(RTAX_WINDOW, window)
-	__ADD(RTAX_RTT, rtt)
-	__ADD(RTAX_RTTVAR, rttvar)
-	__ADD(RTAX_SSTHRESH, ssthresh)
-	__ADD(RTAX_CWND, cwnd)
-	__ADD(RTAX_ADVMSS, advmss)
-	__ADD(RTAX_REORDERING, reordering)
-	__ADD(RTAX_HOPLIMIT, hoplimit)
-	__ADD(RTAX_INITCWND, initcwnd)
-	__ADD(RTAX_FEATURES, features)
+	__ADD(RTAX_UNSPEC, unspec),
+	__ADD(RTAX_LOCK, lock),
+	__ADD(RTAX_MTU, mtu),
+	__ADD(RTAX_WINDOW, window),
+	__ADD(RTAX_RTT, rtt),
+	__ADD(RTAX_RTTVAR, rttvar),
+	__ADD(RTAX_SSTHRESH, ssthresh),
+	__ADD(RTAX_CWND, cwnd),
+	__ADD(RTAX_ADVMSS, advmss),
+	__ADD(RTAX_REORDERING, reordering),
+	__ADD(RTAX_HOPLIMIT, hoplimit),
+	__ADD(RTAX_INITCWND, initcwnd),
+	__ADD(RTAX_FEATURES, features),
 };
 
 char *rtnl_route_metric2str(int metric, char *buf, size_t size)
diff --git a/lib/route/rtnl.c b/lib/route/rtnl.c
index 6a55ca1..f280a48 100644
--- a/lib/route/rtnl.c
+++ b/lib/route/rtnl.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/rtnl.c		Routing Netlink
  *
@@ -34,9 +35,9 @@
  * Fills out a routing netlink request message and sends it out
  * using nl_send_simple().
  *
- * @return 0 on success or a negative error code. Due to a bug in
- * older versions, this returned the number of bytes sent. So for
- * compatibility, treat positive return values as success too.
+ * @return 0 on success or a negative error code. Due to a bug in older
+ * version of the library, this function returned the number of bytes sent.
+ * Treat any non-negative number as success.
  */
 int nl_rtgen_request(struct nl_sock *sk, int type, int family, int flags)
 {
@@ -58,18 +59,18 @@
  */
 
 static const struct trans_tbl rtntypes[] = {
-	__ADD(RTN_UNSPEC,unspec)
-	__ADD(RTN_UNICAST,unicast)
-	__ADD(RTN_LOCAL,local)
-	__ADD(RTN_BROADCAST,broadcast)
-	__ADD(RTN_ANYCAST,anycast)
-	__ADD(RTN_MULTICAST,multicast)
-	__ADD(RTN_BLACKHOLE,blackhole)
-	__ADD(RTN_UNREACHABLE,unreachable)
-	__ADD(RTN_PROHIBIT,prohibit)
-	__ADD(RTN_THROW,throw)
-	__ADD(RTN_NAT,nat)
-	__ADD(RTN_XRESOLVE,xresolve)
+	__ADD(RTN_UNSPEC,unspec),
+	__ADD(RTN_UNICAST,unicast),
+	__ADD(RTN_LOCAL,local),
+	__ADD(RTN_BROADCAST,broadcast),
+	__ADD(RTN_ANYCAST,anycast),
+	__ADD(RTN_MULTICAST,multicast),
+	__ADD(RTN_BLACKHOLE,blackhole),
+	__ADD(RTN_UNREACHABLE,unreachable),
+	__ADD(RTN_PROHIBIT,prohibit),
+	__ADD(RTN_THROW,throw),
+	__ADD(RTN_NAT,nat),
+	__ADD(RTN_XRESOLVE,xresolve),
 };
 
 char *nl_rtntype2str(int type, char *buf, size_t size)
@@ -90,11 +91,11 @@
  */
 
 static const struct trans_tbl scopes[] = {
-	__ADD(255,nowhere)
-	__ADD(254,host)
-	__ADD(253,link)
-	__ADD(200,site)
-	__ADD(0,global)
+	__ADD(255,nowhere),
+	__ADD(254,host),
+	__ADD(253,link),
+	__ADD(200,site),
+	__ADD(0,global),
 };
 
 char *rtnl_scope2str(int scope, char *buf, size_t size)
diff --git a/lib/route/rule.c b/lib/route/rule.c
index b2161a2..a0ba42e 100644
--- a/lib/route/rule.c
+++ b/lib/route/rule.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/rule.c          Routing Rules
  *
@@ -22,22 +23,28 @@
 #include <netlink/route/rtnl.h>
 #include <netlink/route/rule.h>
 #include <inttypes.h>
+#include <linux/fib_rules.h>
 
 /** @cond SKIP */
-#define RULE_ATTR_FAMILY	0x0001
-#define RULE_ATTR_TABLE		0x0002
-#define RULE_ATTR_ACTION	0x0004
-#define RULE_ATTR_FLAGS		0x0008
-#define RULE_ATTR_IIFNAME	0x0010
-#define RULE_ATTR_OIFNAME	0x0020
-#define RULE_ATTR_PRIO		0x0040
-#define RULE_ATTR_MARK		0x0080
-#define RULE_ATTR_MASK		0x0100
-#define RULE_ATTR_GOTO		0x0200
-#define RULE_ATTR_SRC		0x0400
-#define RULE_ATTR_DST		0x0800
-#define RULE_ATTR_DSFIELD	0x1000
-#define RULE_ATTR_FLOW		0x2000
+#define RULE_ATTR_FAMILY	0x000001
+#define RULE_ATTR_TABLE		0x000002
+#define RULE_ATTR_ACTION	0x000004
+#define RULE_ATTR_FLAGS		0x000008
+#define RULE_ATTR_IIFNAME	0x000010
+#define RULE_ATTR_OIFNAME	0x000020
+#define RULE_ATTR_PRIO		0x000040
+#define RULE_ATTR_MARK		0x000080
+#define RULE_ATTR_MASK		0x000100
+#define RULE_ATTR_GOTO		0x000200
+#define RULE_ATTR_SRC		0x000400
+#define RULE_ATTR_DST		0x000800
+#define RULE_ATTR_DSFIELD	0x001000
+#define RULE_ATTR_FLOW		0x002000
+#define RULE_ATTR_L3MDEV	0x004000
+#define RULE_ATTR_PROTOCOL	0x008000
+#define RULE_ATTR_IP_PROTO	0x010000
+#define RULE_ATTR_SPORT		0x020000
+#define RULE_ATTR_DPORT		0x040000
 
 static struct nl_cache_ops rtnl_rule_ops;
 static struct nl_object_ops rule_obj_ops;
@@ -79,6 +86,13 @@
 	[FRA_FWMASK]	= { .type = NLA_U32 },
 	[FRA_GOTO]	= { .type = NLA_U32 },
 	[FRA_FLOW]	= { .type = NLA_U32 },
+	[FRA_L3MDEV]	= { .type = NLA_U8 },
+	[FRA_PROTOCOL]	= { .type = NLA_U8 },
+	[FRA_IP_PROTO]	= { .type = NLA_U8 },
+	[FRA_SPORT_RANGE] = { .minlen = sizeof(struct fib_rule_port_range),
+			      .maxlen = sizeof(struct fib_rule_port_range) },
+	[FRA_DPORT_RANGE] = { .minlen = sizeof(struct fib_rule_port_range),
+			      .maxlen = sizeof(struct fib_rule_port_range) },
 };
 
 static int rule_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
@@ -107,8 +121,9 @@
 	rule->r_action = frh->action;
 	rule->r_flags = frh->flags;
 
-	rule->ce_mask = (RULE_ATTR_FAMILY | RULE_ATTR_TABLE | RULE_ATTR_ACTION |
-			 RULE_ATTR_FLAGS);
+	rule->ce_mask = (RULE_ATTR_FAMILY | RULE_ATTR_ACTION | RULE_ATTR_FLAGS);
+	if (rule->r_table)
+		rule->ce_mask |= RULE_ATTR_TABLE;
 
 	/* ipv4 only */
 	if (frh->tos) {
@@ -118,7 +133,8 @@
 
 	if (tb[FRA_TABLE]) {
 		rule->r_table = nla_get_u32(tb[FRA_TABLE]);
-		rule->ce_mask |= RULE_ATTR_TABLE;
+		if (rule->r_table)
+			rule->ce_mask |= RULE_ATTR_TABLE;
 	}
 
 	if (tb[FRA_IIFNAME]) {
@@ -172,6 +188,37 @@
 		rule->ce_mask |= RULE_ATTR_FLOW;
 	}
 
+	if (tb[FRA_L3MDEV]) {
+		rule->r_l3mdev = nla_get_u8(tb[FRA_L3MDEV]);
+		rule->ce_mask |= RULE_ATTR_L3MDEV;
+	}
+
+	if (tb[FRA_PROTOCOL]) {
+		rule->r_protocol = nla_get_u8(tb[FRA_PROTOCOL]);
+		rule->ce_mask |= RULE_ATTR_PROTOCOL;
+	}
+
+	if (tb[FRA_IP_PROTO]) {
+		rule->r_ip_proto = nla_get_u8(tb[FRA_IP_PROTO]);
+		rule->ce_mask |= RULE_ATTR_IP_PROTO;
+	}
+
+	if (tb[FRA_SPORT_RANGE]) {
+		struct fib_rule_port_range *pr;
+
+		pr = nla_data(tb[FRA_SPORT_RANGE]);
+		rule->r_sport = *pr;
+		rule->ce_mask |= RULE_ATTR_SPORT;
+	}
+
+	if (tb[FRA_DPORT_RANGE]) {
+		struct fib_rule_port_range *pr;
+
+		pr = nla_data(tb[FRA_DPORT_RANGE]);
+		rule->r_dport = *pr;
+		rule->ce_mask |= RULE_ATTR_DPORT;
+	}
+
 	err = pp->pp_cb((struct nl_object *) rule, pp);
 errout:
 	rtnl_rule_put(rule);
@@ -219,6 +266,33 @@
 		nl_dump(p, "lookup %s ",
 			rtnl_route_table2str(r->r_table, buf, sizeof(buf)));
 
+	if (r->ce_mask & RULE_ATTR_L3MDEV)
+		nl_dump(p, "lookup [l3mdev-table] ");
+
+	if (r->ce_mask & RULE_ATTR_IP_PROTO)
+		nl_dump(p, "ipproto %s ",
+			nl_ip_proto2str(r->r_ip_proto, buf, sizeof(buf)));
+
+	if (r->ce_mask & RULE_ATTR_SPORT) {
+		if (r->r_sport.start == r->r_sport.end)
+			nl_dump(p, "sport %u ", r->r_sport.start);
+		else
+			nl_dump(p, "sport %u-%u ",
+				r->r_sport.start, r->r_sport.end);
+	}
+
+	if (r->ce_mask & RULE_ATTR_DPORT) {
+		if (r->r_dport.start == r->r_dport.end)
+			nl_dump(p, "dport %u ", r->r_dport.start);
+		else
+			nl_dump(p, "dport %u-%u ",
+				r->r_dport.start, r->r_dport.end);
+	}
+
+	if (r->ce_mask & RULE_ATTR_PROTOCOL)
+		nl_dump(p, "protocol %s ",
+			rtnl_route_proto2str(r->r_protocol, buf, sizeof(buf)));
+
 	if (r->ce_mask & RULE_ATTR_FLOW)
 		nl_dump(p, "flow %s ",
 			rtnl_realms2str(r->r_flow, buf, sizeof(buf)));
@@ -243,14 +317,12 @@
 	rule_dump_details(obj, p);
 }
 
-#define RULE_ATTR_FLAGS		0x0008
-
-static int rule_compare(struct nl_object *_a, struct nl_object *_b,
-			uint32_t attrs, int flags)
+static uint64_t rule_compare(struct nl_object *_a, struct nl_object *_b,
+			     uint64_t attrs, int flags)
 {
 	struct rtnl_rule *a = (struct rtnl_rule *) _a;
 	struct rtnl_rule *b = (struct rtnl_rule *) _b;
-	int diff = 0;
+	uint64_t diff = 0;
 
 #define RULE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, RULE_ATTR_##ATTR, a, b, EXPR)
 
@@ -274,19 +346,19 @@
 }
 
 static const struct trans_tbl rule_attrs[] = {
-	__ADD(RULE_ATTR_FAMILY, family)
-	__ADD(RULE_ATTR_TABLE, table)
-	__ADD(RULE_ATTR_ACTION, action)
-	__ADD(RULE_ATTR_IIFNAME, iifname)
-	__ADD(RULE_ATTR_OIFNAME, oifname)
-	__ADD(RULE_ATTR_PRIO, prio)
-	__ADD(RULE_ATTR_MARK, mark)
-	__ADD(RULE_ATTR_MASK, mask)
-	__ADD(RULE_ATTR_GOTO, goto)
-	__ADD(RULE_ATTR_SRC, src)
-	__ADD(RULE_ATTR_DST, dst)
-	__ADD(RULE_ATTR_DSFIELD, dsfield)
-	__ADD(RULE_ATTR_FLOW, flow)
+	__ADD(RULE_ATTR_FAMILY, family),
+	__ADD(RULE_ATTR_TABLE, table),
+	__ADD(RULE_ATTR_ACTION, action),
+	__ADD(RULE_ATTR_IIFNAME, iifname),
+	__ADD(RULE_ATTR_OIFNAME, oifname),
+	__ADD(RULE_ATTR_PRIO, prio),
+	__ADD(RULE_ATTR_MARK, mark),
+	__ADD(RULE_ATTR_MASK, mask),
+	__ADD(RULE_ATTR_GOTO, goto),
+	__ADD(RULE_ATTR_SRC, src),
+	__ADD(RULE_ATTR_DST, dst),
+	__ADD(RULE_ATTR_DSFIELD, dsfield),
+	__ADD(RULE_ATTR_FLOW, flow),
 };
 
 static char *rule_attrs2str(int attrs, char *buf, size_t len)
@@ -414,6 +486,22 @@
 	if (tmpl->ce_mask & RULE_ATTR_FLOW)
 		NLA_PUT_U32(msg, FRA_FLOW, tmpl->r_flow);
 
+	if (tmpl->ce_mask & RULE_ATTR_L3MDEV)
+		NLA_PUT_U8(msg, FRA_L3MDEV, tmpl->r_l3mdev);
+
+	if (tmpl->ce_mask & RULE_ATTR_IP_PROTO)
+		NLA_PUT_U8(msg, FRA_IP_PROTO, tmpl->r_ip_proto);
+
+	if (tmpl->ce_mask & RULE_ATTR_SPORT)
+		NLA_PUT(msg, FRA_SPORT_RANGE, sizeof(tmpl->r_sport),
+			&tmpl->r_sport);
+
+	if (tmpl->ce_mask & RULE_ATTR_DPORT)
+		NLA_PUT(msg, FRA_DPORT_RANGE, sizeof(tmpl->r_dport),
+			&tmpl->r_dport);
+
+	if (tmpl->ce_mask & RULE_ATTR_PROTOCOL)
+		NLA_PUT_U8(msg, FRA_PROTOCOL, tmpl->r_protocol);
 
 	*result = msg;
 	return 0;
@@ -690,6 +778,152 @@
 	return rule->r_action;
 }
 
+/**
+ * Set l3mdev value of the rule (FRA_L3MDEV)
+ * @arg rule		rule
+ * @arg value		value to set
+ *
+ * Set the l3mdev value to value. Currently supported values
+ * are only 1 (set it) and -1 (unset it). All other values
+ * are reserved.
+ */
+void rtnl_rule_set_l3mdev(struct rtnl_rule *rule, int value)
+{
+	if (value >= 0) {
+		rule->r_l3mdev = (uint8_t) value;
+		rule->ce_mask |= RULE_ATTR_L3MDEV;
+	} else {
+		rule->r_l3mdev = 0;
+		rule->ce_mask &= ~((uint32_t) RULE_ATTR_L3MDEV);
+	}
+}
+
+/**
+ * Get l3mdev value of the rule (FRA_L3MDEV)
+ * @arg rule		rule
+ *
+ * @return a negative error code, including -NLE_MISSING_ATTR
+ *   if the property is unset. Otherwise returns a non-negative
+ *   value. As FRA_L3MDEV is a boolean, the only expected
+ *   value at the moment is 1.
+ */
+int rtnl_rule_get_l3mdev(struct rtnl_rule *rule)
+{
+	if (!rule)
+		return -NLE_INVAL;
+	if (!(rule->ce_mask & RULE_ATTR_L3MDEV))
+		return -NLE_MISSING_ATTR;
+	return rule->r_l3mdev;
+}
+
+int rtnl_rule_set_protocol(struct rtnl_rule *rule, uint8_t protocol)
+{
+	if (protocol) {
+		rule->r_protocol = protocol;
+		rule->ce_mask |= RULE_ATTR_PROTOCOL;
+	} else {
+		rule->r_protocol = 0;
+		rule->ce_mask &= ~((uint32_t) RULE_ATTR_PROTOCOL);
+	}
+	return 0;
+}
+
+int rtnl_rule_get_protocol(struct rtnl_rule *rule, uint8_t *protocol)
+{
+	if (!(rule->ce_mask & RULE_ATTR_PROTOCOL))
+		return -NLE_INVAL;
+
+	*protocol = rule->r_protocol;
+	return 0;
+}
+
+int rtnl_rule_set_ipproto(struct rtnl_rule *rule, uint8_t ip_proto)
+{
+	if (ip_proto) {
+		rule->r_ip_proto = ip_proto;
+		rule->ce_mask |= RULE_ATTR_IP_PROTO;
+	} else {
+		rule->r_ip_proto = 0;
+		rule->ce_mask &= ~((uint32_t) RULE_ATTR_IP_PROTO);
+	}
+	return 0;
+}
+
+int rtnl_rule_get_ipproto(struct rtnl_rule *rule, uint8_t *ip_proto)
+{
+	if (!(rule->ce_mask & RULE_ATTR_IP_PROTO))
+		return -NLE_INVAL;
+
+	*ip_proto = rule->r_ip_proto;
+	return 0;
+}
+
+static int __rtnl_rule_set_port(struct fib_rule_port_range *prange,
+				uint16_t start, uint16_t end,
+				uint64_t attr, uint64_t *mask)
+{
+	if ((start && end < start) || (end && !start))
+		return -NLE_INVAL;
+
+	if (start) {
+		prange->start = start;
+		prange->end = end;
+		*mask |= attr;
+	} else {
+		prange->start = 0;
+		prange->end = 0;
+		*mask &= ~attr;
+
+	}
+	return 0;
+}
+
+int rtnl_rule_set_sport(struct rtnl_rule *rule, uint16_t sport)
+{
+	return __rtnl_rule_set_port(&rule->r_sport, sport, sport,
+				    RULE_ATTR_SPORT, &rule->ce_mask);
+}
+
+int rtnl_rule_set_sport_range(struct rtnl_rule *rule, uint16_t start,
+			      uint16_t end)
+{
+	return __rtnl_rule_set_port(&rule->r_sport, start, end,
+				    RULE_ATTR_SPORT, &rule->ce_mask);
+}
+
+int rtnl_rule_get_sport(struct rtnl_rule *rule, uint16_t *start, uint16_t *end)
+{
+	if (!(rule->ce_mask & RULE_ATTR_SPORT))
+		return -NLE_INVAL;
+
+	*start = rule->r_sport.start;
+	*end = rule->r_sport.end;
+	return 0;
+}
+
+int rtnl_rule_set_dport(struct rtnl_rule *rule, uint16_t dport)
+{
+	return __rtnl_rule_set_port(&rule->r_dport, dport, dport,
+				    RULE_ATTR_DPORT, &rule->ce_mask);
+}
+
+int rtnl_rule_set_dport_range(struct rtnl_rule *rule, uint16_t start,
+			      uint16_t end)
+{
+	return __rtnl_rule_set_port(&rule->r_dport, start, end,
+				    RULE_ATTR_DPORT, &rule->ce_mask);
+}
+
+int rtnl_rule_get_dport(struct rtnl_rule *rule, uint16_t *start, uint16_t *end)
+{
+	if (!(rule->ce_mask & RULE_ATTR_DPORT))
+		return -NLE_INVAL;
+
+	*start = rule->r_dport.start;
+	*end = rule->r_dport.end;
+	return 0;
+}
+
 void rtnl_rule_set_realms(struct rtnl_rule *rule, uint32_t realms)
 {
 	rule->r_flow = realms;
@@ -729,6 +963,12 @@
 	.oo_id_attrs		= ~0,
 };
 
+static struct nl_af_group rule_groups[] = {
+	{ AF_INET,	RTNLGRP_IPV4_RULE },
+	{ AF_INET6,	RTNLGRP_IPV6_RULE },
+	{ END_OF_GROUP_LIST },
+};
+
 static struct nl_cache_ops rtnl_rule_ops = {
 	.co_name		= "route/rule",
 	.co_hdrsize		= sizeof(struct fib_rule_hdr),
@@ -742,6 +982,7 @@
 	.co_request_update	= rule_request_update,
 	.co_msg_parser		= rule_msg_parser,
 	.co_obj_ops		= &rule_obj_ops,
+	.co_groups		= rule_groups,
 };
 
 static void __init rule_init(void)
diff --git a/lib/route/tc.c b/lib/route/tc.c
index 0f150cc..35303f5 100644
--- a/lib/route/tc.c
+++ b/lib/route/tc.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/route/tc.c		Traffic Control
  *
@@ -24,6 +25,8 @@
 #include <netlink/route/tc.h>
 #include <netlink-private/route/tc-api.h>
 
+#include "netlink-private/utils.h"
+
 /** @cond SKIP */
 
 static struct nl_list_head tc_ops_list[__RTNL_TC_TYPE_MAX];
@@ -32,12 +35,13 @@
 static struct nla_policy tc_policy[TCA_MAX+1] = {
 	[TCA_KIND]	= { .type = NLA_STRING,
 			    .maxlen = TCKINDSIZ },
+	[TCA_CHAIN]	= { .type = NLA_U32 },
 	[TCA_STATS]	= { .minlen = sizeof(struct tc_stats) },
 	[TCA_STATS2]	= { .type = NLA_NESTED },
 };
 
 int tca_parse(struct nlattr **tb, int maxattr, struct rtnl_tc *g,
-	      struct nla_policy *policy)
+	      const struct nla_policy *policy)
 {
 	
 	if (g->ce_mask & TCA_ATTR_OPTS)
@@ -79,6 +83,9 @@
 	nla_strlcpy(kind, tb[TCA_KIND], sizeof(kind));
 	rtnl_tc_set_kind(tc, kind);
 
+	if (tb[TCA_CHAIN])
+	        rtnl_tc_set_chain(tc, nla_get_u32(tb[TCA_CHAIN]));
+
 	tm = nlmsg_data(n);
 	tc->tc_family  = tm->tcm_family;
 	tc->tc_ifindex = tm->tcm_ifindex;
@@ -137,6 +144,7 @@
 			tc->tc_xstats = nl_data_alloc_attr(tbs[TCA_STATS_APP]);
 			if (tc->tc_xstats == NULL)
 				return -NLE_NOMEM;
+			tc->ce_mask |= TCA_ATTR_XSTATS;
 		} else
 			goto compat_xstats;
 	} else {
@@ -201,17 +209,22 @@
 		.tcm_handle = tc->tc_handle,
 		.tcm_parent = tc->tc_parent,
 	};
-	int err = -NLE_MSGSIZE;
+	int err;
 
 	msg = nlmsg_alloc_simple(type, flags);
 	if (!msg)
 		return -NLE_NOMEM;
 
-	if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0)
-		goto nla_put_failure;
+	if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
+		err = -NLE_MSGSIZE;
+		goto out_err;
+	}
 
 	if (tc->ce_mask & TCA_ATTR_KIND)
-	    NLA_PUT_STRING(msg, TCA_KIND, tc->tc_kind);
+		NLA_PUT_STRING(msg, TCA_KIND, tc->tc_kind);
+
+	if (tc->ce_mask & TCA_ATTR_CHAIN)
+	        NLA_PUT_U32(msg, TCA_CHAIN, tc->tc_chain);
 
 	ops = rtnl_tc_get_ops(tc);
 	if (ops && (ops->to_msg_fill || ops->to_msg_fill_raw)) {
@@ -219,31 +232,32 @@
 		void *data = rtnl_tc_data(tc);
 
 		if (ops->to_msg_fill) {
-			if (!(opts = nla_nest_start(msg, TCA_OPTIONS)))
-				goto nla_put_failure;
+			if (!(opts = nla_nest_start(msg, TCA_OPTIONS))) {
+				err = -NLE_NOMEM;
+				goto out_err;
+			}
 
 			if ((err = ops->to_msg_fill(tc, data, msg)) < 0)
-				goto nla_put_failure;
+				goto out_err;
 
-			nla_nest_end(msg, opts);
+			if (strcmp("cgroup", tc->tc_kind))
+				nla_nest_end(msg, opts);
+			else
+				nla_nest_end_keep_empty(msg, opts);
 		} else if ((err = ops->to_msg_fill_raw(tc, data, msg)) < 0)
-			goto nla_put_failure;
+			goto out_err;
 	}
 
 	*result = msg;
 	return 0;
 
 nla_put_failure:
+	err = -NLE_NOMEM;
+out_err:
 	nlmsg_free(msg);
 	return err;
 }
 
-void tca_set_kind(struct rtnl_tc *t, const char *kind)
-{
-	strncpy(t->tc_kind, kind, sizeof(t->tc_kind) - 1);
-	t->ce_mask |= TCA_ATTR_KIND;
-}
-
 
 /** @endcond */
 
@@ -518,7 +532,12 @@
 	if (tc->ce_mask & TCA_ATTR_KIND)
 		return -NLE_EXIST;
 
-	strncpy(tc->tc_kind, kind, sizeof(tc->tc_kind) - 1);
+	if (   !kind
+	    || strlen (kind) >= sizeof (tc->tc_kind))
+		return -NLE_INVAL;
+
+	_nl_strncpy(tc->tc_kind, kind, sizeof(tc->tc_kind));
+
 	tc->ce_mask |= TCA_ATTR_KIND;
 
 	/* Force allocation of data */
@@ -550,12 +569,40 @@
  */
 uint64_t rtnl_tc_get_stat(struct rtnl_tc *tc, enum rtnl_tc_stat id)
 {
-	if (id < 0 || id > RTNL_TC_STATS_MAX)
+	if ((unsigned int) id > RTNL_TC_STATS_MAX)
 		return 0;
 
 	return tc->tc_stats[id];
 }
 
+/**
+ * Set the chain index of a traffic control object
+ * @arg tc		traffic control object
+ * @arg chain		chain index of traffic control object
+ *
+ */
+void rtnl_tc_set_chain(struct rtnl_tc *tc, uint32_t chain)
+{
+	tc->tc_chain = chain;
+	tc->ce_mask |= TCA_ATTR_CHAIN;
+}
+
+/**
+ * Return chain index of traffic control object
+ * @arg tc		traffic control object
+ * @arg out_value       output argument.
+ *
+ * @return 0 of the output value was successfully returned, or a negative
+ *   error code on failure.
+ */
+int rtnl_tc_get_chain(struct rtnl_tc *tc, uint32_t *out_value)
+{
+	if (!(tc->ce_mask & TCA_ATTR_CHAIN))
+		return -NLE_MISSING_ATTR;
+	*out_value = tc->tc_chain;
+	return 0;
+}
+
 /** @} */
 
 /**
@@ -563,6 +610,28 @@
  * @{
  */
 
+static const struct trans_tbl tc_stats[] = {
+	__ADD(RTNL_TC_PACKETS, packets),
+	__ADD(RTNL_TC_BYTES, bytes),
+	__ADD(RTNL_TC_RATE_BPS, rate_bps),
+	__ADD(RTNL_TC_RATE_PPS, rate_pps),
+	__ADD(RTNL_TC_QLEN, qlen),
+	__ADD(RTNL_TC_BACKLOG, backlog),
+	__ADD(RTNL_TC_DROPS, drops),
+	__ADD(RTNL_TC_REQUEUES, requeues),
+	__ADD(RTNL_TC_OVERLIMITS, overlimits),
+};
+
+char *rtnl_tc_stat2str(enum rtnl_tc_stat st, char *buf, size_t len)
+{
+	return __type2str(st, buf, len, tc_stats, ARRAY_SIZE(tc_stats));
+}
+
+int rtnl_tc_str2stat(const char *name)
+{
+	return __str2type(name, tc_stats, ARRAY_SIZE(tc_stats));
+}
+
 /**
  * Calculate time required to transmit buffer at a specific rate
  * @arg bufsize		Size of buffer to be transmited in bytes.
@@ -579,11 +648,7 @@
  */
 int rtnl_tc_calc_txtime(int bufsize, int rate)
 {
-	double tx_time_secs;
-	
-	tx_time_secs = (double) bufsize / (double) rate;
-
-	return tx_time_secs * 1000000.;
+	return ((double) bufsize / (double) rate) * 1000000.0;
 }
 
 /**
@@ -602,11 +667,7 @@
  */
 int rtnl_tc_calc_bufsize(int txtime, int rate)
 {
-	double bufsize;
-
-	bufsize = (double) txtime * (double) rate;
-
-	return bufsize / 1000000.;
+	return ((double) txtime * (double) rate) / 1000000.0;
 }
 
 /**
@@ -715,7 +776,7 @@
 
 	for (i = 0; i < RTNL_TC_RTABLE_SIZE; i++) {
 		size = adjust_size((i + 1) << cell_log, spec->rs_mpu, linktype);
-		dst[i] = nl_us2ticks(rtnl_tc_calc_txtime(size, spec->rs_rate));
+		dst[i] = nl_us2ticks(rtnl_tc_calc_txtime64(size, spec->rs_rate64));
 	}
 
 	spec->rs_cell_align = -1;
@@ -759,16 +820,24 @@
 		dst->tc_link = src->tc_link;
 	}
 
+	dst->tc_opts = NULL;
+	dst->tc_xstats = NULL;
+	dst->tc_subdata = NULL;
+	dst->ce_mask &= ~(TCA_ATTR_OPTS |
+	                  TCA_ATTR_XSTATS);
+
 	if (src->tc_opts) {
 		dst->tc_opts = nl_data_clone(src->tc_opts);
 		if (!dst->tc_opts)
 			return -NLE_NOMEM;
+		dst->ce_mask |= TCA_ATTR_OPTS;
 	}
-	
+
 	if (src->tc_xstats) {
 		dst->tc_xstats = nl_data_clone(src->tc_xstats);
 		if (!dst->tc_xstats)
 			return -NLE_NOMEM;
+		dst->ce_mask |= TCA_ATTR_XSTATS;
 	}
 
 	if (src->tc_subdata) {
@@ -872,47 +941,41 @@
 void rtnl_tc_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_tc *tc = TC_CAST(obj);
-	char *unit, fmt[64];
+	char *unit;
 	float res;
 
 	rtnl_tc_dump_details(OBJ_CAST(tc), p);
 
-	strcpy(fmt, "        %7.2f %s %10u %10u %10u %10u %10u\n");
-
-	nl_dump_line(p, 
-		"    Stats:    bytes    packets      drops overlimits" \
-		"       qlen    backlog\n");
+	nl_dump_line(p,
+	             "  stats: %-14s %-10s   %-10s %-10s %-10s %-10s\n",
+	             "bytes", "packets", "drops", "overlimits", "qlen", "backlog");
 
 	res = nl_cancel_down_bytes(tc->tc_stats[RTNL_TC_BYTES], &unit);
-	if (*unit == 'B')
-		fmt[11] = '9';
 
-	nl_dump_line(p, fmt, res, unit,
-		tc->tc_stats[RTNL_TC_PACKETS],
-		tc->tc_stats[RTNL_TC_DROPS],
-		tc->tc_stats[RTNL_TC_OVERLIMITS],
-		tc->tc_stats[RTNL_TC_QLEN],
-		tc->tc_stats[RTNL_TC_BACKLOG]);
+	nl_dump_line(p,
+	             "       %10.2f %3s   %10u   %-10u %-10u %-10u %-10u\n",
+	             res, unit,
+	             tc->tc_stats[RTNL_TC_PACKETS],
+	             tc->tc_stats[RTNL_TC_DROPS],
+	             tc->tc_stats[RTNL_TC_OVERLIMITS],
+	             tc->tc_stats[RTNL_TC_QLEN],
+	             tc->tc_stats[RTNL_TC_BACKLOG]);
 
 	res = nl_cancel_down_bytes(tc->tc_stats[RTNL_TC_RATE_BPS], &unit);
 
-	strcpy(fmt, "        %7.2f %s/s%9u pps");
-
-	if (*unit == 'B')
-		fmt[11] = '9';
-
-	nl_dump_line(p, fmt, res, unit, tc->tc_stats[RTNL_TC_RATE_PPS]);
-
-	tc_dump(tc, NL_DUMP_LINE, p);
-	nl_dump(p, "\n");
+	nl_dump_line(p,
+	             "       %10.2f %3s/s %10u/s\n",
+	             res,
+	             unit,
+	             tc->tc_stats[RTNL_TC_RATE_PPS]);
 }
 
-int rtnl_tc_compare(struct nl_object *aobj, struct nl_object *bobj,
-		    uint32_t attrs, int flags)
+uint64_t rtnl_tc_compare(struct nl_object *aobj, struct nl_object *bobj,
+			 uint64_t attrs, int flags)
 {
 	struct rtnl_tc *a = TC_CAST(aobj);
 	struct rtnl_tc *b = TC_CAST(bobj);
-	int diff = 0;
+	uint64_t diff = 0;
 
 #define TC_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, TCA_ATTR_##ATTR, a, b, EXPR)
 
@@ -995,6 +1058,19 @@
 }
 
 /**
+ * Returns the private data of the traffic control object.
+ * Contrary to rtnl_tc_data(), this returns NULL if the data is
+ * not yet allocated
+ * @arg tc		traffic control object
+ *
+ * @return pointer to the private data or NULL if not allocated.
+ */
+void *rtnl_tc_data_peek(struct rtnl_tc *tc)
+{
+	return tc->tc_subdata ? nl_data_get(tc->tc_subdata) : NULL;
+}
+
+/**
  * Return pointer to private data of traffic control object
  * @arg tc		traffic control object
  *
@@ -1009,9 +1085,6 @@
 		size_t size;
 
 		if (!tc->tc_ops) {
-			if (!tc->tc_kind)
-				BUG();
-
 			if (!rtnl_tc_get_ops(tc))
 				return NULL;
 		}
@@ -1030,6 +1103,7 @@
  * Check traffic control object type and return private data section 
  * @arg tc		traffic control object
  * @arg ops		expected traffic control object operations
+ * @arg err		the place where saves the error code if fails
  *
  * Checks whether the traffic control object matches the type
  * specified with the traffic control object operations. If the
@@ -1040,8 +1114,10 @@
  *
  * @return Pointer to private tc data or NULL if type mismatches.
  */
-void *rtnl_tc_data_check(struct rtnl_tc *tc, struct rtnl_tc_ops *ops)
+void *rtnl_tc_data_check(struct rtnl_tc *tc, struct rtnl_tc_ops *ops, int *err)
 {
+	void *ret;
+
 	if (tc->tc_ops != ops) {
 		char buf[64];
 
@@ -1050,10 +1126,18 @@
 			 tc, ops->to_kind, tc->tc_ops->to_kind);
 		APPBUG(buf);
 
+		if (err)
+			*err = -NLE_OPNOTSUPP;
 		return NULL;
 	}
 
-	return rtnl_tc_data(tc);
+	ret = rtnl_tc_data(tc);
+	if (ret == NULL) {
+		if (err)
+			*err = -NLE_NOMEM;
+	}
+
+	return ret;
 }
 
 struct nl_af_group tc_groups[] = {
diff --git a/lib/socket.c b/lib/socket.c
index 5f61b38..cfb0743 100644
--- a/lib/socket.c
+++ b/lib/socket.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/socket.c		Netlink Socket
  *
@@ -29,8 +30,11 @@
 
 #include "defs.h"
 
+#include "sys/socket.h"
+
 #include <netlink-private/netlink.h>
 #include <netlink-private/socket.h>
+#include <netlink-private/utils.h>
 #include <netlink/netlink.h>
 #include <netlink/utils.h>
 #include <netlink/handlers.h>
@@ -106,15 +110,14 @@
 
 			nl_write_unlock(&port_map_lock);
 
-			return pid + (((uint32_t)n) << 22);
+			/* ensure we don't return zero. */
+			pid = pid + (((uint32_t)n) << 22);
+			return pid ? pid : 1024;
 		}
 	}
 
 	nl_write_unlock(&port_map_lock);
-
-	/* Out of sockets in our own PID namespace, what to do? FIXME */
-	NL_DBG(1, "Warning: Ran out of unique local port namespace\n");
-	return UINT32_MAX;
+	return 0;
 }
 
 static void release_local_port(uint32_t port)
@@ -122,9 +125,6 @@
 	int nr;
 	uint32_t mask;
 
-	if (port == UINT32_MAX)
-		return;
-
 	BUG_ON(port == 0);
 
 	nr = port >> 22;
@@ -165,7 +165,7 @@
 	nr /= 32;
 
 	/*
-	BUG_ON(port == UINT32_MAX || port == 0 || (getpid() & 0x3FFFFF) != (port & 0x3FFFFF));
+	BUG_ON(port == 0 || (getpid() & 0x3FFFFF) != (port & 0x3FFFFF));
 	BUG_ON(used_ports[nr] & mask);
 	*/
 
@@ -190,7 +190,7 @@
 	sk->s_cb = nl_cb_get(cb);
 	sk->s_local.nl_family = AF_NETLINK;
 	sk->s_peer.nl_family = AF_NETLINK;
-	sk->s_seq_expect = sk->s_seq_next = time(0);
+	sk->s_seq_expect = sk->s_seq_next = time(NULL);
 
 	/* the port is 0 (unspecified), meaning NL_OWN_PORT */
 	sk->s_flags = NL_OWN_PORT;
@@ -206,18 +206,18 @@
 struct nl_sock *nl_socket_alloc(void)
 {
 	struct nl_cb *cb;
-        struct nl_sock *sk;
+	struct nl_sock *sk;
 
 	cb = nl_cb_alloc(default_cb);
 	if (!cb)
 		return NULL;
 
-        /* will increment cb reference count on success */
+	/* will increment cb reference count on success */
 	sk = __alloc_socket(cb);
 
-        nl_cb_put(cb);
+	nl_cb_put(cb);
 
-        return sk;
+	return sk;
 }
 
 /**
@@ -335,16 +335,24 @@
 	return (sk->s_local.nl_pid == 0);
 }
 
-uint32_t _nl_socket_generate_local_port_no_release(struct nl_sock *sk)
+uint32_t _nl_socket_set_local_port_no_release(struct nl_sock *sk, int generate_other)
 {
 	uint32_t port;
 
 	/* reset the port to generate_local_port(), but do not release
 	 * the previously generated port. */
 
-	port = generate_local_port();
-	sk->s_flags &= ~NL_OWN_PORT;
+	if (generate_other)
+		port = generate_local_port();
+	else
+		port = 0;
 	sk->s_local.nl_pid = port;
+	if (port == 0) {
+		/* failed to find an unsed port. Restore the socket to have an
+		 * unspecified port. */
+		sk->s_flags |= NL_OWN_PORT;
+	} else
+		sk->s_flags &= ~NL_OWN_PORT;
 	return port;
 }
 /** \endcond */
@@ -357,6 +365,8 @@
 uint32_t nl_socket_get_local_port(const struct nl_sock *sk)
 {
 	if (sk->s_local.nl_pid == 0) {
+		struct nl_sock *sk_mutable = (struct nl_sock *) sk;
+
 		/* modify the const argument sk. This is justified, because
 		 * nobody ever saw the local_port from externally. So, we
 		 * initilize it on first use.
@@ -366,7 +376,15 @@
 		 * is not automatically threadsafe anyway, so the user is not
 		 * allowed to do that.
 		 */
-		return _nl_socket_generate_local_port_no_release((struct nl_sock *) sk);
+		sk_mutable->s_local.nl_pid = generate_local_port();
+		if (sk_mutable->s_local.nl_pid == 0) {
+			/* could not generate a local port. Assign UINT32_MAX to preserve
+			 * backward compatibility. A user who cares can clear that anyway
+			 * with nl_socket_set_local_port(). */
+			sk_mutable->s_local.nl_pid = UINT32_MAX;
+			sk_mutable->s_flags |= NL_OWN_PORT;
+		} else
+			sk_mutable->s_flags &= ~NL_OWN_PORT;
 	}
 	return sk->s_local.nl_pid;
 }
@@ -434,6 +452,8 @@
 						 &group, sizeof(group));
 		if (err < 0) {
 			va_end(ap);
+			NL_DBG(4, "nl_socket_add_memberships(%p): setsockopt() failed with %d (%s)\n",
+				sk, errno, nl_strerror_l(errno));
 			return -nl_syserr2nlerr(errno);
 		}
 
@@ -482,6 +502,8 @@
 						 &group, sizeof(group));
 		if (err < 0) {
 			va_end(ap);
+			NL_DBG(4, "nl_socket_drop_memberships(%p): setsockopt() failed with %d (%s)\n",
+				sk, errno, nl_strerror_l(errno));
 			return -nl_syserr2nlerr(errno);
 		}
 
@@ -565,6 +587,114 @@
 }
 
 /**
+ * Set the socket file descriptor externally which initializes the
+ * socket similar to nl_connect().
+ *
+ * @arg sk         Netlink socket (required)
+ * @arg protocol   The socket protocol (optional). Linux 2.6.32 supports
+ *                 the socket option SO_PROTOCOL. In this case, you can set
+ *                 protocol to a negative value and let it autodetect.
+ *                 If you set it to a non-negative value, the detected protocol
+ *                 must match the one provided.
+ *                 To support older kernels, you must specify the protocol.
+ * @arg fd         Socket file descriptor to use (required)
+ *
+ * Set the socket file descriptor. @fd must be valid and bind'ed.
+ *
+ * This is an alternative to nl_connect(). nl_connect() creates, binds and
+ * sets the socket. With this function you can set the socket to an externally
+ * created file descriptor.
+ *
+ * @see nl_connect()
+ *
+ * @return 0 on success or a negative error code. On error, @fd is not closed but
+ * possibly unusable.
+ *
+ * @retval -NLE_BAD_SOCK Netlink socket is already connected
+ * @retval -NLE_INVAL Socket is of unexpected type
+ */
+int nl_socket_set_fd(struct nl_sock *sk, int protocol, int fd)
+{
+	int err = 0;
+	socklen_t addrlen;
+	struct sockaddr_nl local = { 0 };
+	int so_type = -1, so_protocol = -1;
+
+	if (sk->s_fd != -1)
+		return -NLE_BAD_SOCK;
+	if (fd < 0)
+		return -NLE_INVAL;
+
+	addrlen = sizeof(local);
+	err = getsockname(fd, (struct sockaddr *) &local,
+	                  &addrlen);
+	if (err < 0) {
+		NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockname() failed with %d (%s)\n",
+		       sk, fd, errno, nl_strerror_l(errno));
+		return -nl_syserr2nlerr(errno);
+	}
+	if (addrlen != sizeof(local))
+		return -NLE_INVAL;
+	if (local.nl_family != AF_NETLINK) {
+		NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockname() returned family %d instead of %d (AF_NETLINK)\n",
+		       sk, fd, local.nl_family, AF_NETLINK);
+		return -NLE_INVAL;
+	}
+
+	addrlen = sizeof(so_type);
+	err = getsockopt(fd, SOL_SOCKET, SO_TYPE, &so_type, &addrlen);
+	if (err < 0) {
+		NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockopt() for SO_TYPE failed with %d (%s)\n",
+		       sk, fd, errno, nl_strerror_l(errno));
+		return -nl_syserr2nlerr(errno);
+	}
+	if (addrlen != sizeof(so_type))
+		return -NLE_INVAL;
+	if (so_type != SOCK_RAW) {
+		NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockopt() returned SO_TYPE %d instead of %d (SOCK_RAW)\n",
+		       sk, fd, so_type, SOCK_RAW);
+		return -NLE_INVAL;
+	}
+
+#if SO_PROTOCOL
+	addrlen = sizeof(so_protocol);
+	err = getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &so_protocol, &addrlen);
+	if (err < 0) {
+		if (errno == ENOPROTOOPT)
+			goto no_so_protocol;
+		NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockopt() for SO_PROTOCOL failed with %d (%s)\n",
+		       sk, fd, errno, nl_strerror_l(errno));
+		return -nl_syserr2nlerr(errno);
+	}
+	if (addrlen != sizeof(so_protocol))
+		return -NLE_INVAL;
+	if (protocol >= 0 && protocol != so_protocol) {
+		NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockopt() for SO_PROTOCOL returned %d instead of %d\n",
+		       sk, fd, so_protocol, protocol);
+		return -NLE_INVAL;
+	}
+
+	if (0)
+#endif
+	{
+no_so_protocol:
+		if (protocol < 0) {
+			NL_DBG(4, "nl_socket_set_fd(%p,%d): unknown protocol and unable to detect it via SO_PROTOCOL socket option\n",
+			       sk, fd);
+			return -NLE_INVAL;
+		}
+		so_protocol = protocol;
+	}
+
+	nl_socket_set_local_port (sk, local.nl_pid);
+	sk->s_local = local;
+	sk->s_fd = fd;
+	sk->s_proto = so_protocol;
+
+	return 0;
+}
+
+/**
  * Set file descriptor of socket to non-blocking state
  * @arg sk		Netlink socket.
  *
@@ -575,8 +705,11 @@
 	if (sk->s_fd == -1)
 		return -NLE_BAD_SOCK;
 
-	if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0)
+	if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0) {
+		NL_DBG(4, "nl_socket_set_nonblocking(%p): fcntl() failed with %d (%s)\n",
+			sk, errno, nl_strerror_l(errno));
 		return -nl_syserr2nlerr(errno);
+	}
 
 	return 0;
 }
@@ -584,18 +717,23 @@
 /**
  * Enable use of MSG_PEEK when reading from socket
  * @arg sk		Netlink socket.
+ *
+ * See also NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT capability
  */
 void nl_socket_enable_msg_peek(struct nl_sock *sk)
 {
-	sk->s_flags |= NL_MSG_PEEK;
+	sk->s_flags |= (NL_MSG_PEEK | NL_MSG_PEEK_EXPLICIT);
 }
 
 /**
  * Disable use of MSG_PEEK when reading from socket
  * @arg sk		Netlink socket.
+ *
+ * See also NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT capability
  */
 void nl_socket_disable_msg_peek(struct nl_sock *sk)
 {
+	sk->s_flags |= NL_MSG_PEEK_EXPLICIT;
 	sk->s_flags &= ~NL_MSG_PEEK;
 }
 
@@ -613,8 +751,8 @@
 
 void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb)
 {
-        if (cb == NULL)
-                BUG();
+	if (cb == NULL)
+		BUG();
 
 	nl_cb_put(sk->s_cb);
 	sk->s_cb = nl_cb_get(cb);
@@ -684,18 +822,22 @@
 
 	if (sk->s_fd == -1)
 		return -NLE_BAD_SOCK;
-	
+
 	err = setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF,
 			 &txbuf, sizeof(txbuf));
-	if (err < 0)
+	if (err < 0) {
+		NL_DBG(4, "nl_socket_set_buffer_size(%p): setsockopt() failed with %d (%s)\n",
+			sk, errno, nl_strerror_l(errno));
 		return -nl_syserr2nlerr(errno);
+	}
 
 	err = setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF,
 			 &rxbuf, sizeof(rxbuf));
-	if (err < 0)
+	if (err < 0) {
+		NL_DBG(4, "nl_socket_set_buffer_size(%p): setsockopt() failed with %d (%s)\n",
+			sk, errno, nl_strerror_l(errno));
 		return -nl_syserr2nlerr(errno);
-
-	sk->s_flags |= NL_SOCK_BUFSIZE_SET;
+	}
 
 	return 0;
 }
@@ -710,6 +852,19 @@
  * socket will be able to receive. It is generally recommneded to specify
  * a buffer size no less than the size of a memory page.
  *
+ * Setting the @bufsize to zero means to use a default of 4 times getpagesize().
+ *
+ * When MSG_PEEK is enabled, the buffer size is used for the initial choice
+ * of the buffer while peeking. It still makes sense to choose an optimal value
+ * to avoid realloc().
+ *
+ * When MSG_PEEK is disabled, the buffer size is important because a too small
+ * size will lead to failure of receiving the message via nl_recvmsgs().
+ *
+ * By default, MSG_PEEK is enabled unless the user calls either nl_socket_disable_msg_peek()/
+ * nl_socket_enable_msg_peek() or sets the message buffer size to a positive value.
+ * See capability NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT for that.
+ *
  * @return 0 on success or a negative error code.
  */
 int nl_socket_set_msg_buf_size(struct nl_sock *sk, size_t bufsize)
@@ -746,8 +901,11 @@
 
 	err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED,
 			 &state, sizeof(state));
-	if (err < 0)
+	if (err < 0) {
+		NL_DBG(4, "nl_socket_set_passcred(%p): setsockopt() failed with %d (%s)\n",
+			sk, errno, nl_strerror_l(errno));
 		return -nl_syserr2nlerr(errno);
+	}
 
 	if (state)
 		sk->s_flags |= NL_SOCK_PASSCRED;
@@ -773,8 +931,11 @@
 
 	err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_PKTINFO,
 			 &state, sizeof(state));
-	if (err < 0)
+	if (err < 0) {
+		NL_DBG(4, "nl_socket_recv_pktinfo(%p): setsockopt() failed with %d (%s)\n",
+			sk, errno, nl_strerror_l(errno));
 		return -nl_syserr2nlerr(errno);
+	}
 
 	return 0;
 }
diff --git a/lib/utils.c b/lib/utils.c
index 5cc9e94..496bf3b 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/utils.c		Utility Functions
  *
@@ -25,10 +26,14 @@
  */
 
 #include <netlink-private/netlink.h>
+#include <netlink-private/utils.h>
 #include <netlink/netlink.h>
 #include <netlink/utils.h>
 #include <linux/socket.h>
 #include <stdlib.h> /* exit() */
+#ifdef HAVE_STRERROR_L
+#include <locale.h>
+#endif
 
 /**
  * Global variable indicating the desired level of debugging output.
@@ -57,7 +62,7 @@
 static void __init nl_debug_init(void)
 {
 	char *nldbg, *end;
-	
+
 	if ((nldbg = getenv("NLDBG"))) {
 		long level = strtol(nldbg, &end, 0);
 		if (nldbg != end)
@@ -73,7 +78,7 @@
 	FILE *fd;
 	char buf[128];
 
-	fd = fopen(path, "r");
+	fd = fopen(path, "re");
 	if (fd == NULL)
 		return -nl_syserr2nlerr(errno);
 
@@ -118,6 +123,32 @@
 
 	return 0;
 }
+
+const char *nl_strerror_l(int err)
+{
+	const char *buf;
+#ifdef HAVE_STRERROR_L
+	int errno_save = errno;
+	locale_t loc = newlocale(LC_MESSAGES_MASK, "", (locale_t)0);
+
+	if (loc == (locale_t)0) {
+		if (errno == ENOENT)
+			loc = newlocale(LC_MESSAGES_MASK,
+					"POSIX", (locale_t)0);
+	}
+	if (loc != (locale_t)0) {
+		buf = strerror_l(err, loc);
+		freelocale(loc);
+	} else {
+		buf = "newlocale() failed";
+	}
+
+	errno = errno_save;
+#else
+	buf = strerror(err);
+#endif
+	return buf;
+}
 /** @endcond */
 
 /**
@@ -202,11 +233,11 @@
 	case NL_BYTE_RATE:
 		frac = nl_cancel_down_bytes(rate, &unit);
 		break;
-	
+
 	case NL_BIT_RATE:
 		frac = nl_cancel_down_bits(rate, &unit);
 		break;
-	
+
 	default:
 		BUG();
 	}
@@ -375,7 +406,7 @@
 	if (*p && strcmp(p, "%") != 0)
 		return -NLE_INVAL;
 
-	return rint(d * NL_PROB_MAX);
+	return (long) (((d * NL_PROB_MAX) + 0.5));
 }
 
 /** @} */
@@ -403,14 +434,25 @@
  * Supports the environment variables:
  *   PROC_NET_PSCHED  - may point to psched file in /proc
  *   PROC_ROOT        - may point to /proc fs */ 
-static void __init get_psched_settings(void)
+static void get_psched_settings(void)
 {
 	char name[FILENAME_MAX];
 	FILE *fd;
 	int got_hz = 0;
+	static volatile int initialized = 0;
+	const char *ev;
+	NL_LOCK(mutex);
 
-	if (getenv("HZ")) {
-		long hz = strtol(getenv("HZ"), NULL, 0);
+	if (initialized == 1)
+		return;
+
+	nl_lock(&mutex);
+
+	if (initialized == 1)
+		return;
+
+	if ((ev = getenv("HZ"))) {
+		long hz = strtol(ev, NULL, 0);
 
 		if (LONG_MIN != hz && LONG_MAX != hz) {
 			user_hz = hz;
@@ -423,31 +465,30 @@
 
 	psched_hz = user_hz;
 
-	if (getenv("TICKS_PER_USEC")) {
-		double t = strtod(getenv("TICKS_PER_USEC"), NULL);
+	if ((ev = getenv("TICKS_PER_USEC"))) {
+		double t = strtod(ev, NULL);
 		ticks_per_usec = t;
 	}
 	else {
-		if (getenv("PROC_NET_PSCHED"))
-			snprintf(name, sizeof(name), "%s", getenv("PROC_NET_PSCHED"));
-		else if (getenv("PROC_ROOT"))
-			snprintf(name, sizeof(name), "%s/net/psched",
-				 getenv("PROC_ROOT"));
+		if ((ev = getenv("PROC_NET_PSCHED")))
+			snprintf(name, sizeof(name), "%s", ev);
+		else if ((ev = getenv("PROC_ROOT")))
+			snprintf(name, sizeof(name), "%s/net/psched", ev);
 		else
 			strncpy(name, "/proc/net/psched", sizeof(name) - 1);
-		
-		if ((fd = fopen(name, "r"))) {
+
+		if ((fd = fopen(name, "re"))) {
 			unsigned int ns_per_usec, ns_per_tick, nom, denom;
 
 			if (fscanf(fd, "%08x %08x %08x %08x",
 			       &ns_per_usec, &ns_per_tick, &nom, &denom) != 4) {
-                            NL_DBG(1, "Fatal error: can not read psched settings from \"%s\". " \
-                                    "Try to set TICKS_PER_USEC, PROC_NET_PSCHED or PROC_ROOT " \
-                                    "environment variables\n", name);
-                            exit(1);
-                        }
+				NL_DBG(1, "Fatal error: can not read psched settings from \"%s\". " \
+				          "Try to set TICKS_PER_USEC, PROC_NET_PSCHED or PROC_ROOT " \
+				          "environment variables\n", name);
+				exit(1);
+			}
 
-			ticks_per_usec = (double) ns_per_usec / 
+			ticks_per_usec = (double) ns_per_usec /
 					 (double) ns_per_tick;
 
 			if (nom == 1000000)
@@ -456,6 +497,9 @@
 			fclose(fd);
 		}
 	}
+	initialized = 1;
+
+	nl_unlock(&mutex);
 }
 
 
@@ -464,6 +508,7 @@
  */
 int nl_get_user_hz(void)
 {
+	get_psched_settings();
 	return user_hz;
 }
 
@@ -472,6 +517,7 @@
  */
 int nl_get_psched_hz(void)
 {
+	get_psched_settings();
 	return psched_hz;
 }
 
@@ -482,6 +528,7 @@
  */
 uint32_t nl_us2ticks(uint32_t us)
 {
+	get_psched_settings();
 	return us * ticks_per_usec;
 }
 
@@ -493,6 +540,7 @@
  */
 uint32_t nl_ticks2us(uint32_t ticks)
 {
+	get_psched_settings();
 	return ticks / ticks_per_usec;
 }
 
@@ -585,24 +633,26 @@
  */
 
 static const struct trans_tbl nlfamilies[] = {
-	__ADD(NETLINK_ROUTE,route)
-	__ADD(NETLINK_USERSOCK,usersock)
-	__ADD(NETLINK_FIREWALL,firewall)
-	__ADD(NETLINK_INET_DIAG,inetdiag)
-	__ADD(NETLINK_NFLOG,nflog)
-	__ADD(NETLINK_XFRM,xfrm)
-	__ADD(NETLINK_SELINUX,selinux)
-	__ADD(NETLINK_ISCSI,iscsi)
-	__ADD(NETLINK_AUDIT,audit)
-	__ADD(NETLINK_FIB_LOOKUP,fib_lookup)
-	__ADD(NETLINK_CONNECTOR,connector)
-	__ADD(NETLINK_NETFILTER,netfilter)
-	__ADD(NETLINK_IP6_FW,ip6_fw)
-	__ADD(NETLINK_DNRTMSG,dnrtmsg)
-	__ADD(NETLINK_KOBJECT_UEVENT,kobject_uevent)
-	__ADD(NETLINK_GENERIC,generic)
-	__ADD(NETLINK_SCSITRANSPORT,scsitransport)
-	__ADD(NETLINK_ECRYPTFS,ecryptfs)
+	__ADD(NETLINK_ROUTE,route),
+	__ADD(NETLINK_USERSOCK,usersock),
+	__ADD(NETLINK_FIREWALL,firewall),
+	__ADD(NETLINK_INET_DIAG,inetdiag),
+	__ADD(NETLINK_NFLOG,nflog),
+	__ADD(NETLINK_XFRM,xfrm),
+	__ADD(NETLINK_SELINUX,selinux),
+	__ADD(NETLINK_ISCSI,iscsi),
+	__ADD(NETLINK_AUDIT,audit),
+	__ADD(NETLINK_FIB_LOOKUP,fib_lookup),
+	__ADD(NETLINK_CONNECTOR,connector),
+	__ADD(NETLINK_NETFILTER,netfilter),
+	__ADD(NETLINK_IP6_FW,ip6_fw),
+	__ADD(NETLINK_DNRTMSG,dnrtmsg),
+	__ADD(NETLINK_KOBJECT_UEVENT,kobject_uevent),
+	__ADD(NETLINK_GENERIC,generic),
+	__ADD(NETLINK_SCSITRANSPORT,scsitransport),
+	__ADD(NETLINK_ECRYPTFS,ecryptfs),
+	__ADD(NETLINK_RDMA,rdma),
+	__ADD(NETLINK_CRYPTO,crypto),
 };
 
 char * nl_nlfamily2str(int family, char *buf, size_t size)
@@ -627,86 +677,83 @@
 
 static const struct trans_tbl llprotos[] = {
 	{0, "generic"},
-	__ADD(ARPHRD_ETHER,ether)
-	__ADD(ARPHRD_EETHER,eether)
-	__ADD(ARPHRD_AX25,ax25)
-	__ADD(ARPHRD_PRONET,pronet)
-	__ADD(ARPHRD_CHAOS,chaos)
-	__ADD(ARPHRD_IEEE802,ieee802)
-	__ADD(ARPHRD_ARCNET,arcnet)
-	__ADD(ARPHRD_APPLETLK,atalk)
-	__ADD(ARPHRD_DLCI,dlci)
-	__ADD(ARPHRD_ATM,atm)
-	__ADD(ARPHRD_METRICOM,metricom)
-	__ADD(ARPHRD_IEEE1394,ieee1394)
-#ifdef ARPHRD_EUI64
-	__ADD(ARPHRD_EUI64,eui64)
-#endif
-	__ADD(ARPHRD_INFINIBAND,infiniband)
-	__ADD(ARPHRD_SLIP,slip)
-	__ADD(ARPHRD_CSLIP,cslip)
-	__ADD(ARPHRD_SLIP6,slip6)
-	__ADD(ARPHRD_CSLIP6,cslip6)
-	__ADD(ARPHRD_RSRVD,rsrvd)
-	__ADD(ARPHRD_ADAPT,adapt)
-	__ADD(ARPHRD_ROSE,rose)
-	__ADD(ARPHRD_X25,x25)
-#ifdef ARPHRD_HWX25
-	__ADD(ARPHRD_HWX25,hwx25)
-#endif
-	__ADD(ARPHRD_CAN,can)
-	__ADD(ARPHRD_PPP,ppp)
-	__ADD(ARPHRD_HDLC,hdlc)
-	__ADD(ARPHRD_LAPB,lapb)
-	__ADD(ARPHRD_DDCMP,ddcmp)
-	__ADD(ARPHRD_RAWHDLC,rawhdlc)
-	__ADD(ARPHRD_TUNNEL,ipip)
-	__ADD(ARPHRD_TUNNEL6,tunnel6)
-	__ADD(ARPHRD_FRAD,frad)
-	__ADD(ARPHRD_SKIP,skip)
-	__ADD(ARPHRD_LOOPBACK,loopback)
-	__ADD(ARPHRD_LOCALTLK,localtlk)
-	__ADD(ARPHRD_FDDI,fddi)
-	__ADD(ARPHRD_BIF,bif)
-	__ADD(ARPHRD_SIT,sit)
-	__ADD(ARPHRD_IPDDP,ip/ddp)
-	__ADD(ARPHRD_IPGRE,gre)
-	__ADD(ARPHRD_PIMREG,pimreg)
-	__ADD(ARPHRD_HIPPI,hippi)
-	__ADD(ARPHRD_ASH,ash)
-	__ADD(ARPHRD_ECONET,econet)
-	__ADD(ARPHRD_IRDA,irda)
-	__ADD(ARPHRD_FCPP,fcpp)
-	__ADD(ARPHRD_FCAL,fcal)
-	__ADD(ARPHRD_FCPL,fcpl)
-	__ADD(ARPHRD_FCFABRIC,fcfb_0)
-	__ADD(ARPHRD_FCFABRIC+1,fcfb_1)
-	__ADD(ARPHRD_FCFABRIC+2,fcfb_2)
-	__ADD(ARPHRD_FCFABRIC+3,fcfb_3)
-	__ADD(ARPHRD_FCFABRIC+4,fcfb_4)
-	__ADD(ARPHRD_FCFABRIC+5,fcfb_5)
-	__ADD(ARPHRD_FCFABRIC+6,fcfb_6)
-	__ADD(ARPHRD_FCFABRIC+7,fcfb_7)
-	__ADD(ARPHRD_FCFABRIC+8,fcfb_8)
-	__ADD(ARPHRD_FCFABRIC+9,fcfb_9)
-	__ADD(ARPHRD_FCFABRIC+10,fcfb_10)
-	__ADD(ARPHRD_FCFABRIC+11,fcfb_11)
-	__ADD(ARPHRD_FCFABRIC+12,fcfb_12)
-	__ADD(ARPHRD_IEEE802_TR,tr)
-	__ADD(ARPHRD_IEEE80211,ieee802.11)
-	__ADD(ARPHRD_PHONET,phonet)
-#ifdef ARPHRD_CAIF
-	__ADD(ARPHRD_CAIF, caif)
-#endif
-#ifdef ARPHRD_IEEE80211_PRISM
-	__ADD(ARPHRD_IEEE80211_PRISM, ieee802.11_prism)
-#endif
-#ifdef ARPHRD_VOID
-	__ADD(ARPHRD_VOID,void)
-#endif
-#ifdef ARPHRD_NONE
-	__ADD(ARPHRD_NONE,nohdr)
-#endif
+	__ADD(ARPHRD_NETROM,netrom),
+	__ADD(ARPHRD_ETHER,ether),
+	__ADD(ARPHRD_EETHER,eether),
+	__ADD(ARPHRD_AX25,ax25),
+	__ADD(ARPHRD_PRONET,pronet),
+	__ADD(ARPHRD_CHAOS,chaos),
+	__ADD(ARPHRD_IEEE802,ieee802),
+	__ADD(ARPHRD_ARCNET,arcnet),
+	__ADD(ARPHRD_APPLETLK,atalk),
+	__ADD(ARPHRD_DLCI,dlci),
+	__ADD(ARPHRD_ATM,atm),
+	__ADD(ARPHRD_METRICOM,metricom),
+	__ADD(ARPHRD_IEEE1394,ieee1394),
+	__ADD(ARPHRD_EUI64,eui64),
+	__ADD(ARPHRD_INFINIBAND,infiniband),
+	__ADD(ARPHRD_SLIP,slip),
+	__ADD(ARPHRD_CSLIP,cslip),
+	__ADD(ARPHRD_SLIP6,slip6),
+	__ADD(ARPHRD_CSLIP6,cslip6),
+	__ADD(ARPHRD_RSRVD,rsrvd),
+	__ADD(ARPHRD_ADAPT,adapt),
+	__ADD(ARPHRD_ROSE,rose),
+	__ADD(ARPHRD_X25,x25),
+	__ADD(ARPHRD_HWX25,hwx25),
+	__ADD(ARPHRD_CAN,can),
+	__ADD(ARPHRD_PPP,ppp),
+	__ADD(ARPHRD_CISCO,cisco),
+	__ADD(ARPHRD_HDLC,hdlc),
+	__ADD(ARPHRD_LAPB,lapb),
+	__ADD(ARPHRD_DDCMP,ddcmp),
+	__ADD(ARPHRD_RAWHDLC,rawhdlc),
+	__ADD(ARPHRD_TUNNEL,ipip),
+	__ADD(ARPHRD_TUNNEL6,tunnel6),
+	__ADD(ARPHRD_FRAD,frad),
+	__ADD(ARPHRD_SKIP,skip),
+	__ADD(ARPHRD_LOOPBACK,loopback),
+	__ADD(ARPHRD_LOCALTLK,localtlk),
+	__ADD(ARPHRD_FDDI,fddi),
+	__ADD(ARPHRD_BIF,bif),
+	__ADD(ARPHRD_SIT,sit),
+	__ADD(ARPHRD_IPDDP,ip/ddp),
+	__ADD(ARPHRD_IPGRE,gre),
+	__ADD(ARPHRD_PIMREG,pimreg),
+	__ADD(ARPHRD_HIPPI,hippi),
+	__ADD(ARPHRD_ASH,ash),
+	__ADD(ARPHRD_ECONET,econet),
+	__ADD(ARPHRD_IRDA,irda),
+	__ADD(ARPHRD_FCPP,fcpp),
+	__ADD(ARPHRD_FCAL,fcal),
+	__ADD(ARPHRD_FCPL,fcpl),
+	__ADD(ARPHRD_FCFABRIC,fcfb_0),
+	__ADD(ARPHRD_FCFABRIC+1,fcfb_1),
+	__ADD(ARPHRD_FCFABRIC+2,fcfb_2),
+	__ADD(ARPHRD_FCFABRIC+3,fcfb_3),
+	__ADD(ARPHRD_FCFABRIC+4,fcfb_4),
+	__ADD(ARPHRD_FCFABRIC+5,fcfb_5),
+	__ADD(ARPHRD_FCFABRIC+6,fcfb_6),
+	__ADD(ARPHRD_FCFABRIC+7,fcfb_7),
+	__ADD(ARPHRD_FCFABRIC+8,fcfb_8),
+	__ADD(ARPHRD_FCFABRIC+9,fcfb_9),
+	__ADD(ARPHRD_FCFABRIC+10,fcfb_10),
+	__ADD(ARPHRD_FCFABRIC+11,fcfb_11),
+	__ADD(ARPHRD_FCFABRIC+12,fcfb_12),
+	__ADD(ARPHRD_IEEE802_TR,tr),
+	__ADD(ARPHRD_IEEE80211,ieee802.11),
+	__ADD(ARPHRD_IEEE80211_PRISM,ieee802.11_prism),
+	__ADD(ARPHRD_IEEE80211_RADIOTAP,ieee802.11_radiotap),
+	__ADD(ARPHRD_IEEE802154,ieee802.15.4),
+	__ADD(ARPHRD_IEEE802154_MONITOR,ieee802.15.4_monitor),
+	__ADD(ARPHRD_PHONET,phonet),
+	__ADD(ARPHRD_PHONET_PIPE,phonet_pipe),
+	__ADD(ARPHRD_CAIF,caif),
+	__ADD(ARPHRD_IP6GRE,ip6gre),
+	__ADD(ARPHRD_NETLINK,netlink),
+	__ADD(ARPHRD_6LOWPAN,6lowpan),
+	__ADD(ARPHRD_VOID,void),
+	__ADD(ARPHRD_NONE,nohdr),
 };
 
 char * nl_llproto2str(int llproto, char *buf, size_t len)
@@ -728,75 +775,75 @@
  */
 
 static const struct trans_tbl ether_protos[] = {
-	__ADD(ETH_P_LOOP,loop)
-	__ADD(ETH_P_PUP,pup)
-	__ADD(ETH_P_PUPAT,pupat)
-	__ADD(ETH_P_IP,ip)
-	__ADD(ETH_P_X25,x25)
-	__ADD(ETH_P_ARP,arp)
-	__ADD(ETH_P_BPQ,bpq)
-	__ADD(ETH_P_IEEEPUP,ieeepup)
-	__ADD(ETH_P_IEEEPUPAT,ieeepupat)
-	__ADD(ETH_P_DEC,dec)
-	__ADD(ETH_P_DNA_DL,dna_dl)
-	__ADD(ETH_P_DNA_RC,dna_rc)
-	__ADD(ETH_P_DNA_RT,dna_rt)
-	__ADD(ETH_P_LAT,lat)
-	__ADD(ETH_P_DIAG,diag)
-	__ADD(ETH_P_CUST,cust)
-	__ADD(ETH_P_SCA,sca)
-	__ADD(ETH_P_TEB,teb)
-	__ADD(ETH_P_RARP,rarp)
-	__ADD(ETH_P_ATALK,atalk)
-	__ADD(ETH_P_AARP,aarp)
+	__ADD(ETH_P_LOOP,loop),
+	__ADD(ETH_P_PUP,pup),
+	__ADD(ETH_P_PUPAT,pupat),
+	__ADD(ETH_P_IP,ip),
+	__ADD(ETH_P_X25,x25),
+	__ADD(ETH_P_ARP,arp),
+	__ADD(ETH_P_BPQ,bpq),
+	__ADD(ETH_P_IEEEPUP,ieeepup),
+	__ADD(ETH_P_IEEEPUPAT,ieeepupat),
+	__ADD(ETH_P_DEC,dec),
+	__ADD(ETH_P_DNA_DL,dna_dl),
+	__ADD(ETH_P_DNA_RC,dna_rc),
+	__ADD(ETH_P_DNA_RT,dna_rt),
+	__ADD(ETH_P_LAT,lat),
+	__ADD(ETH_P_DIAG,diag),
+	__ADD(ETH_P_CUST,cust),
+	__ADD(ETH_P_SCA,sca),
+	__ADD(ETH_P_TEB,teb),
+	__ADD(ETH_P_RARP,rarp),
+	__ADD(ETH_P_ATALK,atalk),
+	__ADD(ETH_P_AARP,aarp),
 #ifdef ETH_P_8021Q
-	__ADD(ETH_P_8021Q,802.1q)
+	__ADD(ETH_P_8021Q,802.1q),
 #endif
-	__ADD(ETH_P_IPX,ipx)
-	__ADD(ETH_P_IPV6,ipv6)
-	__ADD(ETH_P_PAUSE,pause)
-	__ADD(ETH_P_SLOW,slow)
+	__ADD(ETH_P_IPX,ipx),
+	__ADD(ETH_P_IPV6,ipv6),
+	__ADD(ETH_P_PAUSE,pause),
+	__ADD(ETH_P_SLOW,slow),
 #ifdef ETH_P_WCCP
-	__ADD(ETH_P_WCCP,wccp)
+	__ADD(ETH_P_WCCP,wccp),
 #endif
-	__ADD(ETH_P_PPP_DISC,ppp_disc)
-	__ADD(ETH_P_PPP_SES,ppp_ses)
-	__ADD(ETH_P_MPLS_UC,mpls_uc)
-	__ADD(ETH_P_MPLS_MC,mpls_mc)
-	__ADD(ETH_P_ATMMPOA,atmmpoa)
-	__ADD(ETH_P_LINK_CTL,link_ctl)
-	__ADD(ETH_P_ATMFATE,atmfate)
-	__ADD(ETH_P_PAE,pae)
-	__ADD(ETH_P_AOE,aoe)
-	__ADD(ETH_P_TIPC,tipc)
-	__ADD(ETH_P_1588,ieee1588)
-	__ADD(ETH_P_FCOE,fcoe)
-	__ADD(ETH_P_FIP,fip)
-	__ADD(ETH_P_EDSA,edsa)
-	__ADD(ETH_P_EDP2,edp2)
-	__ADD(ETH_P_802_3,802.3)
-	__ADD(ETH_P_AX25,ax25)
-	__ADD(ETH_P_ALL,all)
-	__ADD(ETH_P_802_2,802.2)
-	__ADD(ETH_P_SNAP,snap)
-	__ADD(ETH_P_DDCMP,ddcmp)
-	__ADD(ETH_P_WAN_PPP,wan_ppp)
-	__ADD(ETH_P_PPP_MP,ppp_mp)
-	__ADD(ETH_P_LOCALTALK,localtalk)
-	__ADD(ETH_P_CAN,can)
-	__ADD(ETH_P_PPPTALK,ppptalk)
-	__ADD(ETH_P_TR_802_2,tr_802.2)
-	__ADD(ETH_P_MOBITEX,mobitex)
-	__ADD(ETH_P_CONTROL,control)
-	__ADD(ETH_P_IRDA,irda)
-	__ADD(ETH_P_ECONET,econet)
-	__ADD(ETH_P_HDLC,hdlc)
-	__ADD(ETH_P_ARCNET,arcnet)
-	__ADD(ETH_P_DSA,dsa)
-	__ADD(ETH_P_TRAILER,trailer)
-	__ADD(ETH_P_PHONET,phonet)
-	__ADD(ETH_P_IEEE802154,ieee802154)
-	__ADD(ETH_P_CAIF,caif)
+	__ADD(ETH_P_PPP_DISC,ppp_disc),
+	__ADD(ETH_P_PPP_SES,ppp_ses),
+	__ADD(ETH_P_MPLS_UC,mpls_uc),
+	__ADD(ETH_P_MPLS_MC,mpls_mc),
+	__ADD(ETH_P_ATMMPOA,atmmpoa),
+	__ADD(ETH_P_LINK_CTL,link_ctl),
+	__ADD(ETH_P_ATMFATE,atmfate),
+	__ADD(ETH_P_PAE,pae),
+	__ADD(ETH_P_AOE,aoe),
+	__ADD(ETH_P_TIPC,tipc),
+	__ADD(ETH_P_1588,ieee1588),
+	__ADD(ETH_P_FCOE,fcoe),
+	__ADD(ETH_P_FIP,fip),
+	__ADD(ETH_P_EDSA,edsa),
+	__ADD(ETH_P_EDP2,edp2),
+	__ADD(ETH_P_802_3,802.3),
+	__ADD(ETH_P_AX25,ax25),
+	__ADD(ETH_P_ALL,all),
+	__ADD(ETH_P_802_2,802.2),
+	__ADD(ETH_P_SNAP,snap),
+	__ADD(ETH_P_DDCMP,ddcmp),
+	__ADD(ETH_P_WAN_PPP,wan_ppp),
+	__ADD(ETH_P_PPP_MP,ppp_mp),
+	__ADD(ETH_P_LOCALTALK,localtalk),
+	__ADD(ETH_P_CAN,can),
+	__ADD(ETH_P_PPPTALK,ppptalk),
+	__ADD(ETH_P_TR_802_2,tr_802.2),
+	__ADD(ETH_P_MOBITEX,mobitex),
+	__ADD(ETH_P_CONTROL,control),
+	__ADD(ETH_P_IRDA,irda),
+	__ADD(ETH_P_ECONET,econet),
+	__ADD(ETH_P_HDLC,hdlc),
+	__ADD(ETH_P_ARCNET,arcnet),
+	__ADD(ETH_P_DSA,dsa),
+	__ADD(ETH_P_TRAILER,trailer),
+	__ADD(ETH_P_PHONET,phonet),
+	__ADD(ETH_P_IEEE802154,ieee802154),
+	__ADD(ETH_P_CAIF,caif),
 };
 
 char *nl_ether_proto2str(int eproto, char *buf, size_t len)
@@ -1067,7 +1114,7 @@
 	for (;;) {
 		if (*p == ' ')
 			p++;
-	
+
 		t = strchr(p, ',');
 		len = t ? t - p : strlen(p);
 		for (i = 0; i < tbl_len; i++)
@@ -1148,10 +1195,51 @@
 			NL_CAPABILITY_ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE,
 			NL_CAPABILITY_ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE,
 			NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE,
-			0,
-			0,
+			NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP,
+			NL_CAPABILITY_ROUTE_ADDR_COMPARE_CACHEINFO,
+			NL_CAPABILITY_VERSION_3_2_26,
+			NL_CAPABILITY_NL_RECV_FAIL_TRUNC_NO_PEEK),
+		_NL_SET(1,
+			NL_CAPABILITY_LINK_BUILD_CHANGE_REQUEST_SET_CHANGE,
+			NL_CAPABILITY_RTNL_NEIGH_GET_FILTER_AF_UNSPEC_FIX,
+			NL_CAPABILITY_VERSION_3_2_27,
+			NL_CAPABILITY_RTNL_LINK_VLAN_PROTOCOL_SERIALZE,
+			NL_CAPABILITY_RTNL_LINK_PARSE_GRE_REMOTE,
+			NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR,
+			NL_CAPABILITY_RTNL_LINK_VXLAN_IO_COMPARE,
+			NL_CAPABILITY_NL_OBJECT_DIFF64),
+		_NL_SET (2,
+			NL_CAPABILITY_XFRM_SA_KEY_SIZE,
+			NL_CAPABILITY_RTNL_ADDR_PEER_FIX,
+			NL_CAPABILITY_VERSION_3_2_28,
+			NL_CAPABILITY_RTNL_ADDR_PEER_ID_FIX,
+			NL_CAPABILITY_NL_ADDR_FILL_SOCKADDR,
+			NL_CAPABILITY_XFRM_SEC_CTX_LEN,
+			NL_CAPABILITY_LINK_BUILD_ADD_REQUEST_SET_CHANGE,
+			NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT),
+		_NL_SET (3,
+			NL_CAPABILITY_VERSION_3_2_29,
+			NL_CAPABILITY_XFRM_SP_SEC_CTX_LEN,
+			NL_CAPABILITY_VERSION_3_3_0,
+			NL_CAPABILITY_VERSION_3_4_0,
+			NL_CAPABILITY_ROUTE_FIX_VLAN_SET_EGRESS_MAP,
+			NL_CAPABILITY_VERSION_3_5_0,
 			0,
 			0),
+		/* IMPORTANT: these capability numbers are intended to be universal and stable
+		 * for libnl3. Don't allocate new numbers on your own that differ from upstream
+		 * libnl3.
+		 *
+		 * Instead register a capability number upstream too. We will take patches
+		 * for that. We especially take patches to register a capability number that is
+		 * only implemented in your fork of libnl3.
+		 *
+		 * If you really don't want that, use capabilities in the range 0x7000 to 0x7FFF.
+		 * (NL_CAPABILITY_IS_USER_RESERVED). Upstream libnl3 will not register conflicting
+		 * capabilities in that range.
+		 *
+		 * Obviously, only backport capability numbers to libnl versions that actually
+		 * implement that capability as well. */
 #undef _NL_SET
 #undef _NL_SETV
 #undef _NL_ASSERT
diff --git a/lib/version.c b/lib/version.c
index 0dcafa0..4faae1c 100644
--- a/lib/version.c
+++ b/lib/version.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * lib/version.c	Run-time version information
  *
diff --git a/lib/xfrm/ae.c b/lib/xfrm/ae.c
new file mode 100644
index 0000000..c7baf35
--- /dev/null
+++ b/lib/xfrm/ae.c
@@ -0,0 +1,985 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *    Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *    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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  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.
+ *
+ */
+
+/**
+ * @ingroup xfrmnl
+ * @defgroup ae Attribute Element
+ * @brief
+ *
+ * The AE interface allows a user to retrieve and update various
+ * Security Association (SA) attributes such as lifetime, replay state etc.
+ *
+ * @par AE Flags
+ * @code
+ * XFRM_AE_UNSPEC
+ * XFRM_AE_RTHR=1
+ * XFRM_AE_RVAL=2
+ * XFRM_AE_LVAL=4
+ * XFRM_AE_ETHR=8
+ * XFRM_AE_CR=16
+ * XFRM_AE_CE=32
+ * XFRM_AE_CU=64
+ * @endcode
+ *
+ * @par AE Identification
+ * An AE is uniquely identified by the attributes listed below, whenever
+ * you refer to an existing AE all of the attributes must be set. There is
+ * no cache support for AE since you can retrieve the AE for any given combination
+ * of attributes mentioned below, but not all at once since they just characterize
+ * an SA.
+ *   - destination address (xfrmnl_ae_set_daddr())
+ *   - SPI (xfrmnl_ae_set_spi)
+ *   - protocol (xfrmnl_ae_set_proto)
+ *   - mark (xfrmnl_ae_set_mark)
+ *
+ * @par Changeable Attributes
+ * \anchor ae_changeable
+ *  - current lifetime (xfrmnl_ae_set_curlifetime())
+ *  - replay properties (xfrmnl_ae_set_replay_maxage(), xfrmnl_ae_set_replay_maxdiff())
+ *  - replay state (xfrmnl_ae_set_replay_state(), xfrmnl_ae_set_replay_state_esn))
+ *
+ * @par Required Caches for Dumping
+ * None
+ *
+ * @par TODO
+ * None
+ *
+ * @par 1) Retrieving AE information for a given SA tuple
+ * @code
+ * // Create a netlink socket and connect it to XFRM subsystem in
+ * the kernel to be able to send/receive info from userspace.
+ * struct nl_sock* sk = nl_socket_alloc ();
+ * nl_connect (sk, NETLINK_XFRM);
+ *
+ * // AEs can then be looked up by the SA tuple, destination address,
+ * SPI, protocol, mark:
+ * struct xfrmnl_ae *ae;
+ * xfrmnl_ae_get_kernel(sk, dst_addr, spi, proto,mark_mask, mark_value, &ae);
+ *
+ * // After successful usage, the object must be freed
+ * xfrmnl_ae_put(ae);
+ * @endcode
+ *
+ * @par 2) Updating AE
+ * @code
+ * // Allocate an empty AE handle to be filled out with the attributes
+ * // of the new AE.
+ * struct xfrmnl_ae *ae = xfrmnl_ae_alloc();
+ *
+ * // Fill out the attributes of the new AE
+ * xfrmnl_ae_set_daddr(ae, dst_addr);
+ * xfrmnl_ae_set_spi(ae, 0xDEADBEEF);
+ * xfrmnl_ae_set_proto(ae, 50);
+ * xfrmnl_ae_set_mark(ae, 0x0);
+ * xfrmnl_ae_set_saddr(ae, src_addr);
+ * xfrmnl_ae_set_curlifetime(ae, 540, 10, 0xAABB1122, 0x0);
+ *
+ * // Build the netlink message and send it to the kernel, the operation will
+ * // block until the operation has been completed. Alternatively, a netlink message
+ * // can be built using xfrmnl_ae_build_get_request () API and be sent using
+ * // nl_send_auto(). Further the result from the kernel can be parsed using
+ * // xfrmnl_ae_parse() API.
+ * xfrmnl_ae_set(sk, ae, NLM_F_REPLACE);
+ *
+ * // Free the memory
+ * xfrmnl_ae_put(ae);
+ * @endcode
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/object.h>
+#include <netlink/xfrm/ae.h>
+#include <linux/xfrm.h>
+
+/** @cond SKIP */
+#define XFRM_AE_ATTR_DADDR          0x01
+#define XFRM_AE_ATTR_SPI            0x02
+#define XFRM_AE_ATTR_PROTO          0x04
+#define XFRM_AE_ATTR_SADDR          0x08
+#define XFRM_AE_ATTR_FLAGS          0x10
+#define XFRM_AE_ATTR_REQID          0x20
+#define XFRM_AE_ATTR_MARK           0x40
+#define XFRM_AE_ATTR_LIFETIME       0x80
+#define XFRM_AE_ATTR_REPLAY_MAXAGE  0x100
+#define XFRM_AE_ATTR_REPLAY_MAXDIFF 0x200
+#define XFRM_AE_ATTR_REPLAY_STATE   0x400
+#define XFRM_AE_ATTR_FAMILY         0x800
+
+static struct nl_object_ops xfrm_ae_obj_ops;
+/** @endcond */
+
+
+static void xfrm_ae_free_data(struct nl_object *c)
+{
+	struct xfrmnl_ae* ae =   nl_object_priv (c);
+
+	if (ae == NULL)
+		return;
+
+	nl_addr_put (ae->sa_id.daddr);
+	nl_addr_put (ae->saddr);
+
+	if (ae->replay_state_esn)
+		free (ae->replay_state_esn);
+}
+
+static int xfrm_ae_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+	struct xfrmnl_ae* dst = nl_object_priv(_dst);
+	struct xfrmnl_ae* src = nl_object_priv(_src);
+
+	if (src->sa_id.daddr)
+		if ((dst->sa_id.daddr = nl_addr_clone (src->sa_id.daddr)) == NULL)
+			return -NLE_NOMEM;
+
+	if (src->saddr)
+		if ((dst->saddr = nl_addr_clone (src->saddr)) == NULL)
+			return -NLE_NOMEM;
+
+	if (src->replay_state_esn)
+	{
+		uint32_t len = sizeof (struct xfrmnl_replay_state_esn) + (sizeof (uint32_t) * src->replay_state_esn->bmp_len);
+		if ((dst->replay_state_esn = malloc (len)) == NULL)
+			return -NLE_NOMEM;
+		memcpy (dst->replay_state_esn, src->replay_state_esn, len);
+	}
+
+	return 0;
+}
+
+static uint64_t xfrm_ae_compare(struct nl_object *_a, struct nl_object *_b,
+				uint64_t attrs, int flags)
+{
+	struct xfrmnl_ae* a  =   (struct xfrmnl_ae *) _a;
+	struct xfrmnl_ae* b  =   (struct xfrmnl_ae *) _b;
+	uint64_t diff = 0;
+	int found = 0;
+
+#define XFRM_AE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, XFRM_AE_ATTR_##ATTR, a, b, EXPR)
+	diff |= XFRM_AE_DIFF(DADDR,	nl_addr_cmp(a->sa_id.daddr, b->sa_id.daddr));
+	diff |= XFRM_AE_DIFF(SPI,	a->sa_id.spi != b->sa_id.spi);
+	diff |= XFRM_AE_DIFF(PROTO,	a->sa_id.proto != b->sa_id.proto);
+	diff |= XFRM_AE_DIFF(SADDR,	nl_addr_cmp(a->saddr, b->saddr));
+	diff |= XFRM_AE_DIFF(FLAGS, a->flags != b->flags);
+	diff |= XFRM_AE_DIFF(REQID, a->reqid != b->reqid);
+	diff |= XFRM_AE_DIFF(MARK, (a->mark.v & a->mark.m) != (b->mark.v & b->mark.m));
+	diff |= XFRM_AE_DIFF(REPLAY_MAXAGE, a->replay_maxage != b->replay_maxage);
+	diff |= XFRM_AE_DIFF(REPLAY_MAXDIFF, a->replay_maxdiff != b->replay_maxdiff);
+
+	/* Compare replay states */
+	found = AVAILABLE_MISMATCH (a, b, XFRM_AE_ATTR_REPLAY_STATE);
+	if (found == 0) // attribute exists in both objects
+	{
+		if (((a->replay_state_esn != NULL) && (b->replay_state_esn == NULL)) ||
+			((a->replay_state_esn == NULL) && (b->replay_state_esn != NULL)))
+			found |= 1;
+
+		if (found == 0) // same replay type. compare actual values
+		{
+			if (a->replay_state_esn)
+			{
+				if (a->replay_state_esn->bmp_len != b->replay_state_esn->bmp_len)
+					diff |= 1;
+				else
+				{
+					uint32_t len = sizeof (struct xfrmnl_replay_state_esn) + (sizeof (uint32_t) * a->replay_state_esn->bmp_len);
+					diff |= memcmp (a->replay_state_esn, b->replay_state_esn, len);
+				}
+			}
+			else
+			{
+				if ((a->replay_state.oseq != b->replay_state.oseq) ||
+				    (a->replay_state.seq != b->replay_state.seq) ||
+				    (a->replay_state.bitmap != b->replay_state.bitmap))
+					diff |= 1;
+			}
+		}
+	}
+#undef XFRM_AE_DIFF
+
+	return diff;
+}
+
+/**
+ * @name XFRM AE Attribute Translations
+ * @{
+ */
+static const struct trans_tbl ae_attrs[] =
+{
+	__ADD(XFRM_AE_ATTR_DADDR, daddr),
+	__ADD(XFRM_AE_ATTR_SPI, spi),
+	__ADD(XFRM_AE_ATTR_PROTO, protocol),
+	__ADD(XFRM_AE_ATTR_SADDR, saddr),
+	__ADD(XFRM_AE_ATTR_FLAGS, flags),
+	__ADD(XFRM_AE_ATTR_REQID, reqid),
+	__ADD(XFRM_AE_ATTR_MARK, mark),
+	__ADD(XFRM_AE_ATTR_LIFETIME, cur_lifetime),
+	__ADD(XFRM_AE_ATTR_REPLAY_MAXAGE, replay_maxage),
+	__ADD(XFRM_AE_ATTR_REPLAY_MAXDIFF, replay_maxdiff),
+	__ADD(XFRM_AE_ATTR_REPLAY_STATE, replay_state),
+};
+
+static char* xfrm_ae_attrs2str (int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, ae_attrs, ARRAY_SIZE(ae_attrs));
+}
+/** @} */
+
+/**
+ * @name XFRM AE Flags Translations
+ * @{
+ */
+
+static const struct trans_tbl ae_flags[] = {
+	__ADD(XFRM_AE_UNSPEC, unspecified),
+	__ADD(XFRM_AE_RTHR, replay threshold),
+	__ADD(XFRM_AE_RVAL, replay value),
+	__ADD(XFRM_AE_LVAL, lifetime value),
+	__ADD(XFRM_AE_ETHR, expiry time threshold),
+	__ADD(XFRM_AE_CR, replay update event),
+	__ADD(XFRM_AE_CE, timer expiry event),
+	__ADD(XFRM_AE_CU, policy update event),
+};
+
+char* xfrmnl_ae_flags2str(int flags, char *buf, size_t len)
+{
+	return __flags2str (flags, buf, len, ae_flags, ARRAY_SIZE(ae_flags));
+}
+
+int xfrmnl_ae_str2flag(const char *name)
+{
+	return __str2flags(name, ae_flags, ARRAY_SIZE(ae_flags));
+}
+/** @} */
+
+static void xfrm_ae_dump_line(struct nl_object *a, struct nl_dump_params *p)
+{
+	char                dst[INET6_ADDRSTRLEN+5], src[INET6_ADDRSTRLEN+5];
+	struct xfrmnl_ae*   ae  =   (struct xfrmnl_ae *) a;
+	char                flags[128], buf[128];
+	time_t              add_time, use_time;
+	struct tm           *add_time_tm, *use_time_tm;
+
+	nl_dump_line(p, "src %s dst %s \n", nl_addr2str(ae->saddr, src, sizeof(src)),
+				nl_addr2str(ae->sa_id.daddr, dst, sizeof(dst)));
+
+	nl_dump_line(p, "\tproto %s spi 0x%x reqid %u ",
+				nl_ip_proto2str (ae->sa_id.proto, buf, sizeof (buf)),
+				ae->sa_id.spi, ae->reqid);
+
+	xfrmnl_ae_flags2str(ae->flags, flags, sizeof (flags));
+	nl_dump_line(p, "flags %s(0x%x) mark mask/value 0x%x/0x%x \n", flags,
+				ae->flags, ae->mark.m, ae->mark.v);
+
+	nl_dump_line(p, "\tlifetime current: \n");
+	nl_dump_line(p, "\t\tbytes %llu packets %llu \n", ae->lifetime_cur.bytes,
+				ae->lifetime_cur.packets);
+	if (ae->lifetime_cur.add_time != 0)
+	{
+		add_time = ae->lifetime_cur.add_time;
+		add_time_tm = gmtime (&add_time);
+		strftime (flags, 128, "%Y-%m-%d %H-%M-%S", add_time_tm);
+	}
+	else
+	{
+		sprintf (flags, "%s", "-");
+	}
+
+	if (ae->lifetime_cur.use_time != 0)
+	{
+		use_time = ae->lifetime_cur.use_time;
+		use_time_tm = gmtime (&use_time);
+		strftime (buf, 128, "%Y-%m-%d %H-%M-%S", use_time_tm);
+	}
+	else
+	{
+		sprintf (buf, "%s", "-");
+	}
+	nl_dump_line(p, "\t\tadd_time: %s, use_time: %s\n", flags, buf);
+
+	nl_dump_line(p, "\treplay info: \n");
+	nl_dump_line(p, "\t\tmax age %u max diff %u \n", ae->replay_maxage, ae->replay_maxdiff);
+
+	nl_dump_line(p, "\treplay state info: \n");
+	if (ae->replay_state_esn)
+	{
+		nl_dump_line(p, "\t\toseq %u seq %u oseq_hi %u seq_hi %u replay window: %u \n",
+					ae->replay_state_esn->oseq, ae->replay_state_esn->seq,
+					ae->replay_state_esn->oseq_hi, ae->replay_state_esn->seq_hi,
+					ae->replay_state_esn->replay_window);
+	}
+	else
+	{
+		nl_dump_line(p, "\t\toseq %u seq %u bitmap: %u \n", ae->replay_state.oseq,
+					ae->replay_state.seq, ae->replay_state.bitmap);
+	}
+
+	nl_dump(p, "\n");
+}
+
+static void xfrm_ae_dump_details(struct nl_object *a, struct nl_dump_params *p)
+{
+	xfrm_ae_dump_line(a, p);
+}
+
+static void xfrm_ae_dump_stats(struct nl_object *a, struct nl_dump_params *p)
+{
+	xfrm_ae_dump_details(a, p);
+}
+
+
+static int build_xfrm_ae_message(struct xfrmnl_ae *tmpl, int cmd, int flags,
+			   struct nl_msg **result)
+{
+	struct nl_msg*          msg;
+	struct xfrm_aevent_id   ae_id;
+
+	if (!(tmpl->ce_mask & XFRM_AE_ATTR_DADDR) ||
+		!(tmpl->ce_mask & XFRM_AE_ATTR_SPI) ||
+		!(tmpl->ce_mask & XFRM_AE_ATTR_PROTO))
+		return -NLE_MISSING_ATTR;
+
+	memcpy (&ae_id.sa_id.daddr, nl_addr_get_binary_addr (tmpl->sa_id.daddr), sizeof (uint8_t) * nl_addr_get_len (tmpl->sa_id.daddr));
+	ae_id.sa_id.spi    = htonl(tmpl->sa_id.spi);
+	ae_id.sa_id.family = tmpl->sa_id.family;
+	ae_id.sa_id.proto  = tmpl->sa_id.proto;
+
+	if (tmpl->ce_mask & XFRM_AE_ATTR_SADDR)
+		memcpy (&ae_id.saddr, nl_addr_get_binary_addr (tmpl->saddr), sizeof (uint8_t) * nl_addr_get_len (tmpl->saddr));
+
+	if (tmpl->ce_mask & XFRM_AE_ATTR_FLAGS)
+		ae_id.flags    = tmpl->flags;
+
+	if (tmpl->ce_mask & XFRM_AE_ATTR_REQID)
+		ae_id.reqid    = tmpl->reqid;
+
+	msg = nlmsg_alloc_simple(cmd, flags);
+	if (!msg)
+		return -NLE_NOMEM;
+
+	if (nlmsg_append(msg, &ae_id, sizeof(ae_id), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	if (tmpl->ce_mask & XFRM_AE_ATTR_MARK)
+		NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrmnl_mark), &tmpl->mark);
+
+	if (tmpl->ce_mask & XFRM_AE_ATTR_LIFETIME)
+		NLA_PUT (msg, XFRMA_LTIME_VAL, sizeof (struct xfrmnl_lifetime_cur), &tmpl->lifetime_cur);
+
+	if (tmpl->ce_mask & XFRM_AE_ATTR_REPLAY_MAXAGE)
+		NLA_PUT_U32 (msg, XFRMA_ETIMER_THRESH, tmpl->replay_maxage);
+
+	if (tmpl->ce_mask & XFRM_AE_ATTR_REPLAY_MAXDIFF)
+		NLA_PUT_U32 (msg, XFRMA_REPLAY_THRESH, tmpl->replay_maxdiff);
+
+	if (tmpl->ce_mask & XFRM_AE_ATTR_REPLAY_STATE) {
+		if (tmpl->replay_state_esn) {
+			uint32_t len = sizeof (struct xfrm_replay_state_esn) + (sizeof (uint32_t) * tmpl->replay_state_esn->bmp_len);
+			NLA_PUT (msg, XFRMA_REPLAY_ESN_VAL, len, tmpl->replay_state_esn);
+		}
+		else {
+			NLA_PUT (msg, XFRMA_REPLAY_VAL, sizeof (struct xfrmnl_replay_state), &tmpl->replay_state);
+		}
+	}
+
+	*result = msg;
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+/**
+ * @name XFRM AE Update
+ * @{
+ */
+
+int xfrmnl_ae_set(struct nl_sock* sk, struct xfrmnl_ae* ae, int flags)
+{
+	int err;
+	struct nl_msg *msg;
+
+	if ((err = build_xfrm_ae_message(ae, XFRM_MSG_NEWAE, flags|NLM_F_REPLACE, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return nl_wait_for_ack(sk);
+}
+
+/** @} */
+
+/**
+ * @name XFRM AE Object Allocation/Freeage
+ * @{
+ */
+
+struct xfrmnl_ae* xfrmnl_ae_alloc(void)
+{
+	return (struct xfrmnl_ae*) nl_object_alloc(&xfrm_ae_obj_ops);
+}
+
+void xfrmnl_ae_put(struct xfrmnl_ae* ae)
+{
+	nl_object_put((struct nl_object *) ae);
+}
+
+/** @} */
+
+static struct nla_policy xfrm_ae_policy[XFRMA_MAX+1] = {
+	[XFRMA_LTIME_VAL]       = { .minlen = sizeof(struct xfrm_lifetime_cur) },
+	[XFRMA_REPLAY_VAL]      = { .minlen = sizeof(struct xfrm_replay_state) },
+	[XFRMA_REPLAY_THRESH]   = { .type = NLA_U32 },
+	[XFRMA_ETIMER_THRESH]   = { .type = NLA_U32 },
+	[XFRMA_SRCADDR]         = { .minlen = sizeof(xfrm_address_t) },
+	[XFRMA_MARK]            = { .minlen = sizeof(struct xfrm_mark) },
+	[XFRMA_REPLAY_ESN_VAL]  = { .minlen = sizeof(struct xfrm_replay_state_esn) },
+};
+
+int xfrmnl_ae_parse(struct nlmsghdr *n, struct xfrmnl_ae **result)
+{
+	struct xfrmnl_ae*    ae;
+	struct nlattr           *tb[XFRMA_MAX + 1];
+	struct xfrm_aevent_id*  ae_id;
+	int err;
+
+	ae = xfrmnl_ae_alloc();
+	if (!ae) {
+		err = -NLE_NOMEM;
+		goto errout;
+	}
+
+	ae->ce_msgtype = n->nlmsg_type;
+	ae_id = nlmsg_data(n);
+
+	err = nlmsg_parse(n, sizeof(struct xfrm_aevent_id), tb, XFRMA_MAX, xfrm_ae_policy);
+	if (err < 0)
+		goto errout;
+
+	ae->sa_id.daddr = nl_addr_build(ae_id->sa_id.family, &ae_id->sa_id.daddr, sizeof (ae_id->sa_id.daddr));
+	ae->sa_id.family= ae_id->sa_id.family;
+	ae->sa_id.spi   = ntohl(ae_id->sa_id.spi);
+	ae->sa_id.proto = ae_id->sa_id.proto;
+	ae->saddr       = nl_addr_build(ae_id->sa_id.family, &ae_id->saddr, sizeof (ae_id->saddr));
+	ae->reqid       = ae_id->reqid;
+	ae->flags       = ae_id->flags;
+	ae->ce_mask |= (XFRM_AE_ATTR_DADDR | XFRM_AE_ATTR_FAMILY | XFRM_AE_ATTR_SPI |
+					XFRM_AE_ATTR_PROTO | XFRM_AE_ATTR_SADDR | XFRM_AE_ATTR_REQID |
+					XFRM_AE_ATTR_FLAGS);
+
+	if (tb[XFRMA_MARK]) {
+		struct xfrm_mark* m =   nla_data(tb[XFRMA_MARK]);
+		ae->mark.m  =   m->m;
+		ae->mark.v  =   m->v;
+		ae->ce_mask |= XFRM_AE_ATTR_MARK;
+	}
+
+	if (tb[XFRMA_LTIME_VAL]) {
+		struct xfrm_lifetime_cur* cur =   nla_data(tb[XFRMA_LTIME_VAL]);
+		ae->lifetime_cur.bytes      =   cur->bytes;
+		ae->lifetime_cur.packets    =   cur->packets;
+		ae->lifetime_cur.add_time   =   cur->add_time;
+		ae->lifetime_cur.use_time   =   cur->use_time;
+		ae->ce_mask |= XFRM_AE_ATTR_LIFETIME;
+	}
+
+	if (tb[XFRM_AE_ETHR]) {
+		ae->replay_maxage       =   *(uint32_t*)nla_data(tb[XFRM_AE_ETHR]);
+		ae->ce_mask |= XFRM_AE_ATTR_REPLAY_MAXAGE;
+	}
+
+	if (tb[XFRM_AE_RTHR]) {
+		ae->replay_maxdiff      =   *(uint32_t*)nla_data(tb[XFRM_AE_RTHR]);
+		ae->ce_mask |= XFRM_AE_ATTR_REPLAY_MAXDIFF;
+	}
+
+	if (tb[XFRMA_REPLAY_ESN_VAL]) {
+		struct xfrm_replay_state_esn* esn =  nla_data (tb[XFRMA_REPLAY_ESN_VAL]);
+		uint32_t len = sizeof (struct xfrmnl_replay_state_esn) +  (sizeof (uint32_t) * esn->bmp_len);
+
+		if ((ae->replay_state_esn = calloc (1, len)) == NULL) {
+			err = -ENOMEM;
+			goto errout;
+		}
+		ae->replay_state_esn->oseq       =  esn->oseq;
+		ae->replay_state_esn->seq        =  esn->seq;
+		ae->replay_state_esn->oseq_hi    =  esn->oseq_hi;
+		ae->replay_state_esn->seq_hi     =  esn->seq_hi;
+		ae->replay_state_esn->replay_window   =   esn->replay_window;
+		ae->replay_state_esn->bmp_len    =   esn->bmp_len;
+		memcpy (ae->replay_state_esn->bmp, esn->bmp, sizeof (uint32_t) * esn->bmp_len);
+		ae->ce_mask |= XFRM_AE_ATTR_REPLAY_STATE;
+	}
+	else
+	{
+		struct xfrm_replay_state* replay_state = nla_data (tb[XFRMA_REPLAY_VAL]);
+		ae->replay_state.oseq       =   replay_state->oseq;
+		ae->replay_state.seq        =   replay_state->seq;
+		ae->replay_state.bitmap     =   replay_state->bitmap;
+		ae->ce_mask |= XFRM_AE_ATTR_REPLAY_STATE;
+
+		ae->replay_state_esn = NULL;
+	}
+
+	*result = ae;
+	return 0;
+
+errout:
+	xfrmnl_ae_put(ae);
+	return err;
+}
+
+static int xfrm_ae_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+				struct nlmsghdr *n, struct nl_parser_param *pp)
+{
+	struct xfrmnl_ae*    ae;
+	int err;
+
+	if ((err = xfrmnl_ae_parse(n, &ae)) < 0)
+		return err;
+
+	err = pp->pp_cb((struct nl_object *) ae, pp);
+
+	xfrmnl_ae_put(ae);
+	return err;
+}
+
+/**
+ * @name XFRM AE Get
+ * @{
+ */
+
+int xfrmnl_ae_build_get_request(struct nl_addr* daddr, unsigned int spi, unsigned int protocol,
+                                unsigned int mark_mask, unsigned int mark_value, struct nl_msg **result)
+{
+	struct nl_msg *msg;
+	struct xfrm_aevent_id   ae_id;
+	struct xfrmnl_mark   mark;
+
+	if (!daddr || !spi)
+	{
+		fprintf(stderr, "APPLICATION BUG: %s:%d:%s: A valid destination address, spi must be specified\n",
+				__FILE__, __LINE__, __func__);
+		assert(0);
+		return -NLE_MISSING_ATTR;
+	}
+
+	memset(&ae_id, 0, sizeof(ae_id));
+	memcpy (&ae_id.sa_id.daddr, nl_addr_get_binary_addr (daddr), sizeof (uint8_t) * nl_addr_get_len (daddr));
+	ae_id.sa_id.spi    = htonl(spi);
+	ae_id.sa_id.family = nl_addr_get_family (daddr);
+	ae_id.sa_id.proto  = protocol;
+
+	if (!(msg = nlmsg_alloc_simple(XFRM_MSG_GETAE, 0)))
+		return -NLE_NOMEM;
+
+	if (nlmsg_append(msg, &ae_id, sizeof(ae_id), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	mark.m  =   mark_mask;
+	mark.v  =   mark_value;
+	NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrmnl_mark), &mark);
+
+	*result = msg;
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+int xfrmnl_ae_get_kernel(struct nl_sock* sock, struct nl_addr* daddr, unsigned int spi, unsigned int protocol,
+                         unsigned int mark_mask, unsigned int mark_value, struct xfrmnl_ae** result)
+{
+	struct nl_msg *msg = NULL;
+	struct nl_object *obj;
+	int err;
+
+	if ((err = xfrmnl_ae_build_get_request(daddr, spi, protocol, mark_mask, mark_value, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto(sock, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	if ((err = nl_pickup(sock, &xfrm_ae_msg_parser, &obj)) < 0)
+		return err;
+
+	/* We have used xfrm_ae_msg_parser(), object is definitely a xfrm ae */
+	*result = (struct xfrmnl_ae *) obj;
+
+	/* If an object has been returned, we also need to wait for the ACK */
+	if (err == 0 && obj)
+		nl_wait_for_ack(sock);
+
+	return 0;
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+static inline int __assign_addr(struct xfrmnl_ae* ae, struct nl_addr **pos,
+					struct nl_addr *new, int flag, int nocheck)
+{
+	if (!nocheck) {
+		if (ae->ce_mask & XFRM_AE_ATTR_FAMILY) {
+			if (nl_addr_get_family (new) != ae->sa_id.family)
+				return -NLE_AF_MISMATCH;
+		} else {
+			ae->sa_id.family = nl_addr_get_family (new);
+			ae->ce_mask |= XFRM_AE_ATTR_FAMILY;
+		}
+	}
+
+	if (*pos)
+		nl_addr_put(*pos);
+
+	nl_addr_get(new);
+	*pos = new;
+
+	ae->ce_mask |= flag;
+
+	return 0;
+}
+
+
+struct nl_addr* xfrmnl_ae_get_daddr (struct xfrmnl_ae* ae)
+{
+	if (ae->ce_mask & XFRM_AE_ATTR_DADDR)
+		return ae->sa_id.daddr;
+	else
+		return NULL;
+}
+
+int xfrmnl_ae_set_daddr (struct xfrmnl_ae* ae, struct nl_addr* addr)
+{
+	return __assign_addr(ae, &ae->sa_id.daddr, addr, XFRM_AE_ATTR_DADDR, 0);
+}
+
+int xfrmnl_ae_get_spi (struct xfrmnl_ae* ae)
+{
+	if (ae->ce_mask & XFRM_AE_ATTR_SPI)
+		return ae->sa_id.spi;
+	else
+		return -1;
+}
+
+int xfrmnl_ae_set_spi (struct xfrmnl_ae* ae, unsigned int spi)
+{
+	ae->sa_id.spi = spi;
+	ae->ce_mask |= XFRM_AE_ATTR_SPI;
+
+	return 0;
+}
+
+int xfrmnl_ae_get_family (struct xfrmnl_ae* ae)
+{
+	if (ae->ce_mask & XFRM_AE_ATTR_FAMILY)
+		return ae->sa_id.family;
+	else
+		return -1;
+}
+
+int xfrmnl_ae_set_family (struct xfrmnl_ae* ae, unsigned int family)
+{
+	ae->sa_id.family = family;
+	ae->ce_mask |= XFRM_AE_ATTR_FAMILY;
+
+	return 0;
+}
+
+int xfrmnl_ae_get_proto (struct xfrmnl_ae* ae)
+{
+	if (ae->ce_mask & XFRM_AE_ATTR_PROTO)
+		return ae->sa_id.proto;
+	else
+		return -1;
+}
+
+int xfrmnl_ae_set_proto (struct xfrmnl_ae* ae, unsigned int protocol)
+{
+	ae->sa_id.proto = protocol;
+	ae->ce_mask |= XFRM_AE_ATTR_PROTO;
+
+	return 0;
+}
+
+struct nl_addr* xfrmnl_ae_get_saddr (struct xfrmnl_ae* ae)
+{
+	if (ae->ce_mask & XFRM_AE_ATTR_SADDR)
+		return ae->saddr;
+	else
+		return NULL;
+}
+
+int xfrmnl_ae_set_saddr (struct xfrmnl_ae* ae, struct nl_addr* addr)
+{
+	return 	__assign_addr(ae, &ae->saddr, addr, XFRM_AE_ATTR_SADDR, 1);
+}
+
+int xfrmnl_ae_get_flags (struct xfrmnl_ae* ae)
+{
+	if (ae->ce_mask & XFRM_AE_ATTR_FLAGS)
+		return ae->flags;
+	else
+		return -1;
+}
+
+int xfrmnl_ae_set_flags (struct xfrmnl_ae* ae, unsigned int flags)
+{
+	ae->flags = flags;
+	ae->ce_mask |= XFRM_AE_ATTR_FLAGS;
+
+	return 0;
+}
+
+int xfrmnl_ae_get_reqid (struct xfrmnl_ae* ae)
+{
+	if (ae->ce_mask & XFRM_AE_ATTR_REQID)
+		return ae->reqid;
+	else
+		return -1;
+}
+
+int xfrmnl_ae_set_reqid (struct xfrmnl_ae* ae, unsigned int reqid)
+{
+	ae->reqid = reqid;
+	ae->ce_mask |= XFRM_AE_ATTR_REQID;
+
+	return 0;
+}
+
+int xfrmnl_ae_get_mark (struct xfrmnl_ae* ae, unsigned int* mark_mask, unsigned int* mark_value)
+{
+	if (mark_mask == NULL || mark_value == NULL)
+		return -1;
+
+	if (ae->ce_mask & XFRM_AE_ATTR_MARK)
+	{
+		*mark_mask  =   ae->mark.m;
+		*mark_value  =   ae->mark.v;
+
+		return 0;
+	}
+	else
+		return -1;
+}
+
+int xfrmnl_ae_set_mark (struct xfrmnl_ae* ae, unsigned int value, unsigned int mask)
+{
+	ae->mark.v  = value;
+	ae->mark.m  = mask;
+	ae->ce_mask |= XFRM_AE_ATTR_MARK;
+
+	return 0;
+}
+
+int xfrmnl_ae_get_curlifetime (struct xfrmnl_ae* ae, unsigned long long int* curr_bytes,
+                               unsigned long long int* curr_packets, unsigned long long int* curr_add_time,
+                               unsigned long long int* curr_use_time)
+{
+	if (curr_bytes == NULL || curr_packets == NULL || curr_add_time == NULL || curr_use_time == NULL)
+		return -1;
+
+	if (ae->ce_mask & XFRM_AE_ATTR_LIFETIME)
+	{
+		*curr_bytes     =   ae->lifetime_cur.bytes;
+		*curr_packets   =   ae->lifetime_cur.packets;
+		*curr_add_time  =   ae->lifetime_cur.add_time;
+		*curr_use_time  =   ae->lifetime_cur.use_time;
+
+		return 0;
+	}
+	else
+		return -1;
+}
+
+int xfrmnl_ae_set_curlifetime (struct xfrmnl_ae* ae, unsigned long long int curr_bytes,
+                               unsigned long long int curr_packets, unsigned long long int curr_add_time,
+                               unsigned long long int curr_use_time)
+{
+	ae->lifetime_cur.bytes = curr_bytes;
+	ae->lifetime_cur.packets = curr_packets;
+	ae->lifetime_cur.add_time = curr_add_time;
+	ae->lifetime_cur.use_time = curr_use_time;
+	ae->ce_mask |= XFRM_AE_ATTR_LIFETIME;
+
+	return 0;
+}
+
+int xfrmnl_ae_get_replay_maxage (struct xfrmnl_ae* ae)
+{
+	if (ae->ce_mask & XFRM_AE_ATTR_REPLAY_MAXAGE)
+		return ae->replay_maxage;
+	else
+		return -1;
+}
+
+int xfrmnl_ae_set_replay_maxage (struct xfrmnl_ae* ae, unsigned int replay_maxage)
+{
+	ae->replay_maxage  = replay_maxage;
+	ae->ce_mask |= XFRM_AE_ATTR_REPLAY_MAXAGE;
+
+	return 0;
+}
+
+int xfrmnl_ae_get_replay_maxdiff (struct xfrmnl_ae* ae)
+{
+	if (ae->ce_mask & XFRM_AE_ATTR_REPLAY_MAXDIFF)
+		return ae->replay_maxdiff;
+	else
+		return -1;
+}
+
+int xfrmnl_ae_set_replay_maxdiff (struct xfrmnl_ae* ae, unsigned int replay_maxdiff)
+{
+	ae->replay_maxdiff  = replay_maxdiff;
+	ae->ce_mask |= XFRM_AE_ATTR_REPLAY_MAXDIFF;
+
+	return 0;
+}
+
+int xfrmnl_ae_get_replay_state (struct xfrmnl_ae* ae, unsigned int* oseq, unsigned int* seq, unsigned int* bmp)
+{
+	if (ae->ce_mask & XFRM_AE_ATTR_REPLAY_STATE)
+	{
+		if (ae->replay_state_esn == NULL)
+		{
+			*oseq   =   ae->replay_state.oseq;
+			*seq    =   ae->replay_state.seq;
+			*bmp    =   ae->replay_state.bitmap;
+
+			return 0;
+		}
+		else
+		{
+			return -1;
+		}
+	}
+	else
+		return -1;
+}
+
+int xfrmnl_ae_set_replay_state (struct xfrmnl_ae* ae, unsigned int oseq, unsigned int seq, unsigned int bitmap)
+{
+	ae->replay_state.oseq = oseq;
+	ae->replay_state.seq = seq;
+	ae->replay_state.bitmap = bitmap;
+	ae->ce_mask |= XFRM_AE_ATTR_REPLAY_STATE;
+
+	return 0;
+}
+
+int xfrmnl_ae_get_replay_state_esn(struct xfrmnl_ae* ae, unsigned int* oseq, unsigned int* seq, unsigned int* oseq_hi,
+                                   unsigned int* seq_hi, unsigned int* replay_window, unsigned int* bmp_len, unsigned int* bmp)
+{
+	if (ae->ce_mask & XFRM_AE_ATTR_REPLAY_STATE)
+	{
+		if (ae->replay_state_esn)
+		{
+			*oseq   =   ae->replay_state_esn->oseq;
+			*seq    =   ae->replay_state_esn->seq;
+			*oseq_hi=   ae->replay_state_esn->oseq_hi;
+			*seq_hi =   ae->replay_state_esn->seq_hi;
+			*replay_window  =   ae->replay_state_esn->replay_window;
+			*bmp_len        =   ae->replay_state_esn->bmp_len; // In number of 32 bit words
+			memcpy (bmp, ae->replay_state_esn->bmp, ae->replay_state_esn->bmp_len * sizeof (uint32_t));
+
+			return 0;
+		}
+		else
+		{
+			return -1;
+		}
+	}
+	else
+		return -1;
+}
+
+int xfrmnl_ae_set_replay_state_esn(struct xfrmnl_ae* ae, unsigned int oseq, unsigned int seq,
+                                   unsigned int oseq_hi, unsigned int seq_hi, unsigned int replay_window,
+                                   unsigned int bmp_len, unsigned int* bmp)
+{
+	/* Free the old replay ESN state and allocate new one */
+	if (ae->replay_state_esn)
+		free (ae->replay_state_esn);
+
+	if ((ae->replay_state_esn = calloc (1, sizeof (struct xfrmnl_replay_state_esn) + sizeof (uint32_t) * bmp_len)) == NULL)
+		return -1;
+
+	ae->replay_state_esn->oseq = oseq;
+	ae->replay_state_esn->seq = seq;
+	ae->replay_state_esn->oseq_hi = oseq_hi;
+	ae->replay_state_esn->seq_hi = seq_hi;
+	ae->replay_state_esn->replay_window = replay_window;
+	ae->replay_state_esn->bmp_len = bmp_len; // In number of 32 bit words
+	memcpy (ae->replay_state_esn->bmp, bmp, bmp_len * sizeof (uint32_t));
+	ae->ce_mask |= XFRM_AE_ATTR_REPLAY_STATE;
+
+	return 0;
+}
+
+/** @} */
+
+static struct nl_object_ops xfrm_ae_obj_ops = {
+	.oo_name        =   "xfrm/ae",
+	.oo_size        =   sizeof(struct xfrmnl_ae),
+	.oo_free_data   =   xfrm_ae_free_data,
+	.oo_clone       =   xfrm_ae_clone,
+	.oo_dump        =   {
+	                        [NL_DUMP_LINE]      =   xfrm_ae_dump_line,
+	                        [NL_DUMP_DETAILS]   =   xfrm_ae_dump_details,
+	                        [NL_DUMP_STATS]     =   xfrm_ae_dump_stats,
+	                    },
+	.oo_compare     =   xfrm_ae_compare,
+	.oo_attrs2str   =   xfrm_ae_attrs2str,
+	.oo_id_attrs    =   (XFRM_AE_ATTR_DADDR | XFRM_AE_ATTR_SPI | XFRM_AE_ATTR_PROTO),
+};
+
+/** @} */
+
diff --git a/lib/xfrm/lifetime.c b/lib/xfrm/lifetime.c
new file mode 100644
index 0000000..db761d6
--- /dev/null
+++ b/lib/xfrm/lifetime.c
@@ -0,0 +1,275 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *    Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *    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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  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.
+ *
+ */
+/**
+ * @ingroup xfrmnl
+ * @defgroup XFRM Lifetime Configuration Object
+ *
+ * Abstract data type representing XFRM SA lifetime properties
+ *
+ * @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/xfrm/lifetime.h>
+ * ~~~~
+ */
+
+#include <netlink/xfrm/lifetime.h>
+#include <netlink-private/netlink.h>
+
+static void ltime_cfg_destroy(struct xfrmnl_ltime_cfg* ltime)
+{
+	if (!ltime)
+		return;
+
+	if (ltime->refcnt != 1)
+	{
+		fprintf(stderr, "BUG: %s:%d\n", __FILE__, __LINE__);
+		assert(0);
+	}
+
+	free(ltime);
+}
+
+/**
+ * @name Creating Selector
+ * @{
+ */
+
+/**
+ * Allocate new lifetime config object.
+ * @return Newly allocated lifetime config object or NULL
+ */
+struct xfrmnl_ltime_cfg* xfrmnl_ltime_cfg_alloc()
+{
+	struct xfrmnl_ltime_cfg* ltime;
+
+	ltime = calloc(1, sizeof(struct xfrmnl_ltime_cfg));
+	if (!ltime)
+		return NULL;
+
+	ltime->refcnt = 1;
+
+	return ltime;
+}
+
+/**
+ * Clone existing lifetime config object.
+ * @arg ltime		Selector object.
+ * @return Newly allocated lifetime config object being a duplicate of the
+ *         specified lifetime config object or NULL if a failure occured.
+ */
+struct xfrmnl_ltime_cfg* xfrmnl_ltime_cfg_clone(struct xfrmnl_ltime_cfg* ltime)
+{
+	struct xfrmnl_ltime_cfg* new;
+
+	new = xfrmnl_ltime_cfg_alloc();
+	if (new)
+		memcpy ((void*)new, (void*)ltime, sizeof (struct xfrmnl_ltime_cfg));
+
+	return new;
+}
+
+/** @} */
+
+/**
+ * @name Managing Usage References
+ * @{
+ */
+
+struct xfrmnl_ltime_cfg* xfrmnl_ltime_cfg_get(struct xfrmnl_ltime_cfg* ltime)
+{
+	ltime->refcnt++;
+
+	return ltime;
+}
+
+void xfrmnl_ltime_cfg_put(struct xfrmnl_ltime_cfg* ltime)
+{
+	if (!ltime)
+		return;
+
+	if (ltime->refcnt == 1)
+		ltime_cfg_destroy(ltime);
+	else
+		ltime->refcnt--;
+}
+
+/**
+ * Check whether an lifetime config object is shared.
+ * @arg addr		Selector object.
+ * @return Non-zero if the lifetime config object is shared, otherwise 0.
+ */
+int xfrmnl_ltime_cfg_shared(struct xfrmnl_ltime_cfg* ltime)
+{
+	return ltime->refcnt > 1;
+}
+
+/** @} */
+
+/**
+ * @name Miscellaneous
+ * @{
+ */
+
+/**
+ * Compares two lifetime config objects.
+ * @arg a		A lifetime config object.
+ * @arg b		Another lifetime config object.
+ *
+ * @return Non zero if difference is found, 0 otherwise if both
+ * the objects are identical.
+ */
+int xfrmnl_ltime_cfg_cmp(struct xfrmnl_ltime_cfg* a, struct xfrmnl_ltime_cfg* b)
+{
+	/* Check for any differences */
+	if ((a->soft_byte_limit != b->soft_byte_limit) ||
+		(a->soft_packet_limit != b->soft_packet_limit) ||
+		(a->hard_byte_limit != b->hard_byte_limit) ||
+		(a->hard_packet_limit != b->hard_packet_limit) ||
+		(a->soft_add_expires_seconds != b->soft_add_expires_seconds) ||
+		(a->hard_add_expires_seconds != b->hard_add_expires_seconds) ||
+		(a->soft_use_expires_seconds != b->soft_use_expires_seconds) ||
+		(a->hard_use_expires_seconds != b->hard_use_expires_seconds))
+		return 1;
+
+	/* The objects are identical */
+	return 0;
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+unsigned long long xfrmnl_ltime_cfg_get_soft_bytelimit (struct xfrmnl_ltime_cfg* ltime)
+{
+	return ltime->soft_byte_limit;
+}
+
+int xfrmnl_ltime_cfg_set_soft_bytelimit (struct xfrmnl_ltime_cfg* ltime, unsigned long long soft_byte_limit)
+{
+	ltime->soft_byte_limit = soft_byte_limit;
+
+	return 0;
+}
+
+unsigned long long xfrmnl_ltime_cfg_get_hard_bytelimit (struct xfrmnl_ltime_cfg* ltime)
+{
+	return ltime->hard_byte_limit;
+}
+
+int xfrmnl_ltime_cfg_set_hard_bytelimit (struct xfrmnl_ltime_cfg* ltime, unsigned long long hard_byte_limit)
+{
+	ltime->hard_byte_limit = hard_byte_limit;
+
+	return 0;
+}
+
+unsigned long long xfrmnl_ltime_cfg_get_soft_packetlimit (struct xfrmnl_ltime_cfg* ltime)
+{
+	return ltime->soft_packet_limit;
+}
+
+int xfrmnl_ltime_cfg_set_soft_packetlimit (struct xfrmnl_ltime_cfg* ltime, unsigned long long soft_packet_limit)
+{
+	ltime->soft_packet_limit = soft_packet_limit;
+
+	return 0;
+}
+
+unsigned long long xfrmnl_ltime_cfg_get_hard_packetlimit (struct xfrmnl_ltime_cfg* ltime)
+{
+	return ltime->hard_packet_limit;
+}
+
+int xfrmnl_ltime_cfg_set_hard_packetlimit (struct xfrmnl_ltime_cfg* ltime, unsigned long long hard_packet_limit)
+{
+	ltime->hard_packet_limit = hard_packet_limit;
+
+	return 0;
+}
+
+unsigned long long xfrmnl_ltime_cfg_get_soft_addexpires (struct xfrmnl_ltime_cfg* ltime)
+{
+	return ltime->soft_add_expires_seconds;
+}
+
+int xfrmnl_ltime_cfg_set_soft_addexpires (struct xfrmnl_ltime_cfg* ltime, unsigned long long soft_add_expires_seconds)
+{
+	ltime->soft_add_expires_seconds = soft_add_expires_seconds;
+
+	return 0;
+}
+
+unsigned long long xfrmnl_ltime_cfg_get_hard_addexpires (struct xfrmnl_ltime_cfg* ltime)
+{
+	return ltime->hard_add_expires_seconds;
+}
+
+int xfrmnl_ltime_cfg_set_hard_addexpires (struct xfrmnl_ltime_cfg* ltime, unsigned long long hard_add_expires_seconds)
+{
+	ltime->hard_add_expires_seconds = hard_add_expires_seconds;
+
+	return 0;
+}
+
+unsigned long long xfrmnl_ltime_cfg_get_soft_useexpires (struct xfrmnl_ltime_cfg* ltime)
+{
+	return ltime->soft_use_expires_seconds;
+}
+
+int xfrmnl_ltime_cfg_set_soft_useexpires (struct xfrmnl_ltime_cfg* ltime, unsigned long long soft_use_expires_seconds)
+{
+	ltime->soft_use_expires_seconds = soft_use_expires_seconds;
+
+	return 0;
+}
+
+unsigned long long xfrmnl_ltime_cfg_get_hard_useexpires (struct xfrmnl_ltime_cfg* ltime)
+{
+	return ltime->hard_use_expires_seconds;
+}
+
+int xfrmnl_ltime_cfg_set_hard_useexpires (struct xfrmnl_ltime_cfg* ltime, unsigned long long hard_use_expires_seconds)
+{
+	ltime->hard_use_expires_seconds = hard_use_expires_seconds;
+
+	return 0;
+}
+
+/** @} */
diff --git a/lib/xfrm/sa.c b/lib/xfrm/sa.c
new file mode 100644
index 0000000..48265ba
--- /dev/null
+++ b/lib/xfrm/sa.c
@@ -0,0 +1,2233 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *    Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *    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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  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.
+ *
+ */
+
+/**
+ * @ingroup xfrmnl
+ * @defgroup sa Security Association
+ * @brief
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/object.h>
+#include <netlink/xfrm/sa.h>
+#include <netlink/xfrm/selector.h>
+#include <netlink/xfrm/lifetime.h>
+#include <time.h>
+
+#include "netlink-private/utils.h"
+
+/** @cond SKIP */
+#define XFRM_SA_ATTR_SEL            0x01
+#define XFRM_SA_ATTR_DADDR          0x02
+#define XFRM_SA_ATTR_SPI            0x04
+#define XFRM_SA_ATTR_PROTO          0x08
+#define XFRM_SA_ATTR_SADDR          0x10
+#define XFRM_SA_ATTR_LTIME_CFG      0x20
+#define XFRM_SA_ATTR_LTIME_CUR      0x40
+#define XFRM_SA_ATTR_STATS          0x80
+#define XFRM_SA_ATTR_SEQ            0x100
+#define XFRM_SA_ATTR_REQID          0x200
+#define XFRM_SA_ATTR_FAMILY         0x400
+#define XFRM_SA_ATTR_MODE           0x800
+#define XFRM_SA_ATTR_REPLAY_WIN     0x1000
+#define XFRM_SA_ATTR_FLAGS          0x2000
+#define XFRM_SA_ATTR_ALG_AEAD       0x4000
+#define XFRM_SA_ATTR_ALG_AUTH       0x8000
+#define XFRM_SA_ATTR_ALG_CRYPT      0x10000
+#define XFRM_SA_ATTR_ALG_COMP       0x20000
+#define XFRM_SA_ATTR_ENCAP          0x40000
+#define XFRM_SA_ATTR_TFCPAD         0x80000
+#define XFRM_SA_ATTR_COADDR         0x100000
+#define XFRM_SA_ATTR_MARK           0x200000
+#define XFRM_SA_ATTR_SECCTX         0x400000
+#define XFRM_SA_ATTR_REPLAY_MAXAGE  0x800000
+#define XFRM_SA_ATTR_REPLAY_MAXDIFF 0x1000000
+#define XFRM_SA_ATTR_REPLAY_STATE   0x2000000
+#define XFRM_SA_ATTR_EXPIRE         0x4000000
+
+static struct nl_cache_ops  xfrmnl_sa_ops;
+static struct nl_object_ops xfrm_sa_obj_ops;
+/** @endcond */
+
+static void xfrm_sa_alloc_data(struct nl_object *c)
+{
+	struct xfrmnl_sa* sa =   nl_object_priv (c);
+
+	if ((sa->sel = xfrmnl_sel_alloc ()) == NULL)
+		return;
+
+	if ((sa->lft = xfrmnl_ltime_cfg_alloc ()) == NULL)
+		return;
+}
+
+static void xfrm_sa_free_data(struct nl_object *c)
+{
+	struct xfrmnl_sa* sa =   nl_object_priv (c);
+
+	if (sa == NULL)
+		return;
+
+	xfrmnl_sel_put (sa->sel);
+	xfrmnl_ltime_cfg_put (sa->lft);
+	nl_addr_put (sa->id.daddr);
+	nl_addr_put (sa->saddr);
+
+	if (sa->aead)
+		free (sa->aead);
+	if (sa->auth)
+		free (sa->auth);
+	if (sa->crypt)
+		free (sa->crypt);
+	if (sa->comp)
+		free (sa->comp);
+	if (sa->encap) {
+		if (sa->encap->encap_oa)
+			nl_addr_put(sa->encap->encap_oa);
+		free(sa->encap);
+	}
+	if (sa->coaddr)
+		nl_addr_put (sa->coaddr);
+	if (sa->sec_ctx)
+		free (sa->sec_ctx);
+	if (sa->replay_state_esn)
+		free (sa->replay_state_esn);
+}
+
+static int xfrm_sa_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+	struct xfrmnl_sa*   dst = nl_object_priv(_dst);
+	struct xfrmnl_sa*   src = nl_object_priv(_src);
+	uint32_t            len = 0;
+
+	if (src->sel)
+		if ((dst->sel = xfrmnl_sel_clone (src->sel)) == NULL)
+			return -NLE_NOMEM;
+
+	if (src->lft)
+		if ((dst->lft = xfrmnl_ltime_cfg_clone (src->lft)) == NULL)
+			return -NLE_NOMEM;
+
+	if (src->id.daddr)
+		if ((dst->id.daddr = nl_addr_clone (src->id.daddr)) == NULL)
+			return -NLE_NOMEM;
+
+	if (src->saddr)
+		if ((dst->saddr = nl_addr_clone (src->saddr)) == NULL)
+			return -NLE_NOMEM;
+
+	if (src->aead)
+	{
+		len = sizeof (struct xfrmnl_algo_aead) + ((src->aead->alg_key_len + 7) / 8);
+		if ((dst->aead = calloc (1, len)) == NULL)
+			return -NLE_NOMEM;
+		memcpy ((void *)dst->aead, (void *)src->aead, len);
+	}
+
+	if (src->auth)
+	{
+		len = sizeof (struct xfrmnl_algo_auth) + ((src->auth->alg_key_len + 7) / 8);
+		if ((dst->auth = calloc (1, len)) == NULL)
+			return -NLE_NOMEM;
+		memcpy ((void *)dst->auth, (void *)src->auth, len);
+	}
+
+	if (src->crypt)
+	{
+		len = sizeof (struct xfrmnl_algo) + ((src->crypt->alg_key_len + 7) / 8);
+		if ((dst->crypt = calloc (1, len)) == NULL)
+			return -NLE_NOMEM;
+		memcpy ((void *)dst->crypt, (void *)src->crypt, len);
+	}
+
+	if (src->comp)
+	{
+		len = sizeof (struct xfrmnl_algo) + ((src->comp->alg_key_len + 7) / 8);
+		if ((dst->comp = calloc (1, len)) == NULL)
+			return -NLE_NOMEM;
+		memcpy ((void *)dst->comp, (void *)src->comp, len);
+	}
+
+	if (src->encap)
+	{
+		len = sizeof (struct xfrmnl_encap_tmpl);
+		if ((dst->encap = calloc (1, len)) == NULL)
+			return -NLE_NOMEM;
+		memcpy ((void *)dst->encap, (void *)src->encap, len);
+	}
+
+	if (src->coaddr)
+		if ((dst->coaddr = nl_addr_clone (src->coaddr)) == NULL)
+			return -NLE_NOMEM;
+
+	if (src->sec_ctx)
+	{
+		len = sizeof (*src->sec_ctx) + src->sec_ctx->ctx_len;
+		if ((dst->sec_ctx = calloc (1, len)) == NULL)
+			return -NLE_NOMEM;
+		memcpy ((void *)dst->sec_ctx, (void *)src->sec_ctx, len);
+	}
+
+	if (src->replay_state_esn)
+	{
+		len = sizeof (struct xfrmnl_replay_state_esn) + (src->replay_state_esn->bmp_len * sizeof (uint32_t));
+		if ((dst->replay_state_esn = calloc (1, len)) == NULL)
+			return -NLE_NOMEM;
+		memcpy ((void *)dst->replay_state_esn, (void *)src->replay_state_esn, len);
+	}
+
+	return 0;
+}
+
+static uint64_t xfrm_sa_compare(struct nl_object *_a, struct nl_object *_b,
+				uint64_t attrs, int flags)
+{
+	struct xfrmnl_sa* a  =   (struct xfrmnl_sa *) _a;
+	struct xfrmnl_sa* b  =   (struct xfrmnl_sa *) _b;
+	uint64_t diff = 0;
+	int found = 0;
+
+#define XFRM_SA_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, XFRM_SA_ATTR_##ATTR, a, b, EXPR)
+	diff |= XFRM_SA_DIFF(SEL,	xfrmnl_sel_cmp(a->sel, b->sel));
+	diff |= XFRM_SA_DIFF(DADDR,	nl_addr_cmp(a->id.daddr, b->id.daddr));
+	diff |= XFRM_SA_DIFF(SPI,	a->id.spi != b->id.spi);
+	diff |= XFRM_SA_DIFF(PROTO,	a->id.proto != b->id.proto);
+	diff |= XFRM_SA_DIFF(SADDR,	nl_addr_cmp(a->saddr, b->saddr));
+	diff |= XFRM_SA_DIFF(LTIME_CFG,	xfrmnl_ltime_cfg_cmp(a->lft, b->lft));
+	diff |= XFRM_SA_DIFF(REQID,	a->reqid != b->reqid);
+	diff |= XFRM_SA_DIFF(FAMILY,a->family != b->family);
+	diff |= XFRM_SA_DIFF(MODE,a->mode != b->mode);
+	diff |= XFRM_SA_DIFF(REPLAY_WIN,a->replay_window != b->replay_window);
+	diff |= XFRM_SA_DIFF(FLAGS,a->flags != b->flags);
+	diff |= XFRM_SA_DIFF(ALG_AEAD,(strcmp(a->aead->alg_name, b->aead->alg_name) ||
+	                              (a->aead->alg_key_len != b->aead->alg_key_len) ||
+	                              (a->aead->alg_icv_len != b->aead->alg_icv_len) ||
+	                              memcmp(a->aead->alg_key, b->aead->alg_key,
+	                              ((a->aead->alg_key_len + 7)/8))));
+	diff |= XFRM_SA_DIFF(ALG_AUTH,(strcmp(a->auth->alg_name, b->auth->alg_name) ||
+	                              (a->auth->alg_key_len != b->auth->alg_key_len) ||
+	                              (a->auth->alg_trunc_len != b->auth->alg_trunc_len) ||
+	                              memcmp(a->auth->alg_key, b->auth->alg_key,
+	                              ((a->auth->alg_key_len + 7)/8))));
+	diff |= XFRM_SA_DIFF(ALG_CRYPT,(strcmp(a->crypt->alg_name, b->crypt->alg_name) ||
+	                              (a->crypt->alg_key_len != b->crypt->alg_key_len) ||
+	                              memcmp(a->crypt->alg_key, b->crypt->alg_key,
+	                              ((a->crypt->alg_key_len + 7)/8))));
+	diff |= XFRM_SA_DIFF(ALG_COMP,(strcmp(a->comp->alg_name, b->comp->alg_name) ||
+	                              (a->comp->alg_key_len != b->comp->alg_key_len) ||
+	                              memcmp(a->comp->alg_key, b->comp->alg_key,
+	                              ((a->comp->alg_key_len + 7)/8))));
+	diff |= XFRM_SA_DIFF(ENCAP,((a->encap->encap_type != b->encap->encap_type) ||
+	                            (a->encap->encap_sport != b->encap->encap_sport) ||
+	                            (a->encap->encap_dport != b->encap->encap_dport) ||
+	                            nl_addr_cmp(a->encap->encap_oa, b->encap->encap_oa)));
+	diff |= XFRM_SA_DIFF(TFCPAD,a->tfcpad != b->tfcpad);
+	diff |= XFRM_SA_DIFF(COADDR,nl_addr_cmp(a->coaddr, b->coaddr));
+	diff |= XFRM_SA_DIFF(MARK,(a->mark.m != b->mark.m) ||
+	                          (a->mark.v != b->mark.v));
+	diff |= XFRM_SA_DIFF(SECCTX,((a->sec_ctx->ctx_doi != b->sec_ctx->ctx_doi) ||
+	                            (a->sec_ctx->ctx_alg != b->sec_ctx->ctx_alg) ||
+	                            (a->sec_ctx->ctx_len != b->sec_ctx->ctx_len) ||
+	                            strcmp(a->sec_ctx->ctx, b->sec_ctx->ctx)));
+	diff |= XFRM_SA_DIFF(REPLAY_MAXAGE,a->replay_maxage != b->replay_maxage);
+	diff |= XFRM_SA_DIFF(REPLAY_MAXDIFF,a->replay_maxdiff != b->replay_maxdiff);
+	diff |= XFRM_SA_DIFF(EXPIRE,a->hard != b->hard);
+
+	/* Compare replay states */
+	found = AVAILABLE_MISMATCH (a, b, XFRM_SA_ATTR_REPLAY_STATE);
+	if (found == 0) // attribute exists in both objects
+	{
+		if (((a->replay_state_esn != NULL) && (b->replay_state_esn == NULL)) ||
+		    ((a->replay_state_esn == NULL) && (b->replay_state_esn != NULL)))
+			found |= 1;
+
+		if (found == 0) // same replay type. compare actual values
+		{
+			if (a->replay_state_esn)
+			{
+				if (a->replay_state_esn->bmp_len != b->replay_state_esn->bmp_len)
+					diff |= 1;
+				else
+				{
+					uint32_t len = sizeof (struct xfrmnl_replay_state_esn) +
+					               (a->replay_state_esn->bmp_len * sizeof (uint32_t));
+					diff |= memcmp (a->replay_state_esn, b->replay_state_esn, len);
+				}
+			}
+			else
+			{
+				if ((a->replay_state.oseq != b->replay_state.oseq) ||
+				    (a->replay_state.seq != b->replay_state.seq) ||
+				    (a->replay_state.bitmap != b->replay_state.bitmap))
+					diff |= 1;
+			}
+		}
+	}
+#undef XFRM_SA_DIFF
+
+	return diff;
+}
+
+/**
+ * @name XFRM SA Attribute Translations
+ * @{
+ */
+static const struct trans_tbl sa_attrs[] = {
+	__ADD(XFRM_SA_ATTR_SEL, selector),
+	__ADD(XFRM_SA_ATTR_DADDR, daddr),
+	__ADD(XFRM_SA_ATTR_SPI, spi),
+	__ADD(XFRM_SA_ATTR_PROTO, proto),
+	__ADD(XFRM_SA_ATTR_SADDR, saddr),
+	__ADD(XFRM_SA_ATTR_LTIME_CFG, lifetime_cfg),
+	__ADD(XFRM_SA_ATTR_LTIME_CUR, lifetime_cur),
+	__ADD(XFRM_SA_ATTR_STATS, stats),
+	__ADD(XFRM_SA_ATTR_SEQ, seqnum),
+	__ADD(XFRM_SA_ATTR_REQID, reqid),
+	__ADD(XFRM_SA_ATTR_FAMILY, family),
+	__ADD(XFRM_SA_ATTR_MODE, mode),
+	__ADD(XFRM_SA_ATTR_REPLAY_WIN, replay_window),
+	__ADD(XFRM_SA_ATTR_FLAGS, flags),
+	__ADD(XFRM_SA_ATTR_ALG_AEAD, alg_aead),
+	__ADD(XFRM_SA_ATTR_ALG_AUTH, alg_auth),
+	__ADD(XFRM_SA_ATTR_ALG_CRYPT, alg_crypto),
+	__ADD(XFRM_SA_ATTR_ALG_COMP, alg_comp),
+	__ADD(XFRM_SA_ATTR_ENCAP, encap),
+	__ADD(XFRM_SA_ATTR_TFCPAD, tfcpad),
+	__ADD(XFRM_SA_ATTR_COADDR, coaddr),
+	__ADD(XFRM_SA_ATTR_MARK, mark),
+	__ADD(XFRM_SA_ATTR_SECCTX, sec_ctx),
+	__ADD(XFRM_SA_ATTR_REPLAY_MAXAGE, replay_maxage),
+	__ADD(XFRM_SA_ATTR_REPLAY_MAXDIFF, replay_maxdiff),
+	__ADD(XFRM_SA_ATTR_REPLAY_STATE, replay_state),
+	__ADD(XFRM_SA_ATTR_EXPIRE, expire),
+};
+
+static char* xfrm_sa_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str (attrs, buf, len, sa_attrs, ARRAY_SIZE(sa_attrs));
+}
+/** @} */
+
+/**
+ * @name XFRM SA Flags Translations
+ * @{
+ */
+static const struct trans_tbl sa_flags[] = {
+	__ADD(XFRM_STATE_NOECN, no ecn),
+	__ADD(XFRM_STATE_DECAP_DSCP, decap dscp),
+	__ADD(XFRM_STATE_NOPMTUDISC, no pmtu discovery),
+	__ADD(XFRM_STATE_WILDRECV, wild receive),
+	__ADD(XFRM_STATE_ICMP, icmp),
+	__ADD(XFRM_STATE_AF_UNSPEC, unspecified),
+	__ADD(XFRM_STATE_ALIGN4, align4),
+	__ADD(XFRM_STATE_ESN, esn),
+};
+
+char* xfrmnl_sa_flags2str(int flags, char *buf, size_t len)
+{
+	return __flags2str (flags, buf, len, sa_flags, ARRAY_SIZE(sa_flags));
+}
+
+int xfrmnl_sa_str2flag(const char *name)
+{
+	return __str2flags (name, sa_flags, ARRAY_SIZE(sa_flags));
+}
+/** @} */
+
+/**
+ * @name XFRM SA Mode Translations
+ * @{
+ */
+static const struct trans_tbl sa_modes[] = {
+	__ADD(XFRM_MODE_TRANSPORT, transport),
+	__ADD(XFRM_MODE_TUNNEL, tunnel),
+	__ADD(XFRM_MODE_ROUTEOPTIMIZATION, route optimization),
+	__ADD(XFRM_MODE_IN_TRIGGER, in trigger),
+	__ADD(XFRM_MODE_BEET, beet),
+};
+
+char* xfrmnl_sa_mode2str(int mode, char *buf, size_t len)
+{
+	return __type2str (mode, buf, len, sa_modes, ARRAY_SIZE(sa_modes));
+}
+
+int xfrmnl_sa_str2mode(const char *name)
+{
+	return __str2type (name, sa_modes, ARRAY_SIZE(sa_modes));
+}
+/** @} */
+
+
+static void xfrm_sa_dump_line(struct nl_object *a, struct nl_dump_params *p)
+{
+	char                dst[INET6_ADDRSTRLEN+5], src[INET6_ADDRSTRLEN+5];
+	struct xfrmnl_sa*   sa  =   (struct xfrmnl_sa *) a;
+	char                flags[128], mode[128];
+	time_t              add_time, use_time;
+	struct tm           *add_time_tm, *use_time_tm;
+
+	nl_dump_line(p, "src %s dst %s family: %s\n", nl_addr2str(sa->saddr, src, sizeof(src)),
+	             nl_addr2str(sa->id.daddr, dst, sizeof(dst)),
+	             nl_af2str (sa->family, flags, sizeof (flags)));
+
+	nl_dump_line(p, "\tproto %s spi 0x%x reqid %u\n",
+	             nl_ip_proto2str (sa->id.proto, flags, sizeof(flags)),
+	             sa->id.spi, sa->reqid);
+
+	xfrmnl_sa_flags2str(sa->flags, flags, sizeof (flags));
+	xfrmnl_sa_mode2str(sa->mode, mode, sizeof (mode));
+	nl_dump_line(p, "\tmode: %s flags: %s (0x%x) seq: %u replay window: %u\n",
+	             mode, flags, sa->flags, sa->seq, sa->replay_window);
+
+	nl_dump_line(p, "\tlifetime configuration: \n");
+	if (sa->lft->soft_byte_limit == XFRM_INF)
+		sprintf (flags, "INF");
+	else
+		sprintf (flags, "%" PRIu64, sa->lft->soft_byte_limit);
+	if (sa->lft->soft_packet_limit == XFRM_INF)
+		sprintf (mode, "INF");
+	else
+		sprintf (mode, "%" PRIu64, sa->lft->soft_packet_limit);
+	nl_dump_line(p, "\t\tsoft limit: %s (bytes), %s (packets)\n", flags, mode);
+	if (sa->lft->hard_byte_limit == XFRM_INF)
+		sprintf (flags, "INF");
+	else
+		sprintf (flags, "%" PRIu64, sa->lft->hard_byte_limit);
+	if (sa->lft->hard_packet_limit == XFRM_INF)
+		sprintf (mode, "INF");
+	else
+		sprintf (mode, "%" PRIu64, sa->lft->hard_packet_limit);
+	nl_dump_line(p, "\t\thard limit: %s (bytes), %s (packets)\n", flags, mode);
+	nl_dump_line(p, "\t\tsoft add_time: %llu (seconds), soft use_time: %llu (seconds) \n",
+	             sa->lft->soft_add_expires_seconds, sa->lft->soft_use_expires_seconds);
+	nl_dump_line(p, "\t\thard add_time: %llu (seconds), hard use_time: %llu (seconds) \n",
+	             sa->lft->hard_add_expires_seconds, sa->lft->hard_use_expires_seconds);
+
+	nl_dump_line(p, "\tlifetime current: \n");
+	nl_dump_line(p, "\t\t%llu bytes, %llu packets\n", sa->curlft.bytes, sa->curlft.packets);
+	if (sa->curlft.add_time != 0)
+	{
+		add_time = sa->curlft.add_time;
+		add_time_tm = gmtime (&add_time);
+		strftime (flags, 128, "%Y-%m-%d %H-%M-%S", add_time_tm);
+	}
+	else
+	{
+		sprintf (flags, "%s", "-");
+	}
+
+	if (sa->curlft.use_time != 0)
+	{
+		use_time = sa->curlft.use_time;
+		use_time_tm = gmtime (&use_time);
+		strftime (mode, 128, "%Y-%m-%d %H-%M-%S", use_time_tm);
+	}
+	else
+	{
+		sprintf (mode, "%s", "-");
+	}
+	nl_dump_line(p, "\t\tadd_time: %s, use_time: %s\n", flags, mode);
+
+	if (sa->aead)
+	{
+		nl_dump_line(p, "\tAEAD Algo: \n");
+		nl_dump_line(p, "\t\tName: %s Key len(bits): %u ICV Len(bits): %u\n",
+		             sa->aead->alg_name, sa->aead->alg_key_len, sa->aead->alg_icv_len);
+	}
+
+	if (sa->auth)
+	{
+		nl_dump_line(p, "\tAuth Algo: \n");
+		nl_dump_line(p, "\t\tName: %s Key len(bits): %u Trunc len(bits): %u\n",
+		             sa->auth->alg_name, sa->auth->alg_key_len, sa->auth->alg_trunc_len);
+	}
+
+	if (sa->crypt)
+	{
+		nl_dump_line(p, "\tEncryption Algo: \n");
+		nl_dump_line(p, "\t\tName: %s Key len(bits): %u\n",
+		             sa->crypt->alg_name, sa->crypt->alg_key_len);
+	}
+
+	if (sa->comp)
+	{
+		nl_dump_line(p, "\tCompression Algo: \n");
+		nl_dump_line(p, "\t\tName: %s Key len(bits): %u\n",
+		             sa->comp->alg_name, sa->comp->alg_key_len);
+	}
+
+	if (sa->encap)
+	{
+		nl_dump_line(p, "\tEncapsulation template: \n");
+		nl_dump_line(p, "\t\tType: %d Src port: %d Dst port: %d Encap addr: %s\n",
+		             sa->encap->encap_type, sa->encap->encap_sport, sa->encap->encap_dport,
+		             nl_addr2str (sa->encap->encap_oa, dst, sizeof (dst)));
+	}
+
+	if (sa->ce_mask & XFRM_SA_ATTR_TFCPAD)
+		nl_dump_line(p, "\tTFC Pad: %u\n", sa->tfcpad);
+
+	if (sa->ce_mask & XFRM_SA_ATTR_COADDR)
+		nl_dump_line(p, "\tCO Address: %s\n", nl_addr2str (sa->coaddr, dst, sizeof (dst)));
+
+	if (sa->ce_mask & XFRM_SA_ATTR_MARK)
+		nl_dump_line(p, "\tMark mask: 0x%x Mark value: 0x%x\n", sa->mark.m, sa->mark.v);
+
+	if (sa->ce_mask & XFRM_SA_ATTR_SECCTX)
+		nl_dump_line(p, "\tDOI: %d Algo: %d Len: %u ctx: %s\n", sa->sec_ctx->ctx_doi,
+		             sa->sec_ctx->ctx_alg, sa->sec_ctx->ctx_len, sa->sec_ctx->ctx);
+
+	nl_dump_line(p, "\treplay info: \n");
+	nl_dump_line(p, "\t\tmax age %u max diff %u \n", sa->replay_maxage, sa->replay_maxdiff);
+
+	if (sa->ce_mask & XFRM_SA_ATTR_REPLAY_STATE)
+	{
+		nl_dump_line(p, "\treplay state info: \n");
+		if (sa->replay_state_esn)
+		{
+			nl_dump_line(p, "\t\toseq %u seq %u oseq_hi %u seq_hi %u replay window: %u \n",
+			             sa->replay_state_esn->oseq, sa->replay_state_esn->seq,
+			             sa->replay_state_esn->oseq_hi, sa->replay_state_esn->seq_hi,
+			             sa->replay_state_esn->replay_window);
+		}
+		else
+		{
+			nl_dump_line(p, "\t\toseq %u seq %u bitmap: %u \n", sa->replay_state.oseq,
+			             sa->replay_state.seq, sa->replay_state.bitmap);
+		}
+	}
+
+	nl_dump_line(p, "\tselector info: \n");
+	xfrmnl_sel_dump (sa->sel, p);
+
+	nl_dump_line(p, "\tHard: %d\n", sa->hard);
+
+	nl_dump(p, "\n");
+}
+
+static void xfrm_sa_dump_stats(struct nl_object *a, struct nl_dump_params *p)
+{
+	struct xfrmnl_sa*   sa  =   (struct xfrmnl_sa*)a;
+
+	nl_dump_line(p, "\tstats: \n");
+	nl_dump_line(p, "\t\treplay window: %u replay: %u integrity failed: %u \n",
+	             sa->stats.replay_window, sa->stats.replay, sa->stats.integrity_failed);
+
+	return;
+}
+
+static void xfrm_sa_dump_details(struct nl_object *a, struct nl_dump_params *p)
+{
+	xfrm_sa_dump_line(a, p);
+	xfrm_sa_dump_stats (a, p);
+}
+
+/**
+ * @name XFRM SA Object Allocation/Freeage
+ * @{
+ */
+
+struct xfrmnl_sa* xfrmnl_sa_alloc(void)
+{
+	return (struct xfrmnl_sa*) nl_object_alloc(&xfrm_sa_obj_ops);
+}
+
+void xfrmnl_sa_put(struct xfrmnl_sa* sa)
+{
+	nl_object_put((struct nl_object *) sa);
+}
+
+/** @} */
+
+/**
+ * @name SA Cache Managament
+ * @{
+ */
+
+/**
+ * Build a SA cache including all SAs currently configured in the kernel.
+ * @arg sock		Netlink socket.
+ * @arg result		Pointer to store resulting cache.
+ *
+ * Allocates a new SA cache, initializes it properly and updates it
+ * to include all SAs currently configured in the kernel.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int xfrmnl_sa_alloc_cache(struct nl_sock *sock, struct nl_cache **result)
+{
+	return nl_cache_alloc_and_fill(&xfrmnl_sa_ops, sock, result);
+}
+
+/**
+ * Look up a SA by destination address, SPI, protocol
+ * @arg cache		SA cache
+ * @arg daddr		destination address of the SA
+ * @arg spi         SPI
+ * @arg proto       protocol
+ * @return sa handle or NULL if no match was found.
+ */
+struct xfrmnl_sa* xfrmnl_sa_get(struct nl_cache* cache, struct nl_addr* daddr,
+                                unsigned int spi, unsigned int proto)
+{
+	struct xfrmnl_sa *sa;
+
+	//nl_list_for_each_entry(sa, &cache->c_items, ce_list) {
+	for (sa = (struct xfrmnl_sa*)nl_cache_get_first (cache);
+		 sa != NULL;
+		 sa = (struct xfrmnl_sa*)nl_cache_get_next ((struct nl_object*)sa))
+	{
+		if (sa->id.proto == proto &&
+		    sa->id.spi == spi &&
+			!nl_addr_cmp(sa->id.daddr, daddr))
+		{
+			nl_object_get((struct nl_object *) sa);
+			return sa;
+		}
+
+	}
+
+	return NULL;
+}
+
+
+/** @} */
+
+
+static struct nla_policy xfrm_sa_policy[XFRMA_MAX+1] = {
+	[XFRMA_SA]              = { .minlen = sizeof(struct xfrm_usersa_info)},
+	[XFRMA_ALG_AUTH_TRUNC]  = { .minlen = sizeof(struct xfrm_algo_auth)},
+	[XFRMA_ALG_AEAD]        = { .minlen = sizeof(struct xfrm_algo_aead) },
+	[XFRMA_ALG_AUTH]        = { .minlen = sizeof(struct xfrm_algo) },
+	[XFRMA_ALG_CRYPT]       = { .minlen = sizeof(struct xfrm_algo) },
+	[XFRMA_ALG_COMP]        = { .minlen = sizeof(struct xfrm_algo) },
+	[XFRMA_ENCAP]           = { .minlen = sizeof(struct xfrm_encap_tmpl) },
+	[XFRMA_TMPL]            = { .minlen = sizeof(struct xfrm_user_tmpl) },
+	[XFRMA_SEC_CTX]         = { .minlen = sizeof(struct xfrm_sec_ctx) },
+	[XFRMA_LTIME_VAL]       = { .minlen = sizeof(struct xfrm_lifetime_cur) },
+	[XFRMA_REPLAY_VAL]      = { .minlen = sizeof(struct xfrm_replay_state) },
+	[XFRMA_REPLAY_THRESH]   = { .type = NLA_U32 },
+	[XFRMA_ETIMER_THRESH]   = { .type = NLA_U32 },
+	[XFRMA_SRCADDR]         = { .minlen = sizeof(xfrm_address_t) },
+	[XFRMA_COADDR]          = { .minlen = sizeof(xfrm_address_t) },
+	[XFRMA_MARK]            = { .minlen = sizeof(struct xfrm_mark) },
+	[XFRMA_TFCPAD]          = { .type = NLA_U32 },
+	[XFRMA_REPLAY_ESN_VAL]  = { .minlen = sizeof(struct xfrm_replay_state_esn) },
+};
+
+static int xfrm_sa_request_update(struct nl_cache *c, struct nl_sock *h)
+{
+	struct xfrm_id sa_id;
+
+	memset (&sa_id, 0, sizeof (sa_id));
+	return nl_send_simple (h, XFRM_MSG_GETSA, NLM_F_DUMP,
+	                       &sa_id, sizeof (sa_id));
+}
+
+int xfrmnl_sa_parse(struct nlmsghdr *n, struct xfrmnl_sa **result)
+{
+	struct xfrmnl_sa*           sa;
+	struct nlattr               *tb[XFRMA_MAX + 1];
+	struct xfrm_usersa_info*    sa_info;
+	struct xfrm_user_expire*    ue;
+	int                         len, err;
+	struct nl_addr*             addr;
+
+	sa = xfrmnl_sa_alloc();
+	if (!sa) {
+		err = -NLE_NOMEM;
+		goto errout;
+	}
+
+	sa->ce_msgtype = n->nlmsg_type;
+	if (n->nlmsg_type == XFRM_MSG_EXPIRE)
+	{
+		ue = nlmsg_data(n);
+		sa_info = &ue->state;
+		sa->hard = ue->hard;
+		sa->ce_mask |= XFRM_SA_ATTR_EXPIRE;
+	}
+	else if (n->nlmsg_type == XFRM_MSG_DELSA)
+	{
+		sa_info = (struct xfrm_usersa_info*)((char *)nlmsg_data(n) + sizeof (struct xfrm_usersa_id) + NLA_HDRLEN);
+	}
+	else
+	{
+		sa_info = nlmsg_data(n);
+	}
+
+	err = nlmsg_parse(n, sizeof(struct xfrm_usersa_info), tb, XFRMA_MAX, xfrm_sa_policy);
+	if (err < 0)
+		goto errout;
+
+	if (sa_info->sel.family == AF_INET)
+		addr    = nl_addr_build (sa_info->sel.family, &sa_info->sel.daddr.a4, sizeof (sa_info->sel.daddr.a4));
+	else
+		addr    = nl_addr_build (sa_info->sel.family, &sa_info->sel.daddr.a6, sizeof (sa_info->sel.daddr.a6));
+	nl_addr_set_prefixlen (addr, sa_info->sel.prefixlen_d);
+	xfrmnl_sel_set_daddr (sa->sel, addr);
+	xfrmnl_sel_set_prefixlen_d (sa->sel, sa_info->sel.prefixlen_d);
+
+	if (sa_info->sel.family == AF_INET)
+		addr    = nl_addr_build (sa_info->sel.family, &sa_info->sel.saddr.a4, sizeof (sa_info->sel.saddr.a4));
+	else
+		addr    = nl_addr_build (sa_info->sel.family, &sa_info->sel.saddr.a6, sizeof (sa_info->sel.saddr.a6));
+	nl_addr_set_prefixlen (addr, sa_info->sel.prefixlen_s);
+	xfrmnl_sel_set_saddr (sa->sel, addr);
+	xfrmnl_sel_set_prefixlen_s (sa->sel, sa_info->sel.prefixlen_s);
+
+	xfrmnl_sel_set_dport (sa->sel, ntohs(sa_info->sel.dport));
+	xfrmnl_sel_set_dportmask (sa->sel, ntohs(sa_info->sel.dport_mask));
+	xfrmnl_sel_set_sport (sa->sel, ntohs(sa_info->sel.sport));
+	xfrmnl_sel_set_sportmask (sa->sel, ntohs(sa_info->sel.sport_mask));
+	xfrmnl_sel_set_family (sa->sel, sa_info->sel.family);
+	xfrmnl_sel_set_proto (sa->sel, sa_info->sel.proto);
+	xfrmnl_sel_set_ifindex (sa->sel, sa_info->sel.ifindex);
+	xfrmnl_sel_set_userid (sa->sel, sa_info->sel.user);
+	sa->ce_mask             |= XFRM_SA_ATTR_SEL;
+
+	if (sa_info->family == AF_INET)
+		sa->id.daddr        = nl_addr_build (sa_info->family, &sa_info->id.daddr.a4, sizeof (sa_info->id.daddr.a4));
+	else
+		sa->id.daddr        = nl_addr_build (sa_info->family, &sa_info->id.daddr.a6, sizeof (sa_info->id.daddr.a6));
+	sa->id.spi              = ntohl(sa_info->id.spi);
+	sa->id.proto            = sa_info->id.proto;
+	sa->ce_mask             |= (XFRM_SA_ATTR_DADDR | XFRM_SA_ATTR_SPI | XFRM_SA_ATTR_PROTO);
+
+	if (sa_info->family == AF_INET)
+		sa->saddr           = nl_addr_build (sa_info->family, &sa_info->saddr.a4, sizeof (sa_info->saddr.a4));
+	else
+		sa->saddr           = nl_addr_build (sa_info->family, &sa_info->saddr.a6, sizeof (sa_info->saddr.a6));
+	sa->ce_mask             |= XFRM_SA_ATTR_SADDR;
+
+	sa->lft->soft_byte_limit    =   sa_info->lft.soft_byte_limit;
+	sa->lft->hard_byte_limit    =   sa_info->lft.hard_byte_limit;
+	sa->lft->soft_packet_limit  =   sa_info->lft.soft_packet_limit;
+	sa->lft->hard_packet_limit  =   sa_info->lft.hard_packet_limit;
+	sa->lft->soft_add_expires_seconds   =   sa_info->lft.soft_add_expires_seconds;
+	sa->lft->hard_add_expires_seconds   =   sa_info->lft.hard_add_expires_seconds;
+	sa->lft->soft_use_expires_seconds   =   sa_info->lft.soft_use_expires_seconds;
+	sa->lft->hard_use_expires_seconds   =   sa_info->lft.hard_use_expires_seconds;
+	sa->ce_mask             |= XFRM_SA_ATTR_LTIME_CFG;
+
+	sa->curlft.bytes        = sa_info->curlft.bytes;
+	sa->curlft.packets      = sa_info->curlft.packets;
+	sa->curlft.add_time     = sa_info->curlft.add_time;
+	sa->curlft.use_time     = sa_info->curlft.use_time;
+	sa->ce_mask             |= XFRM_SA_ATTR_LTIME_CUR;
+
+	sa->stats.replay_window = sa_info->stats.replay_window;
+	sa->stats.replay        = sa_info->stats.replay;
+	sa->stats.integrity_failed = sa_info->stats.integrity_failed;
+	sa->ce_mask             |= XFRM_SA_ATTR_STATS;
+
+	sa->seq                 = sa_info->seq;
+	sa->reqid               = sa_info->reqid;
+	sa->family              = sa_info->family;
+	sa->mode                = sa_info->mode;
+	sa->replay_window       = sa_info->replay_window;
+	sa->flags               = sa_info->flags;
+	sa->ce_mask             |= (XFRM_SA_ATTR_SEQ | XFRM_SA_ATTR_REQID |
+	                            XFRM_SA_ATTR_FAMILY | XFRM_SA_ATTR_MODE |
+	                            XFRM_SA_ATTR_REPLAY_WIN | XFRM_SA_ATTR_FLAGS);
+
+	if (tb[XFRMA_ALG_AEAD]) {
+		struct xfrm_algo_aead* aead = nla_data(tb[XFRMA_ALG_AEAD]);
+		len = sizeof (struct xfrmnl_algo_aead) + ((aead->alg_key_len + 7) / 8);
+		if ((sa->aead = calloc (1, len)) == NULL)
+		{
+			err = -NLE_NOMEM;
+			goto errout;
+		}
+		memcpy ((void *)sa->aead, (void *)aead, len);
+		sa->ce_mask     |= XFRM_SA_ATTR_ALG_AEAD;
+	}
+
+	if (tb[XFRMA_ALG_AUTH_TRUNC]) {
+		struct xfrm_algo_auth* auth = nla_data(tb[XFRMA_ALG_AUTH_TRUNC]);
+		len = sizeof (struct xfrmnl_algo_auth) + ((auth->alg_key_len + 7) / 8);
+		if ((sa->auth = calloc (1, len)) == NULL)
+		{
+			err = -NLE_NOMEM;
+			goto errout;
+		}
+		memcpy ((void *)sa->auth, (void *)auth, len);
+		sa->ce_mask     |= XFRM_SA_ATTR_ALG_AUTH;
+	}
+
+	if (tb[XFRMA_ALG_AUTH] && !sa->auth) {
+		struct xfrm_algo* auth = nla_data(tb[XFRMA_ALG_AUTH]);
+		len = sizeof (struct xfrmnl_algo_auth) + ((auth->alg_key_len + 7) / 8);
+		if ((sa->auth = calloc (1, len)) == NULL)
+		{
+			err = -NLE_NOMEM;
+			goto errout;
+		}
+		strcpy(sa->auth->alg_name, auth->alg_name);
+		memcpy(sa->auth->alg_key, auth->alg_key, (auth->alg_key_len + 7) / 8);
+		sa->auth->alg_key_len = auth->alg_key_len;
+		sa->ce_mask     |=  XFRM_SA_ATTR_ALG_AUTH;
+	}
+
+	if (tb[XFRMA_ALG_CRYPT]) {
+		struct xfrm_algo* crypt = nla_data(tb[XFRMA_ALG_CRYPT]);
+		len = sizeof (struct xfrmnl_algo) + ((crypt->alg_key_len + 7) / 8);
+		if ((sa->crypt = calloc (1, len)) == NULL)
+		{
+			err = -NLE_NOMEM;
+			goto errout;
+		}
+		memcpy ((void *)sa->crypt, (void *)crypt, len);
+		sa->ce_mask     |= XFRM_SA_ATTR_ALG_CRYPT;
+	}
+
+	if (tb[XFRMA_ALG_COMP]) {
+		struct xfrm_algo* comp = nla_data(tb[XFRMA_ALG_COMP]);
+		len = sizeof (struct xfrmnl_algo) + ((comp->alg_key_len + 7) / 8);
+		if ((sa->comp = calloc (1, len)) == NULL)
+		{
+			err = -NLE_NOMEM;
+			goto errout;
+		}
+		memcpy ((void *)sa->comp, (void *)comp, len);
+		sa->ce_mask     |= XFRM_SA_ATTR_ALG_COMP;
+	}
+
+	if (tb[XFRMA_ENCAP]) {
+		struct xfrm_encap_tmpl* encap = nla_data(tb[XFRMA_ENCAP]);
+		len = sizeof (struct xfrmnl_encap_tmpl);
+		if ((sa->encap = calloc (1, len)) == NULL)
+		{
+			err = -NLE_NOMEM;
+			goto errout;
+		}
+		sa->encap->encap_type   =   encap->encap_type;
+		sa->encap->encap_sport  =   ntohs(encap->encap_sport);
+		sa->encap->encap_dport  =   ntohs(encap->encap_dport);
+		if (sa_info->family == AF_INET)
+			sa->encap->encap_oa =   nl_addr_build (sa_info->family, &encap->encap_oa.a4, sizeof (encap->encap_oa.a4));
+		else
+			sa->encap->encap_oa =   nl_addr_build (sa_info->family, &encap->encap_oa.a6, sizeof (encap->encap_oa.a6));
+		sa->ce_mask     |= XFRM_SA_ATTR_ENCAP;
+	}
+
+	if (tb[XFRMA_TFCPAD]) {
+		sa->tfcpad      =   *(uint32_t*)nla_data(tb[XFRMA_TFCPAD]);
+		sa->ce_mask     |= XFRM_SA_ATTR_TFCPAD;
+	}
+
+	if (tb[XFRMA_COADDR]) {
+		if (sa_info->family == AF_INET)
+		{
+			sa->coaddr  = nl_addr_build(sa_info->family, nla_data(tb[XFRMA_COADDR]),
+			                            sizeof (uint32_t));
+		}
+		else
+		{
+			sa->coaddr  = nl_addr_build(sa_info->family, nla_data(tb[XFRMA_COADDR]),
+			                            sizeof (uint32_t) * 4);
+		}
+		sa->ce_mask         |= XFRM_SA_ATTR_COADDR;
+	}
+
+	if (tb[XFRMA_MARK]) {
+		struct xfrm_mark* m =   nla_data(tb[XFRMA_MARK]);
+		sa->mark.m  =   m->m;
+		sa->mark.v  =   m->v;
+		sa->ce_mask |= XFRM_SA_ATTR_MARK;
+	}
+
+	if (tb[XFRMA_SEC_CTX]) {
+		struct xfrm_user_sec_ctx* sec_ctx = nla_data(tb[XFRMA_SEC_CTX]);
+		len = sizeof (struct xfrmnl_user_sec_ctx) + sec_ctx->ctx_len;
+		if ((sa->sec_ctx = calloc (1, len)) == NULL)
+		{
+			err = -NLE_NOMEM;
+			goto errout;
+		}
+		memcpy (sa->sec_ctx, sec_ctx, len);
+		sa->ce_mask     |= XFRM_SA_ATTR_SECCTX;
+	}
+
+	if (tb[XFRMA_ETIMER_THRESH]) {
+		sa->replay_maxage       =   *(uint32_t*)nla_data(tb[XFRMA_ETIMER_THRESH]);
+		sa->ce_mask |= XFRM_SA_ATTR_REPLAY_MAXAGE;
+	}
+
+	if (tb[XFRMA_REPLAY_THRESH]) {
+		sa->replay_maxdiff      =   *(uint32_t*)nla_data(tb[XFRMA_REPLAY_THRESH]);
+		sa->ce_mask |= XFRM_SA_ATTR_REPLAY_MAXDIFF;
+	}
+
+	if (tb[XFRMA_REPLAY_ESN_VAL]) {
+		struct xfrm_replay_state_esn* esn = nla_data (tb[XFRMA_REPLAY_ESN_VAL]);
+		len =   sizeof (struct xfrmnl_replay_state_esn) + (sizeof (uint32_t) * esn->bmp_len);
+		if ((sa->replay_state_esn = calloc (1, len)) == NULL)
+		{
+			err = -NLE_NOMEM;
+			goto errout;
+		}
+		memcpy ((void *)sa->replay_state_esn, (void *)esn, len);
+		sa->ce_mask |= XFRM_SA_ATTR_REPLAY_STATE;
+	}
+	else if (tb[XFRMA_REPLAY_VAL])
+	{
+		struct xfrm_replay_state* replay_state = nla_data (tb[XFRMA_REPLAY_VAL]);
+		sa->replay_state.oseq       =   replay_state->oseq;
+		sa->replay_state.seq        =   replay_state->seq;
+		sa->replay_state.bitmap     =   replay_state->bitmap;
+		sa->ce_mask |= XFRM_SA_ATTR_REPLAY_STATE;
+		sa->replay_state_esn = NULL;
+	}
+
+	*result = sa;
+	return 0;
+
+errout:
+	xfrmnl_sa_put(sa);
+	return err;
+}
+
+static int xfrm_sa_update_cache (struct nl_cache *cache, struct nl_object *obj,
+                                 change_func_t change_cb, change_func_v2_t change_cb_v2,
+				 void *data)
+{
+	struct nl_object*       old_sa;
+	struct xfrmnl_sa*       sa = (struct xfrmnl_sa*)obj;
+
+	if (nl_object_get_msgtype (obj) == XFRM_MSG_EXPIRE)
+	{
+		/* On hard expiry, the SA gets deleted too from the kernel state without any
+		 * further delete event. On Expire message, we are only updating the cache with
+		 * the SA object's new state. In absence of the explicit delete event, the cache will
+		 * be out of sync with the kernel state. To get around this, expiry messages cache
+		 * operations are handled here (installed with NL_ACT_UNSPEC action) instead of
+		 * in Libnl Cache module. */
+
+		/* Do we already have this object in the cache? */
+		old_sa = nl_cache_search(cache, obj);
+		if (old_sa)
+		{
+			/* Found corresponding SA object in cache. Delete it */
+			nl_cache_remove (old_sa);
+		}
+
+		/* Handle the expiry event now */
+		if (sa->hard == 0)
+		{
+			/* Soft expiry event: Save the new object to the
+			 * cache and notify application of the expiry event. */
+			nl_cache_move (cache, obj);
+
+			if (old_sa == NULL)
+			{
+				/* Application CB present, no previous instance of SA object present.
+				 * Notify application CB as a NEW event */
+				if (change_cb_v2)
+					change_cb_v2(cache, NULL, obj, 0, NL_ACT_NEW, data);
+				else if (change_cb)
+					change_cb(cache, obj, NL_ACT_NEW, data);
+			}
+			else if (old_sa)
+			{
+				uint64_t diff = 0;
+				if (change_cb || change_cb_v2)
+					diff = nl_object_diff64(old_sa, obj);
+
+				/* Application CB present, a previous instance of SA object present.
+				 * Notify application CB as a CHANGE1 event */
+				if (diff) {
+					if (change_cb_v2) {
+						change_cb_v2(cache, old_sa, obj, diff, NL_ACT_CHANGE, data);
+					} else if (change_cb)
+						change_cb(cache, obj, NL_ACT_CHANGE, data);
+				}
+				nl_object_put (old_sa);
+			}
+		}
+		else
+		{
+			/* Hard expiry event: Delete the object from the
+			 * cache and notify application of the expiry event. */
+			if (change_cb_v2)
+				change_cb_v2(cache, obj, NULL, 0, NL_ACT_DEL, data);
+			else if (change_cb)
+				change_cb (cache, obj, NL_ACT_DEL, data);
+			nl_object_put (old_sa);
+		}
+
+		/* Done handling expire message */
+		return 0;
+	}
+	else
+	{
+		/* All other messages other than Expire, let the standard Libnl cache
+		 * module handle it. */
+		if (change_cb_v2)
+			return nl_cache_include_v2(cache, obj, change_cb_v2, data);
+		else
+			return nl_cache_include (cache, obj, change_cb, data);
+	}
+}
+
+static int xfrm_sa_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+				struct nlmsghdr *n, struct nl_parser_param *pp)
+{
+	struct xfrmnl_sa*       sa;
+	int                     err;
+
+	if ((err = xfrmnl_sa_parse(n, &sa)) < 0)
+		return err;
+
+	err = pp->pp_cb((struct nl_object *) sa, pp);
+
+	xfrmnl_sa_put(sa);
+	return err;
+}
+
+/**
+ * @name XFRM SA Get
+ * @{
+ */
+
+int xfrmnl_sa_build_get_request(struct nl_addr* daddr, unsigned int spi, unsigned int protocol, unsigned int mark_v, unsigned int mark_m, struct nl_msg **result)
+{
+	struct nl_msg               *msg;
+	struct xfrm_usersa_id       sa_id;
+	struct xfrm_mark            mark;
+
+	if (!daddr || !spi)
+	{
+		fprintf(stderr, "APPLICATION BUG: %s:%d:%s: A valid destination address, spi must be specified\n",
+		        __FILE__, __LINE__, __func__);
+		assert(0);
+		return -NLE_MISSING_ATTR;
+	}
+
+	memset(&sa_id, 0, sizeof(sa_id));
+	memcpy (&sa_id.daddr, nl_addr_get_binary_addr (daddr), sizeof (uint8_t) * nl_addr_get_len (daddr));
+	sa_id.family = nl_addr_get_family (daddr);
+	sa_id.spi    = htonl(spi);
+	sa_id.proto  = protocol;
+
+	if (!(msg = nlmsg_alloc_simple(XFRM_MSG_GETSA, 0)))
+		return -NLE_NOMEM;
+
+	if (nlmsg_append(msg, &sa_id, sizeof(sa_id), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	if ((mark_m & mark_v) != 0)
+	{
+		memset(&mark, 0, sizeof(struct xfrm_mark));
+		mark.m = mark_m;
+		mark.v = mark_v;
+
+		NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrm_mark), &mark);
+	}
+
+	*result = msg;
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+int xfrmnl_sa_get_kernel(struct nl_sock* sock, struct nl_addr* daddr, unsigned int spi, unsigned int protocol, unsigned int mark_v, unsigned int mark_m, struct xfrmnl_sa** result)
+{
+	struct nl_msg *msg = NULL;
+	struct nl_object *obj;
+	int err;
+
+	if ((err = xfrmnl_sa_build_get_request(daddr, spi, protocol, mark_m, mark_v, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto(sock, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	if ((err = nl_pickup(sock, &xfrm_sa_msg_parser, &obj)) < 0)
+		return err;
+
+	/* We have used xfrm_sa_msg_parser(), object is definitely a xfrm sa */
+	*result = (struct xfrmnl_sa *) obj;
+
+	/* If an object has been returned, we also need to wait for the ACK */
+	if (err == 0 && obj)
+		nl_wait_for_ack(sock);
+
+	return 0;
+}
+
+/** @} */
+
+static int build_xfrm_sa_message(struct xfrmnl_sa *tmpl, int cmd, int flags, struct nl_msg **result)
+{
+	struct nl_msg*          msg;
+	struct xfrm_usersa_info sa_info;
+	uint32_t                len;
+	struct nl_addr*         addr;
+
+	if (!(tmpl->ce_mask & XFRM_SA_ATTR_DADDR) ||
+		!(tmpl->ce_mask & XFRM_SA_ATTR_SPI) ||
+		!(tmpl->ce_mask & XFRM_SA_ATTR_PROTO))
+		return -NLE_MISSING_ATTR;
+
+	memset ((void*)&sa_info, 0, sizeof (sa_info));
+	if (tmpl->ce_mask & XFRM_SA_ATTR_SEL)
+	{
+		addr = xfrmnl_sel_get_daddr (tmpl->sel);
+		memcpy ((void*)&sa_info.sel.daddr, (void*)nl_addr_get_binary_addr (addr), sizeof (uint8_t) * nl_addr_get_len (addr));
+		addr = xfrmnl_sel_get_saddr (tmpl->sel);
+		memcpy ((void*)&sa_info.sel.saddr, (void*)nl_addr_get_binary_addr (addr), sizeof (uint8_t) * nl_addr_get_len (addr));
+		sa_info.sel.dport       =   htons (xfrmnl_sel_get_dport (tmpl->sel));
+		sa_info.sel.dport_mask  =   htons (xfrmnl_sel_get_dportmask (tmpl->sel));
+		sa_info.sel.sport       =   htons (xfrmnl_sel_get_sport (tmpl->sel));
+		sa_info.sel.sport_mask  =   htons (xfrmnl_sel_get_sportmask (tmpl->sel));
+		sa_info.sel.family      =   xfrmnl_sel_get_family (tmpl->sel);
+		sa_info.sel.prefixlen_d =   xfrmnl_sel_get_prefixlen_d (tmpl->sel);
+		sa_info.sel.prefixlen_s =   xfrmnl_sel_get_prefixlen_s (tmpl->sel);
+		sa_info.sel.proto       =   xfrmnl_sel_get_proto (tmpl->sel);
+		sa_info.sel.ifindex     =   xfrmnl_sel_get_ifindex (tmpl->sel);
+		sa_info.sel.user        =   xfrmnl_sel_get_userid (tmpl->sel);
+	}
+
+	memcpy (&sa_info.id.daddr, nl_addr_get_binary_addr (tmpl->id.daddr), sizeof (uint8_t) * nl_addr_get_len (tmpl->id.daddr));
+	sa_info.id.spi    = htonl(tmpl->id.spi);
+	sa_info.id.proto  = tmpl->id.proto;
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_SADDR)
+		memcpy (&sa_info.saddr, nl_addr_get_binary_addr (tmpl->saddr), sizeof (uint8_t) * nl_addr_get_len (tmpl->saddr));
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_LTIME_CFG)
+	{
+		sa_info.lft.soft_byte_limit = xfrmnl_ltime_cfg_get_soft_bytelimit (tmpl->lft);
+		sa_info.lft.hard_byte_limit = xfrmnl_ltime_cfg_get_hard_bytelimit (tmpl->lft);
+		sa_info.lft.soft_packet_limit = xfrmnl_ltime_cfg_get_soft_packetlimit (tmpl->lft);
+		sa_info.lft.hard_packet_limit = xfrmnl_ltime_cfg_get_hard_packetlimit (tmpl->lft);
+		sa_info.lft.soft_add_expires_seconds = xfrmnl_ltime_cfg_get_soft_addexpires (tmpl->lft);
+		sa_info.lft.hard_add_expires_seconds = xfrmnl_ltime_cfg_get_hard_addexpires (tmpl->lft);
+		sa_info.lft.soft_use_expires_seconds = xfrmnl_ltime_cfg_get_soft_useexpires (tmpl->lft);
+		sa_info.lft.hard_use_expires_seconds = xfrmnl_ltime_cfg_get_hard_useexpires (tmpl->lft);
+	}
+
+	//Skip current lifetime: cur lifetime can be updated only via AE
+	//Skip stats: stats cant be updated
+	//Skip seq: seq cant be updated
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_REQID)
+		sa_info.reqid           = tmpl->reqid;
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_FAMILY)
+		sa_info.family          = tmpl->family;
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_MODE)
+		sa_info.mode            = tmpl->mode;
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_REPLAY_WIN)
+		sa_info.replay_window   = tmpl->replay_window;
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_FLAGS)
+		sa_info.flags           = tmpl->flags;
+
+	msg = nlmsg_alloc_simple(cmd, flags);
+	if (!msg)
+		return -NLE_NOMEM;
+
+	if (nlmsg_append(msg, &sa_info, sizeof(sa_info), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_ALG_AEAD) {
+		len = sizeof (struct xfrm_algo_aead) + ((tmpl->aead->alg_key_len + 7) / 8);
+		NLA_PUT (msg, XFRMA_ALG_AEAD, len, tmpl->aead);
+	}
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_ALG_AUTH) {
+		/* kernel prefers XFRMA_ALG_AUTH_TRUNC over XFRMA_ALG_AUTH, so only
+		 * one of the attributes needs to be present */
+		if (tmpl->auth->alg_trunc_len) {
+			len = sizeof (struct xfrm_algo_auth) + ((tmpl->auth->alg_key_len + 7) / 8);
+			NLA_PUT (msg, XFRMA_ALG_AUTH_TRUNC, len, tmpl->auth);
+		} else {
+			struct xfrm_algo *auth;
+
+			len = sizeof (struct xfrm_algo) + ((tmpl->auth->alg_key_len + 7) / 8);
+			auth = malloc(len);
+			if (!auth) {
+				nlmsg_free(msg);
+				return -NLE_NOMEM;
+			}
+
+			strncpy(auth->alg_name, tmpl->auth->alg_name, sizeof(auth->alg_name));
+			auth->alg_name[sizeof(auth->alg_name) - 1] = '\0';
+			auth->alg_key_len = tmpl->auth->alg_key_len;
+			memcpy(auth->alg_key, tmpl->auth->alg_key, (tmpl->auth->alg_key_len + 7) / 8);
+			if (nla_put(msg, XFRMA_ALG_AUTH, len, auth) < 0) {
+				free(auth);
+				goto nla_put_failure;
+			}
+			free(auth);
+		}
+	}
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_ALG_CRYPT) {
+		len = sizeof (struct xfrm_algo) + ((tmpl->crypt->alg_key_len + 7) / 8);
+		NLA_PUT (msg, XFRMA_ALG_CRYPT, len, tmpl->crypt);
+	}
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_ALG_COMP) {
+		len = sizeof (struct xfrm_algo) + ((tmpl->comp->alg_key_len + 7) / 8);
+		NLA_PUT (msg, XFRMA_ALG_COMP, len, tmpl->comp);
+	}
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_ENCAP) {
+		struct xfrm_encap_tmpl* encap_tmpl;
+		struct nlattr*          encap_attr;
+
+		len = sizeof (struct xfrm_encap_tmpl);
+		encap_attr = nla_reserve(msg, XFRMA_ENCAP, len);
+		if (!encap_attr)
+			goto nla_put_failure;
+		encap_tmpl = nla_data (encap_attr);
+		encap_tmpl->encap_type  =   tmpl->encap->encap_type;
+		encap_tmpl->encap_sport =   htons (tmpl->encap->encap_sport);
+		encap_tmpl->encap_dport =   htons (tmpl->encap->encap_dport);
+		memcpy (&encap_tmpl->encap_oa, nl_addr_get_binary_addr (tmpl->encap->encap_oa), sizeof (uint8_t) * nl_addr_get_len (tmpl->encap->encap_oa));
+	}
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_TFCPAD) {
+		NLA_PUT_U32 (msg, XFRMA_TFCPAD, tmpl->tfcpad);
+	}
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_COADDR) {
+		NLA_PUT (msg, XFRMA_COADDR, sizeof (xfrm_address_t), tmpl->coaddr);
+	}
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_MARK) {
+		NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrm_mark), &tmpl->mark);
+	}
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_SECCTX) {
+		len = sizeof (struct xfrm_sec_ctx) + tmpl->sec_ctx->ctx_len;
+		NLA_PUT (msg, XFRMA_SEC_CTX, len, tmpl->sec_ctx);
+	}
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_REPLAY_MAXAGE) {
+		NLA_PUT_U32 (msg, XFRMA_ETIMER_THRESH, tmpl->replay_maxage);
+	}
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_REPLAY_MAXDIFF) {
+		NLA_PUT_U32 (msg, XFRMA_REPLAY_THRESH, tmpl->replay_maxdiff);
+	}
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_REPLAY_STATE) {
+		if (tmpl->replay_state_esn) {
+			len =   sizeof (struct xfrm_replay_state_esn) + (sizeof (uint32_t) * tmpl->replay_state_esn->bmp_len);
+			NLA_PUT (msg, XFRMA_REPLAY_ESN_VAL, len, tmpl->replay_state_esn);
+		}
+		else {
+			NLA_PUT (msg, XFRMA_REPLAY_VAL, sizeof (struct xfrm_replay_state), &tmpl->replay_state);
+		}
+	}
+
+	*result = msg;
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+/**
+ * @name XFRM SA Add
+ * @{
+ */
+
+int xfrmnl_sa_build_add_request(struct xfrmnl_sa* tmpl, int flags, struct nl_msg **result)
+{
+	return build_xfrm_sa_message (tmpl, XFRM_MSG_NEWSA, flags, result);
+}
+
+int xfrmnl_sa_add(struct nl_sock* sk, struct xfrmnl_sa* tmpl, int flags)
+{
+	int             err;
+	struct nl_msg   *msg;
+
+	if ((err = xfrmnl_sa_build_add_request(tmpl, flags, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return nl_wait_for_ack(sk);
+}
+
+/**
+ * @name XFRM SA Update
+ * @{
+ */
+
+int xfrmnl_sa_build_update_request(struct xfrmnl_sa* tmpl, int flags, struct nl_msg **result)
+{
+	return build_xfrm_sa_message (tmpl, XFRM_MSG_UPDSA, flags, result);
+}
+
+int xfrmnl_sa_update(struct nl_sock* sk, struct xfrmnl_sa* tmpl, int flags)
+{
+	int             err;
+	struct nl_msg   *msg;
+
+	if ((err = xfrmnl_sa_build_update_request(tmpl, flags, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return nl_wait_for_ack(sk);
+}
+
+/** @} */
+
+static int build_xfrm_sa_delete_message(struct xfrmnl_sa *tmpl, int cmd, int flags, struct nl_msg **result)
+{
+	struct nl_msg*          msg;
+	struct xfrm_usersa_id   sa_id;
+
+	if (!(tmpl->ce_mask & XFRM_SA_ATTR_DADDR) ||
+		!(tmpl->ce_mask & XFRM_SA_ATTR_SPI) ||
+		!(tmpl->ce_mask & XFRM_SA_ATTR_PROTO))
+		return -NLE_MISSING_ATTR;
+
+	memcpy (&sa_id.daddr, nl_addr_get_binary_addr (tmpl->id.daddr),
+	        sizeof (uint8_t) * nl_addr_get_len (tmpl->id.daddr));
+	sa_id.family = nl_addr_get_family (tmpl->id.daddr);
+	sa_id.spi    = htonl(tmpl->id.spi);
+	sa_id.proto  = tmpl->id.proto;
+
+	msg = nlmsg_alloc_simple(cmd, flags);
+	if (!msg)
+		return -NLE_NOMEM;
+
+	if (nlmsg_append(msg, &sa_id, sizeof(sa_id), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	if (tmpl->ce_mask & XFRM_SA_ATTR_MARK) {
+		NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrm_mark), &tmpl->mark);
+	}
+
+	*result = msg;
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+/**
+ * @name XFRM SA Delete
+ * @{
+ */
+
+int xfrmnl_sa_build_delete_request(struct xfrmnl_sa* tmpl, int flags, struct nl_msg **result)
+{
+	return build_xfrm_sa_delete_message (tmpl, XFRM_MSG_DELSA, flags, result);
+}
+
+int xfrmnl_sa_delete(struct nl_sock* sk, struct xfrmnl_sa* tmpl, int flags)
+{
+	int             err;
+	struct nl_msg   *msg;
+
+	if ((err = xfrmnl_sa_build_delete_request(tmpl, flags, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return nl_wait_for_ack(sk);
+}
+
+/** @} */
+
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+struct xfrmnl_sel* xfrmnl_sa_get_sel (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_SEL)
+		return sa->sel;
+	else
+		return NULL;
+}
+
+int xfrmnl_sa_set_sel (struct xfrmnl_sa* sa, struct xfrmnl_sel* sel)
+{
+	/* Release any previously held selector object from the SA */
+	if (sa->sel)
+		xfrmnl_sel_put (sa->sel);
+
+	/* Increment ref count on new selector and save it in the SA */
+	xfrmnl_sel_get (sel);
+	sa->sel     =   sel;
+	sa->ce_mask |=  XFRM_SA_ATTR_SEL;
+
+	return 0;
+}
+
+static inline int __assign_addr(struct xfrmnl_sa* sa, struct nl_addr **pos,
+					struct nl_addr *new, int flag, int nocheck)
+{
+	if (!nocheck)
+	{
+		if (sa->ce_mask & XFRM_SA_ATTR_FAMILY)
+		{
+			if (nl_addr_get_family (new) != sa->family)
+				return -NLE_AF_MISMATCH;
+		}
+	}
+
+	if (*pos)
+		nl_addr_put(*pos);
+
+	nl_addr_get(new);
+	*pos = new;
+
+	sa->ce_mask |= flag;
+
+	return 0;
+}
+
+
+struct nl_addr* xfrmnl_sa_get_daddr (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_DADDR)
+		return sa->id.daddr;
+	else
+		return NULL;
+}
+
+int xfrmnl_sa_set_daddr (struct xfrmnl_sa* sa, struct nl_addr* addr)
+{
+	return __assign_addr(sa, &sa->id.daddr, addr, XFRM_SA_ATTR_DADDR, 0);
+}
+
+int xfrmnl_sa_get_spi (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_SPI)
+		return sa->id.spi;
+	else
+		return -1;
+}
+
+int xfrmnl_sa_set_spi (struct xfrmnl_sa* sa, unsigned int spi)
+{
+	sa->id.spi = spi;
+	sa->ce_mask |= XFRM_SA_ATTR_SPI;
+
+	return 0;
+}
+
+int xfrmnl_sa_get_proto (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_PROTO)
+		return sa->id.proto;
+	else
+		return -1;
+}
+
+int xfrmnl_sa_set_proto (struct xfrmnl_sa* sa, unsigned int protocol)
+{
+	sa->id.proto = protocol;
+	sa->ce_mask |= XFRM_SA_ATTR_PROTO;
+
+	return 0;
+}
+
+struct nl_addr* xfrmnl_sa_get_saddr (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_SADDR)
+		return sa->saddr;
+	else
+		return NULL;
+}
+
+int xfrmnl_sa_set_saddr (struct xfrmnl_sa* sa, struct nl_addr* addr)
+{
+	return __assign_addr(sa, &sa->saddr, addr, XFRM_SA_ATTR_SADDR, 1);
+}
+
+struct xfrmnl_ltime_cfg* xfrmnl_sa_get_lifetime_cfg (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_LTIME_CFG)
+		return sa->lft;
+	else
+		return NULL;
+}
+
+int xfrmnl_sa_set_lifetime_cfg (struct xfrmnl_sa* sa, struct xfrmnl_ltime_cfg* ltime)
+{
+	/* Release any previously held lifetime cfg object from the SA */
+	if (sa->lft)
+		xfrmnl_ltime_cfg_put (sa->lft);
+
+	/* Increment ref count on new lifetime object and save it in the SA */
+	xfrmnl_ltime_cfg_get (ltime);
+	sa->lft     =   ltime;
+	sa->ce_mask |=  XFRM_SA_ATTR_LTIME_CFG;
+
+	return 0;
+}
+
+int xfrmnl_sa_get_curlifetime (struct xfrmnl_sa* sa, unsigned long long int* curr_bytes,
+                               unsigned long long int* curr_packets, unsigned long long int* curr_add_time, unsigned long long int* curr_use_time)
+{
+	if (sa == NULL || curr_bytes == NULL || curr_packets == NULL || curr_add_time == NULL || curr_use_time == NULL)
+		return -1;
+
+	if (sa->ce_mask & XFRM_SA_ATTR_LTIME_CUR)
+	{
+		*curr_bytes     =   sa->curlft.bytes;
+		*curr_packets   =   sa->curlft.packets;
+		*curr_add_time  =   sa->curlft.add_time;
+		*curr_use_time  =   sa->curlft.use_time;
+	}
+	else
+		return -1;
+
+	return 0;
+}
+
+int xfrmnl_sa_get_stats (struct xfrmnl_sa* sa, unsigned long long int* replay_window,
+                         unsigned long long int* replay, unsigned long long int* integrity_failed)
+{
+	if (sa == NULL || replay_window == NULL || replay == NULL || integrity_failed == NULL)
+		return -1;
+
+	if (sa->ce_mask & XFRM_SA_ATTR_STATS)
+	{
+		*replay_window      =   sa->stats.replay_window;
+		*replay             =   sa->stats.replay;
+		*integrity_failed   =   sa->stats.integrity_failed;
+	}
+	else
+		return -1;
+
+	return 0;
+}
+
+int xfrmnl_sa_get_seq (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_SEQ)
+		return sa->seq;
+	else
+		return -1;
+}
+
+int xfrmnl_sa_get_reqid (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_REQID)
+		return sa->reqid;
+	else
+		return -1;
+}
+
+int xfrmnl_sa_set_reqid (struct xfrmnl_sa* sa, unsigned int reqid)
+{
+	sa->reqid = reqid;
+	sa->ce_mask |= XFRM_SA_ATTR_REQID;
+
+	return 0;
+}
+
+int xfrmnl_sa_get_family (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_FAMILY)
+		return sa->family;
+	else
+		return -1;
+}
+
+int xfrmnl_sa_set_family (struct xfrmnl_sa* sa, unsigned int family)
+{
+	sa->family = family;
+	sa->ce_mask |= XFRM_SA_ATTR_FAMILY;
+
+	return 0;
+}
+
+int xfrmnl_sa_get_mode (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_MODE)
+		return sa->mode;
+	else
+		return -1;
+}
+
+int xfrmnl_sa_set_mode (struct xfrmnl_sa* sa, unsigned int mode)
+{
+	sa->mode    =   mode;
+	sa->ce_mask |=  XFRM_SA_ATTR_MODE;
+
+	return 0;
+}
+
+int xfrmnl_sa_get_replay_window (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_REPLAY_WIN)
+		return sa->replay_window;
+	else
+		return -1;
+}
+
+int xfrmnl_sa_set_replay_window (struct xfrmnl_sa* sa, unsigned int replay_window)
+{
+	sa->replay_window   =   replay_window;
+	sa->ce_mask         |=  XFRM_SA_ATTR_REPLAY_WIN;
+
+	return 0;
+}
+
+int xfrmnl_sa_get_flags (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_FLAGS)
+		return sa->flags;
+	else
+		return -1;
+}
+
+int xfrmnl_sa_set_flags (struct xfrmnl_sa* sa, unsigned int flags)
+{
+	sa->flags = flags;
+	sa->ce_mask |= XFRM_SA_ATTR_FLAGS;
+
+	return 0;
+}
+
+/**
+ * Get the aead-params
+ * @arg sa              the xfrmnl_sa object
+ * @arg alg_name        an optional output buffer for the algorithm name. Must be at least 64 bytes.
+ * @arg key_len         an optional output value for the key length in bits.
+ * @arg icv_len         an optional output value for the alt-icv-len.
+ * @arg key             an optional buffer large enough for the key. It must contain at least
+ *                      ((@key_len + 7) / 8) bytes.
+ *
+ * Warning: you must ensure that @key is large enough. If you don't know the key_len before-hand,
+ * call xfrmnl_sa_get_aead_params() without @key argument to query only the required buffer size.
+ * This modified API is available in all versions of libnl3 that support the capability
+ * @def NL_CAPABILITY_XFRM_SA_KEY_SIZE (@see nl_has_capability for further information).
+ *
+ * @return 0 on success or a negative error code.
+ */
+int xfrmnl_sa_get_aead_params (struct xfrmnl_sa* sa, char* alg_name, unsigned int* key_len, unsigned int* icv_len, char* key)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_ALG_AEAD)
+	{
+		if (alg_name)
+			strcpy (alg_name, sa->aead->alg_name);
+		if (key_len)
+			*key_len = sa->aead->alg_key_len;
+		if (icv_len)
+			*icv_len = sa->aead->alg_icv_len;
+		if (key)
+			memcpy (key, sa->aead->alg_key, ((sa->aead->alg_key_len + 7)/8));
+	}
+	else
+		return -1;
+
+	return 0;
+}
+
+int xfrmnl_sa_set_aead_params (struct xfrmnl_sa* sa, const char* alg_name, unsigned int key_len, unsigned int icv_len, const char* key)
+{
+	_nl_auto_free struct xfrmnl_algo_aead *b = NULL;
+	size_t keysize = sizeof (uint8_t) * ((key_len + 7)/8);
+	uint32_t newlen = sizeof (struct xfrmnl_algo_aead) + keysize;
+
+	/* Free up the old key and allocate memory to hold new key */
+	if (strlen (alg_name) >= sizeof (sa->aead->alg_name))
+		return -1;
+	if (!(b = calloc (1, newlen)))
+		return -1;
+
+	strcpy (b->alg_name, alg_name);
+	b->alg_key_len   = key_len;
+	b->alg_icv_len   = icv_len;
+	memcpy (b->alg_key, key, keysize);
+
+	free (sa->aead);
+	sa->aead = _nl_steal_pointer (&b);
+	sa->ce_mask |= XFRM_SA_ATTR_ALG_AEAD;
+	return 0;
+}
+
+/**
+ * Get the auth-params
+ * @arg sa              the xfrmnl_sa object
+ * @arg alg_name        an optional output buffer for the algorithm name. Must be at least 64 bytes.
+ * @arg key_len         an optional output value for the key length in bits.
+ * @arg trunc_len       an optional output value for the alg-trunc-len.
+ * @arg key             an optional buffer large enough for the key. It must contain at least
+ *                      ((@key_len + 7) / 8) bytes.
+ *
+ * Warning: you must ensure that @key is large enough. If you don't know the key_len before-hand,
+ * call xfrmnl_sa_get_auth_params() without @key argument to query only the required buffer size.
+ * This modified API is available in all versions of libnl3 that support the capability
+ * @def NL_CAPABILITY_XFRM_SA_KEY_SIZE (@see nl_has_capability for further information).
+ *
+ * @return 0 on success or a negative error code.
+ */
+int xfrmnl_sa_get_auth_params (struct xfrmnl_sa* sa, char* alg_name, unsigned int* key_len, unsigned int* trunc_len, char* key)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_ALG_AUTH)
+	{
+		if (alg_name)
+			strcpy (alg_name, sa->auth->alg_name);
+		if (key_len)
+			*key_len = sa->auth->alg_key_len;
+		if (trunc_len)
+			*trunc_len = sa->auth->alg_trunc_len;
+		if (key)
+			memcpy (key, sa->auth->alg_key, (sa->auth->alg_key_len + 7)/8);
+	}
+	else
+		return -1;
+
+	return 0;
+}
+
+int xfrmnl_sa_set_auth_params (struct xfrmnl_sa* sa, const char* alg_name, unsigned int key_len, unsigned int trunc_len, const char* key)
+{
+	_nl_auto_free struct xfrmnl_algo_auth *b = NULL;
+	size_t keysize = sizeof (uint8_t) * ((key_len + 7)/8);
+	uint32_t newlen = sizeof (struct xfrmnl_algo_auth) + keysize;
+
+	if (strlen (alg_name) >= sizeof (sa->auth->alg_name))
+		return -1;
+	if (!(b = calloc (1, newlen)))
+		return -1;
+
+	strcpy (b->alg_name, alg_name);
+	b->alg_key_len   = key_len;
+	b->alg_trunc_len = trunc_len;
+	memcpy (b->alg_key, key, keysize);
+
+	free (sa->auth);
+	sa->auth = _nl_steal_pointer (&b);
+	sa->ce_mask |= XFRM_SA_ATTR_ALG_AUTH;
+	return 0;
+}
+
+/**
+ * Get the crypto-params
+ * @arg sa              the xfrmnl_sa object
+ * @arg alg_name        an optional output buffer for the algorithm name. Must be at least 64 bytes.
+ * @arg key_len         an optional output value for the key length in bits.
+ * @arg key             an optional buffer large enough for the key. It must contain at least
+ *                      ((@key_len + 7) / 8) bytes.
+ *
+ * Warning: you must ensure that @key is large enough. If you don't know the key_len before-hand,
+ * call xfrmnl_sa_get_crypto_params() without @key argument to query only the required buffer size.
+ * This modified API is available in all versions of libnl3 that support the capability
+ * @def NL_CAPABILITY_XFRM_SA_KEY_SIZE (@see nl_has_capability for further information).
+ *
+ * @return 0 on success or a negative error code.
+ */
+int xfrmnl_sa_get_crypto_params (struct xfrmnl_sa* sa, char* alg_name, unsigned int* key_len, char* key)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_ALG_CRYPT)
+	{
+		if (alg_name)
+			strcpy (alg_name, sa->crypt->alg_name);
+		if (key_len)
+			*key_len = sa->crypt->alg_key_len;
+		if (key)
+			memcpy (key, sa->crypt->alg_key, ((sa->crypt->alg_key_len + 7)/8));
+	}
+	else
+		return -1;
+
+	return 0;
+}
+
+int xfrmnl_sa_set_crypto_params (struct xfrmnl_sa* sa, const char* alg_name, unsigned int key_len, const char* key)
+{
+	_nl_auto_free struct xfrmnl_algo *b = NULL;
+	size_t keysize = sizeof (uint8_t) * ((key_len + 7)/8);
+	uint32_t newlen = sizeof (struct xfrmnl_algo) + keysize;
+
+	if (strlen (alg_name) >= sizeof (sa->crypt->alg_name))
+		return -1;
+	if (!(b = calloc (1, newlen)))
+		return -1;
+
+	strcpy (b->alg_name, alg_name);
+	b->alg_key_len  = key_len;
+	memcpy (b->alg_key, key, keysize);
+
+	free(sa->crypt);
+	sa->crypt = _nl_steal_pointer(&b);
+	sa->ce_mask |= XFRM_SA_ATTR_ALG_CRYPT;
+	return 0;
+}
+
+/**
+ * Get the comp-params
+ * @arg sa              the xfrmnl_sa object
+ * @arg alg_name        an optional output buffer for the algorithm name. Must be at least 64 bytes.
+ * @arg key_len         an optional output value for the key length in bits.
+ * @arg key             an optional buffer large enough for the key. It must contain at least
+ *                      ((@key_len + 7) / 8) bytes.
+ *
+ * Warning: you must ensure that @key is large enough. If you don't know the key_len before-hand,
+ * call xfrmnl_sa_get_comp_params() without @key argument to query only the required buffer size.
+ * This modified API is available in all versions of libnl3 that support the capability
+ * @def NL_CAPABILITY_XFRM_SA_KEY_SIZE (@see nl_has_capability for further information).
+ *
+ * @return 0 on success or a negative error code.
+ */
+int xfrmnl_sa_get_comp_params (struct xfrmnl_sa* sa, char* alg_name, unsigned int* key_len, char* key)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_ALG_COMP)
+	{
+		if (alg_name)
+			strcpy (alg_name, sa->comp->alg_name);
+		if (key_len)
+			*key_len = sa->comp->alg_key_len;
+		if (key)
+			memcpy (key, sa->comp->alg_key, ((sa->comp->alg_key_len + 7)/8));
+	}
+	else
+		return -1;
+
+	return 0;
+}
+
+int xfrmnl_sa_set_comp_params (struct xfrmnl_sa* sa, const char* alg_name, unsigned int key_len, const char* key)
+{
+	_nl_auto_free struct xfrmnl_algo *b = NULL;
+	size_t keysize = sizeof (uint8_t) * ((key_len + 7)/8);
+	uint32_t newlen = sizeof (struct xfrmnl_algo) + keysize;
+
+	if (strlen (alg_name) >= sizeof (sa->comp->alg_name))
+		return -1;
+	if (!(b = calloc (1, newlen)))
+		return -1;
+
+	strcpy (b->alg_name, alg_name);
+	b->alg_key_len  = key_len;
+	memcpy (b->alg_key, key, keysize);
+
+	free(sa->comp);
+	sa->comp = _nl_steal_pointer(&b);
+	sa->ce_mask |= XFRM_SA_ATTR_ALG_COMP;
+	return 0;
+}
+
+int xfrmnl_sa_get_encap_tmpl (struct xfrmnl_sa* sa, unsigned int* encap_type, unsigned int* encap_sport, unsigned int* encap_dport, struct nl_addr** encap_oa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_ENCAP)
+	{
+		*encap_type     =   sa->encap->encap_type;
+		*encap_sport    =   sa->encap->encap_sport;
+		*encap_dport    =   sa->encap->encap_dport;
+		*encap_oa       =   nl_addr_clone (sa->encap->encap_oa);
+	}
+	else
+		return -1;
+
+	return 0;
+}
+
+int xfrmnl_sa_set_encap_tmpl (struct xfrmnl_sa* sa, unsigned int encap_type, unsigned int encap_sport, unsigned int encap_dport, struct nl_addr* encap_oa)
+{
+	if (sa->encap) {
+		/* Free up the old encap OA */
+		if (sa->encap->encap_oa)
+			nl_addr_put(sa->encap->encap_oa);
+		memset(sa->encap, 0, sizeof (*sa->encap));
+	} else if ((sa->encap = calloc(1, sizeof(*sa->encap))) == NULL)
+		return -1;
+
+	/* Save the new info */
+	sa->encap->encap_type   =   encap_type;
+	sa->encap->encap_sport  =   encap_sport;
+	sa->encap->encap_dport  =   encap_dport;
+	nl_addr_get (encap_oa);
+	sa->encap->encap_oa     =   encap_oa;
+
+	sa->ce_mask |= XFRM_SA_ATTR_ENCAP;
+
+	return 0;
+}
+
+int xfrmnl_sa_get_tfcpad (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_TFCPAD)
+		return sa->tfcpad;
+	else
+		return -1;
+}
+
+int xfrmnl_sa_set_tfcpad (struct xfrmnl_sa* sa, unsigned int tfcpad)
+{
+	sa->tfcpad  =   tfcpad;
+	sa->ce_mask |=  XFRM_SA_ATTR_TFCPAD;
+
+	return 0;
+}
+
+struct nl_addr* xfrmnl_sa_get_coaddr (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_COADDR)
+		return sa->coaddr;
+	else
+		return NULL;
+}
+
+int xfrmnl_sa_set_coaddr (struct xfrmnl_sa* sa, struct nl_addr* coaddr)
+{
+	/* Free up the old coaddr */
+	if (sa->coaddr)
+		nl_addr_put (sa->coaddr);
+
+	/* Save the new info */
+	nl_addr_get (coaddr);
+	sa->coaddr  =   coaddr;
+
+	sa->ce_mask |= XFRM_SA_ATTR_COADDR;
+
+	return 0;
+}
+
+int xfrmnl_sa_get_mark (struct xfrmnl_sa* sa, unsigned int* mark_mask, unsigned int* mark_value)
+{
+	if (mark_mask == NULL || mark_value == NULL)
+		return -1;
+
+	if (sa->ce_mask & XFRM_SA_ATTR_MARK)
+	{
+		*mark_mask  =   sa->mark.m;
+		*mark_value  =   sa->mark.v;
+
+		return 0;
+	}
+	else
+		return -1;
+}
+
+int xfrmnl_sa_set_mark (struct xfrmnl_sa* sa, unsigned int value, unsigned int mask)
+{
+	sa->mark.v  = value;
+	sa->mark.m  = mask;
+	sa->ce_mask |= XFRM_SA_ATTR_MARK;
+
+	return 0;
+}
+
+/**
+ * Get the security context.
+ *
+ * @arg sa              The xfrmnl_sa object.
+ * @arg doi             An optional output value for the security context domain of interpretation.
+ * @arg alg             An optional output value for the security context algorithm.
+ * @arg len             An optional output value for the security context length, including the
+ *                      terminating null byte ('\0').
+ * @arg sid             Unused parameter.
+ * @arg ctx_str         An optional buffer large enough for the security context string. It must
+ *                      contain at least @len bytes.
+ *
+ * Warning: you must ensure that @ctx_str is large enough. If you don't know the length before-hand,
+ * call xfrmnl_sa_get_sec_ctx() without @ctx_str argument to query only the required buffer size.
+ * This modified API is available in all versions of libnl3 that support the capability
+ * @def NL_CAPABILITY_XFRM_SEC_CTX_LEN (@see nl_has_capability for further information).
+ *
+ * @return 0 on success or a negative error code.
+ */
+int xfrmnl_sa_get_sec_ctx (struct xfrmnl_sa* sa, unsigned int* doi, unsigned int* alg,
+		unsigned int* len, unsigned int* sid, char* ctx_str)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_SECCTX)
+	{
+		if (doi)
+			*doi = sa->sec_ctx->ctx_doi;
+		if (alg)
+			*alg = sa->sec_ctx->ctx_alg;
+		if (len)
+			*len = sa->sec_ctx->ctx_len;
+		if (ctx_str)
+			memcpy (ctx_str, sa->sec_ctx->ctx, sa->sec_ctx->ctx_len);
+	}
+	else
+		return -1;
+
+	return 0;
+}
+
+/**
+ * Set the security context.
+ *
+ * @arg sa              The xfrmnl_sa object.
+ * @arg doi             Parameter for the security context domain of interpretation.
+ * @arg alg             Parameter for the security context algorithm.
+ * @arg len             Parameter for the length of the security context string containing
+ *                      the terminating null byte ('\0').
+ * @arg sid             Unused parameter.
+ * @arg ctx_str         Buffer containing the security context string.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int xfrmnl_sa_set_sec_ctx (struct xfrmnl_sa* sa, unsigned int doi, unsigned int alg, unsigned int len,
+                           unsigned int sid, const char* ctx_str)
+{
+	_nl_auto_free struct xfrmnl_user_sec_ctx *b = NULL;
+
+	if (!(b = calloc(1, sizeof (struct xfrmnl_user_sec_ctx) + 1 + len)))
+		return -1;
+
+	b->len     = sizeof(struct xfrmnl_user_sec_ctx) + len;
+	b->exttype = XFRMA_SEC_CTX;
+	b->ctx_alg = alg;
+	b->ctx_doi = doi;
+	b->ctx_len = len;
+	memcpy (b->ctx, ctx_str, len);
+	b->ctx[len] = '\0';
+
+	free(sa->sec_ctx);
+	sa->sec_ctx = _nl_steal_pointer(&b);
+	sa->ce_mask |= XFRM_SA_ATTR_SECCTX;
+	return 0;
+}
+
+
+int xfrmnl_sa_get_replay_maxage (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_REPLAY_MAXAGE)
+		return sa->replay_maxage;
+	else
+		return -1;
+}
+
+int xfrmnl_sa_set_replay_maxage (struct xfrmnl_sa* sa, unsigned int replay_maxage)
+{
+	sa->replay_maxage  = replay_maxage;
+	sa->ce_mask |= XFRM_SA_ATTR_REPLAY_MAXAGE;
+
+	return 0;
+}
+
+int xfrmnl_sa_get_replay_maxdiff (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_REPLAY_MAXDIFF)
+		return sa->replay_maxdiff;
+	else
+		return -1;
+}
+
+int xfrmnl_sa_set_replay_maxdiff (struct xfrmnl_sa* sa, unsigned int replay_maxdiff)
+{
+	sa->replay_maxdiff  = replay_maxdiff;
+	sa->ce_mask |= XFRM_SA_ATTR_REPLAY_MAXDIFF;
+
+	return 0;
+}
+
+int xfrmnl_sa_get_replay_state (struct xfrmnl_sa* sa, unsigned int* oseq, unsigned int* seq, unsigned int* bmp)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_REPLAY_STATE)
+	{
+		if (sa->replay_state_esn == NULL)
+		{
+			*oseq   =   sa->replay_state.oseq;
+			*seq    =   sa->replay_state.seq;
+			*bmp    =   sa->replay_state.bitmap;
+
+			return 0;
+		}
+		else
+		{
+			return -1;
+		}
+	}
+	else
+		return -1;
+}
+
+int xfrmnl_sa_set_replay_state (struct xfrmnl_sa* sa, unsigned int oseq, unsigned int seq, unsigned int bitmap)
+{
+	sa->replay_state.oseq = oseq;
+	sa->replay_state.seq = seq;
+	sa->replay_state.bitmap = bitmap;
+	sa->ce_mask |= XFRM_SA_ATTR_REPLAY_STATE;
+
+	return 0;
+}
+
+int xfrmnl_sa_get_replay_state_esn (struct xfrmnl_sa* sa, unsigned int* oseq, unsigned int* seq, unsigned int* oseq_hi,
+                                    unsigned int* seq_hi, unsigned int* replay_window, unsigned int* bmp_len, unsigned int* bmp)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_REPLAY_STATE)
+	{
+		if (sa->replay_state_esn)
+		{
+			*oseq   =   sa->replay_state_esn->oseq;
+			*seq    =   sa->replay_state_esn->seq;
+			*oseq_hi=   sa->replay_state_esn->oseq_hi;
+			*seq_hi =   sa->replay_state_esn->seq_hi;
+			*replay_window  =   sa->replay_state_esn->replay_window;
+			*bmp_len        =   sa->replay_state_esn->bmp_len; // In number of 32 bit words
+			memcpy (bmp, sa->replay_state_esn->bmp, sa->replay_state_esn->bmp_len * sizeof (uint32_t));
+
+			return 0;
+		}
+		else
+		{
+			return -1;
+		}
+	}
+	else
+		return -1;
+}
+
+int xfrmnl_sa_set_replay_state_esn (struct xfrmnl_sa* sa, unsigned int oseq, unsigned int seq,
+                                    unsigned int oseq_hi, unsigned int seq_hi, unsigned int replay_window,
+                                    unsigned int bmp_len, unsigned int* bmp)
+{
+	_nl_auto_free struct xfrmnl_replay_state_esn *b = NULL;
+
+	if (!(b = calloc (1, sizeof (struct xfrmnl_replay_state_esn) + (sizeof (uint32_t) * bmp_len))))
+		return -1;
+
+	b->oseq = oseq;
+	b->seq = seq;
+	b->oseq_hi = oseq_hi;
+	b->seq_hi = seq_hi;
+	b->replay_window = replay_window;
+	b->bmp_len = bmp_len; // In number of 32 bit words
+	memcpy (b->bmp, bmp, bmp_len * sizeof (uint32_t));
+
+	free(sa->replay_state_esn);
+	sa->replay_state_esn = _nl_steal_pointer(&b);
+	sa->ce_mask |= XFRM_SA_ATTR_REPLAY_STATE;
+	return 0;
+}
+
+
+int xfrmnl_sa_is_hardexpiry_reached (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_EXPIRE)
+		return (sa->hard > 0 ? 1: 0);
+	else
+		return 0;
+}
+
+int xfrmnl_sa_is_expiry_reached (struct xfrmnl_sa* sa)
+{
+	if (sa->ce_mask & XFRM_SA_ATTR_EXPIRE)
+		return 1;
+	else
+		return 0;
+}
+
+/** @} */
+
+static struct nl_object_ops xfrm_sa_obj_ops = {
+	.oo_name        =   "xfrm/sa",
+	.oo_size        =   sizeof(struct xfrmnl_sa),
+	.oo_constructor =   xfrm_sa_alloc_data,
+	.oo_free_data   =   xfrm_sa_free_data,
+	.oo_clone       =   xfrm_sa_clone,
+	.oo_dump        =   {
+	                        [NL_DUMP_LINE]      =   xfrm_sa_dump_line,
+	                        [NL_DUMP_DETAILS]   =   xfrm_sa_dump_details,
+	                        [NL_DUMP_STATS]     =   xfrm_sa_dump_stats,
+	                    },
+	.oo_compare     =   xfrm_sa_compare,
+	.oo_attrs2str   =   xfrm_sa_attrs2str,
+	.oo_id_attrs    =   (XFRM_SA_ATTR_DADDR | XFRM_SA_ATTR_SPI | XFRM_SA_ATTR_PROTO),
+};
+
+static struct nl_af_group xfrm_sa_groups[] = {
+	{ AF_UNSPEC, XFRMNLGRP_SA },
+	{ AF_UNSPEC, XFRMNLGRP_EXPIRE },
+	{ END_OF_GROUP_LIST },
+};
+
+static struct nl_cache_ops xfrmnl_sa_ops = {
+	.co_name            = "xfrm/sa",
+	.co_hdrsize         = sizeof(struct xfrm_usersa_info),
+	.co_msgtypes        = {
+	                        { XFRM_MSG_NEWSA, NL_ACT_NEW, "new" },
+	                        { XFRM_MSG_DELSA, NL_ACT_DEL, "del" },
+	                        { XFRM_MSG_GETSA, NL_ACT_GET, "get" },
+	                        { XFRM_MSG_EXPIRE, NL_ACT_UNSPEC, "expire"},
+	                        { XFRM_MSG_UPDSA, NL_ACT_NEW, "update"},
+	                        END_OF_MSGTYPES_LIST,
+	                      },
+	.co_protocol        = NETLINK_XFRM,
+	.co_groups          = xfrm_sa_groups,
+	.co_request_update  = xfrm_sa_request_update,
+	.co_msg_parser      = xfrm_sa_msg_parser,
+	.co_obj_ops         = &xfrm_sa_obj_ops,
+	.co_include_event   = &xfrm_sa_update_cache
+};
+
+/**
+ * @name XFRM SA Cache Managament
+ * @{
+ */
+
+static void __attribute__ ((constructor)) xfrm_sa_init(void)
+{
+	nl_cache_mngt_register(&xfrmnl_sa_ops);
+}
+
+static void __attribute__ ((destructor)) xfrm_sa_exit(void)
+{
+	nl_cache_mngt_unregister(&xfrmnl_sa_ops);
+}
+
+/** @} */
diff --git a/lib/xfrm/selector.c b/lib/xfrm/selector.c
new file mode 100644
index 0000000..04d30c3
--- /dev/null
+++ b/lib/xfrm/selector.c
@@ -0,0 +1,353 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *    Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *    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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  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.
+ *
+ */
+/**
+ * @ingroup xfrmnl
+ * @defgroup XFRM Address Selector
+ *
+ * Abstract data type representing XFRM SA/SP selector properties
+ *
+ * @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/xfrm/selector.h>
+ * ~~~~
+ */
+
+#include <netlink/xfrm/selector.h>
+#include <netlink-private/netlink.h>
+
+static void sel_destroy(struct xfrmnl_sel* sel)
+{
+	if (!sel)
+		return;
+
+	if (sel->refcnt != 1)
+	{
+		fprintf(stderr, "BUG: %s:%d\n", __FILE__, __LINE__);
+		assert(0);
+	}
+
+	nl_addr_put (sel->daddr);
+	nl_addr_put (sel->saddr);
+	free(sel);
+}
+
+/**
+ * @name Creating Selector
+ * @{
+ */
+
+/**
+ * Allocate new selector object.
+ * @return Newly allocated selector object or NULL
+ */
+struct xfrmnl_sel* xfrmnl_sel_alloc()
+{
+	struct xfrmnl_sel* sel;
+
+	sel = calloc(1, sizeof(struct xfrmnl_sel));
+	if (!sel)
+		return NULL;
+
+	sel->refcnt = 1;
+
+	return sel;
+}
+
+/**
+ * Clone existing selector object.
+ * @arg sel		Selector object.
+ * @return Newly allocated selector object being a duplicate of the
+ *         specified selector object or NULL if a failure occured.
+ */
+struct xfrmnl_sel* xfrmnl_sel_clone(struct xfrmnl_sel* sel)
+{
+	struct xfrmnl_sel* new;
+
+	new = xfrmnl_sel_alloc();
+	if (!new)
+		return NULL;
+
+	memcpy(new, sel, sizeof(struct xfrmnl_sel));
+	new->daddr = nl_addr_clone(sel->daddr);
+	new->saddr = nl_addr_clone(sel->saddr);
+
+	return new;
+}
+
+/** @} */
+
+/**
+ * @name Managing Usage References
+ * @{
+ */
+
+struct xfrmnl_sel* xfrmnl_sel_get(struct xfrmnl_sel* sel)
+{
+	sel->refcnt++;
+
+	return sel;
+}
+
+void xfrmnl_sel_put(struct xfrmnl_sel* sel)
+{
+	if (!sel)
+		return;
+
+	if (sel->refcnt == 1)
+		sel_destroy(sel);
+	else
+		sel->refcnt--;
+}
+
+/**
+ * Check whether an selector object is shared.
+ * @arg addr		Selector object.
+ * @return Non-zero if the selector object is shared, otherwise 0.
+ */
+int xfrmnl_sel_shared(struct xfrmnl_sel* sel)
+{
+	return sel->refcnt > 1;
+}
+
+/** @} */
+
+/**
+ * @name Miscellaneous
+ * @{
+ */
+
+/**
+ * Compares two selector objects.
+ * @arg a		A selector object.
+ * @arg b		Another selector object.
+ *
+ * @return Non zero if difference is found, 0 otherwise if both
+ * the objects are identical.
+ */
+int xfrmnl_sel_cmp(struct xfrmnl_sel* a, struct xfrmnl_sel* b)
+{
+	/* Check for any differences */
+	if ((nl_addr_cmp_prefix (a->daddr, b->daddr) != 0) ||
+	    (nl_addr_cmp_prefix (a->saddr, b->saddr) != 0) ||
+	    ((a->sport & a->sport_mask) != (b->sport & b->sport_mask)) ||
+	    ((a->dport & a->dport_mask) != (b->dport & b->dport_mask)) ||
+	    (a->family != b->family) ||
+	    (a->proto && (a->proto != b->proto)) ||
+	    (a->ifindex && a->ifindex != b->ifindex) ||
+	    (a->user != b->user))
+		return 1;
+
+	/* The objects are identical */
+	return 0;
+}
+
+void xfrmnl_sel_dump(struct xfrmnl_sel* sel, struct nl_dump_params *p)
+{
+	char    dst[INET6_ADDRSTRLEN+5], src[INET6_ADDRSTRLEN+5];
+	char    buf [128];
+
+	nl_dump_line(p, "\t\tsrc %s dst %s family: %s\n", nl_addr2str(sel->saddr, src, sizeof(src)),
+	              nl_addr2str (sel->daddr, dst, sizeof (dst)), nl_af2str (sel->family, buf, 128));
+	nl_dump_line (p, "\t\tsrc port/mask: %d/%d dst port/mask: %d/%d\n",
+	              sel->dport, sel->dport_mask, sel->sport, sel->sport_mask);
+	nl_dump_line (p, "\t\tprotocol: %s ifindex: %u user: %u\n",
+	              nl_ip_proto2str (sel->proto, buf, sizeof(buf)), sel->ifindex, sel->user);
+
+	return;
+}
+
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+struct nl_addr* xfrmnl_sel_get_daddr (struct xfrmnl_sel* sel)
+{
+	return sel->daddr;
+}
+
+int xfrmnl_sel_set_daddr (struct xfrmnl_sel* sel, struct nl_addr* addr)
+{
+	/* Increment reference counter on this to keep this address
+	 * object around while selector in use */
+	nl_addr_get(addr);
+
+	sel->daddr = addr;
+
+	return 0;
+}
+
+struct nl_addr* xfrmnl_sel_get_saddr (struct xfrmnl_sel* sel)
+{
+	return sel->saddr;
+}
+
+int xfrmnl_sel_set_saddr (struct xfrmnl_sel* sel, struct nl_addr* addr)
+{
+	/* Increment reference counter on this to keep this address
+	 * object around while selector in use */
+	nl_addr_get(addr);
+
+	sel->saddr = addr;
+
+	return 0;
+}
+
+int xfrmnl_sel_get_dport (struct xfrmnl_sel* sel)
+{
+	return sel->dport;
+}
+
+int xfrmnl_sel_set_dport (struct xfrmnl_sel* sel, unsigned int dport)
+{
+	sel->dport = dport;
+
+	return 0;
+}
+
+int xfrmnl_sel_get_dportmask (struct xfrmnl_sel* sel)
+{
+	return sel->dport_mask;
+}
+
+int xfrmnl_sel_set_dportmask (struct xfrmnl_sel* sel, unsigned int dport_mask)
+{
+	sel->dport_mask = dport_mask;
+
+	return 0;
+}
+
+int xfrmnl_sel_get_sport (struct xfrmnl_sel* sel)
+{
+	return sel->sport;
+}
+
+int xfrmnl_sel_set_sport (struct xfrmnl_sel* sel, unsigned int sport)
+{
+	sel->sport = sport;
+
+	return 0;
+}
+
+int xfrmnl_sel_get_sportmask (struct xfrmnl_sel* sel)
+{
+	return sel->sport_mask;
+}
+
+int xfrmnl_sel_set_sportmask (struct xfrmnl_sel* sel, unsigned int sport_mask)
+{
+	sel->sport_mask = sport_mask;
+
+	return 0;
+}
+
+int xfrmnl_sel_get_family(struct xfrmnl_sel *sel)
+{
+	return sel->family;
+}
+
+int xfrmnl_sel_set_family(struct xfrmnl_sel *sel, unsigned int family)
+{
+	sel->family = family;
+
+	return 0;
+}
+
+int xfrmnl_sel_get_prefixlen_d (struct xfrmnl_sel* sel)
+{
+	return sel->prefixlen_d;
+}
+
+int xfrmnl_sel_set_prefixlen_d (struct xfrmnl_sel* sel, unsigned int prefixlen)
+{
+	sel->prefixlen_d = prefixlen;
+
+	return 0;
+}
+
+int xfrmnl_sel_get_prefixlen_s (struct xfrmnl_sel* sel)
+{
+	return sel->prefixlen_s;
+}
+
+int xfrmnl_sel_set_prefixlen_s (struct xfrmnl_sel* sel, unsigned int prefixlen)
+{
+	sel->prefixlen_s = prefixlen;
+
+	return 0;
+}
+
+int xfrmnl_sel_get_proto (struct xfrmnl_sel* sel)
+{
+	return sel->proto;
+}
+
+int xfrmnl_sel_set_proto (struct xfrmnl_sel* sel, unsigned int protocol)
+{
+	sel->proto = protocol;
+
+	return 0;
+}
+
+int xfrmnl_sel_get_ifindex (struct xfrmnl_sel* sel)
+{
+	return sel->ifindex;
+}
+
+int xfrmnl_sel_set_ifindex (struct xfrmnl_sel* sel, unsigned int ifindex)
+{
+	sel->ifindex = ifindex;
+
+	return 0;
+}
+
+int xfrmnl_sel_get_userid (struct xfrmnl_sel* sel)
+{
+	return sel->user;
+}
+
+int xfrmnl_sel_set_userid (struct xfrmnl_sel* sel, unsigned int userid)
+{
+	sel->user   = userid;
+	return 0;
+}
+
+
+/** @} */
diff --git a/lib/xfrm/sp.c b/lib/xfrm/sp.c
new file mode 100644
index 0000000..99b6a4c
--- /dev/null
+++ b/lib/xfrm/sp.c
@@ -0,0 +1,1436 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *    Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *    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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  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.
+ *
+ */
+
+/**
+ * @ingroup xfrmnl
+ * @defgroup sp Security Policy
+ * @brief
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/object.h>
+#include <netlink/xfrm/selector.h>
+#include <netlink/xfrm/lifetime.h>
+#include <netlink/xfrm/template.h>
+#include <netlink/xfrm/sp.h>
+
+/** @cond SKIP */
+#define XFRM_SP_ATTR_SEL            0x01
+#define XFRM_SP_ATTR_LTIME_CFG      0x02
+#define XFRM_SP_ATTR_LTIME_CUR      0x04
+#define XFRM_SP_ATTR_PRIO           0x08
+#define XFRM_SP_ATTR_INDEX          0x10
+#define XFRM_SP_ATTR_DIR            0x20
+#define XFRM_SP_ATTR_ACTION         0x40
+#define XFRM_SP_ATTR_FLAGS          0x80
+#define XFRM_SP_ATTR_SHARE          0x100
+#define XFRM_SP_ATTR_POLTYPE        0x200
+#define XFRM_SP_ATTR_SECCTX         0x400
+#define XFRM_SP_ATTR_TMPL           0x800
+#define XFRM_SP_ATTR_MARK           0x1000
+
+static struct nl_cache_ops  xfrmnl_sp_ops;
+static struct nl_object_ops xfrm_sp_obj_ops;
+/** @endcond */
+
+static void xfrm_sp_alloc_data(struct nl_object *c)
+{
+	struct xfrmnl_sp* sp =   nl_object_priv (c);
+
+	if ((sp->sel = xfrmnl_sel_alloc ()) == NULL)
+		return;
+
+	if ((sp->lft = xfrmnl_ltime_cfg_alloc ()) == NULL)
+		return;
+
+	nl_init_list_head(&sp->usertmpl_list);
+
+	return;
+}
+
+static void xfrm_sp_free_data(struct nl_object *c)
+{
+	struct xfrmnl_sp* sp =   nl_object_priv (c);
+	struct xfrmnl_user_tmpl *utmpl, *tmp;
+
+	if (sp == NULL)
+		return;
+
+	xfrmnl_sel_put (sp->sel);
+	xfrmnl_ltime_cfg_put (sp->lft);
+
+	if(sp->sec_ctx)
+	{
+		free (sp->sec_ctx);
+	}
+
+	nl_list_for_each_entry_safe(utmpl, tmp, &sp->usertmpl_list, utmpl_list) {
+		xfrmnl_sp_remove_usertemplate (sp, utmpl);
+		xfrmnl_user_tmpl_free (utmpl);
+	}
+}
+
+static int xfrm_sp_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+	struct xfrmnl_sp*       dst = nl_object_priv(_dst);
+	struct xfrmnl_sp*       src = nl_object_priv(_src);
+	uint32_t                len = 0;
+	struct xfrmnl_user_tmpl *utmpl, *new;
+
+	if (src->sel)
+		if ((dst->sel = xfrmnl_sel_clone (src->sel)) == NULL)
+			return -NLE_NOMEM;
+
+	if (src->lft)
+		if ((dst->lft = xfrmnl_ltime_cfg_clone (src->lft)) == NULL)
+			return -NLE_NOMEM;
+
+	if(src->sec_ctx)
+	{
+		len =   sizeof (struct xfrmnl_user_sec_ctx) + src->sec_ctx->ctx_len;
+		if ((dst->sec_ctx = calloc (1, len)) == NULL)
+			return -NLE_NOMEM;
+		memcpy ((void *)dst->sec_ctx, (void *)src->sec_ctx, len);
+	}
+
+	nl_init_list_head(&dst->usertmpl_list);
+	nl_list_for_each_entry(utmpl, &src->usertmpl_list, utmpl_list) {
+		new = xfrmnl_user_tmpl_clone (utmpl);
+		if (!new)
+			return -NLE_NOMEM;
+
+		xfrmnl_sp_add_usertemplate(dst, new);
+	}
+
+	return 0;
+}
+
+static uint64_t xfrm_sp_compare(struct nl_object *_a, struct nl_object *_b,
+				uint64_t attrs, int flags)
+{
+	struct xfrmnl_sp* a  =   (struct xfrmnl_sp *) _a;
+	struct xfrmnl_sp* b  =   (struct xfrmnl_sp *) _b;
+	struct xfrmnl_user_tmpl *tmpl_a, *tmpl_b;
+	uint64_t diff = 0;
+
+#define XFRM_SP_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, XFRM_SP_ATTR_##ATTR, a, b, EXPR)
+	diff |= XFRM_SP_DIFF(SEL,	xfrmnl_sel_cmp(a->sel, b->sel));
+	diff |= XFRM_SP_DIFF(LTIME_CFG,	xfrmnl_ltime_cfg_cmp(a->lft, b->lft));
+	diff |= XFRM_SP_DIFF(PRIO,	a->priority != b->priority);
+	diff |= XFRM_SP_DIFF(INDEX,	a->index != b->index);
+	diff |= XFRM_SP_DIFF(DIR,	a->dir != b->dir);
+	diff |= XFRM_SP_DIFF(ACTION,	a->action != b->action);
+	diff |= XFRM_SP_DIFF(FLAGS,	a->flags != b->flags);
+	diff |= XFRM_SP_DIFF(SHARE,	a->share != b->share);
+	diff |= XFRM_SP_DIFF(SECCTX,((a->sec_ctx->len != b->sec_ctx->len) ||
+	                            (a->sec_ctx->exttype != b->sec_ctx->exttype) ||
+	                            (a->sec_ctx->ctx_alg != b->sec_ctx->ctx_alg) ||
+	                            (a->sec_ctx->ctx_doi != b->sec_ctx->ctx_doi) ||
+	                            (a->sec_ctx->ctx_len != b->sec_ctx->ctx_len) ||
+	                            strcmp(a->sec_ctx->ctx, b->sec_ctx->ctx)));
+	diff |= XFRM_SP_DIFF(POLTYPE,(a->uptype.type != b->uptype.type));
+	diff |= XFRM_SP_DIFF(TMPL,(a->nr_user_tmpl != b->nr_user_tmpl));
+	diff |= XFRM_SP_DIFF(MARK,(a->mark.m != b->mark.m) ||
+	                          (a->mark.v != b->mark.v));
+
+	/* Compare the templates */
+	nl_list_for_each_entry(tmpl_b, &b->usertmpl_list, utmpl_list)
+	nl_list_for_each_entry(tmpl_a, &a->usertmpl_list, utmpl_list)
+	diff |= xfrmnl_user_tmpl_cmp (tmpl_a, tmpl_b);
+#undef XFRM_SP_DIFF
+
+	return diff;
+}
+
+/**
+ * @name XFRM SP Attribute Translations
+ * @{
+ */
+static const struct trans_tbl sp_attrs[] = {
+	__ADD(XFRM_SP_ATTR_SEL, selector),
+	__ADD(XFRM_SP_ATTR_LTIME_CFG, lifetime_cfg),
+	__ADD(XFRM_SP_ATTR_LTIME_CUR, lifetime_cur),
+	__ADD(XFRM_SP_ATTR_PRIO, priority),
+	__ADD(XFRM_SP_ATTR_INDEX, index),
+	__ADD(XFRM_SP_ATTR_DIR, direction),
+	__ADD(XFRM_SP_ATTR_ACTION, action),
+	__ADD(XFRM_SP_ATTR_FLAGS, flags),
+	__ADD(XFRM_SP_ATTR_SHARE, share),
+	__ADD(XFRM_SP_ATTR_POLTYPE, policy_type),
+	__ADD(XFRM_SP_ATTR_SECCTX, security_context),
+	__ADD(XFRM_SP_ATTR_TMPL, user_template),
+	__ADD(XFRM_SP_ATTR_MARK, mark),
+};
+
+static char* xfrm_sp_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str (attrs, buf, len, sp_attrs, ARRAY_SIZE(sp_attrs));
+}
+/** @} */
+
+/**
+ * @name XFRM SP Action Translations
+ * @{
+ */
+static const struct trans_tbl sa_actions[] = {
+	__ADD(XFRM_POLICY_ALLOW, allow),
+	__ADD(XFRM_POLICY_BLOCK, block),
+};
+
+char* xfrmnl_sp_action2str(int action, char *buf, size_t len)
+{
+	return __type2str (action, buf, len, sa_actions, ARRAY_SIZE(sa_actions));
+}
+
+int xfrmnl_sp_str2action(const char *name)
+{
+	return __str2type (name, sa_actions, ARRAY_SIZE(sa_actions));
+}
+/** @} */
+
+/**
+ * @name XFRM SP Flags Translations
+ * @{
+ */
+static const struct trans_tbl sp_flags[] = {
+	__ADD(XFRM_POLICY_LOCALOK, allow policy override by user),
+	__ADD(XFRM_POLICY_ICMP, auto include ICMP in policy),
+};
+
+char* xfrmnl_sp_flags2str(int flags, char *buf, size_t len)
+{
+	return __flags2str (flags, buf, len, sp_flags, ARRAY_SIZE(sp_flags));
+}
+
+int xfrmnl_sp_str2flag(const char *name)
+{
+	return __str2flags(name, sp_flags, ARRAY_SIZE(sp_flags));
+}
+/** @} */
+
+/**
+ * @name XFRM SP Type Translations
+ * @{
+ */
+static const struct trans_tbl sp_types[] = {
+	__ADD(XFRM_POLICY_TYPE_MAIN, main),
+	__ADD(XFRM_POLICY_TYPE_SUB, sub),
+	__ADD(XFRM_POLICY_TYPE_MAX, max),
+	__ADD(XFRM_POLICY_TYPE_ANY, any),
+};
+
+char* xfrmnl_sp_type2str(int type, char *buf, size_t len)
+{
+	return __type2str(type, buf, len, sp_types, ARRAY_SIZE(sp_types));
+}
+
+int xfrmnl_sp_str2type(const char *name)
+{
+	return __str2type(name, sp_types, ARRAY_SIZE(sp_types));
+}
+/** @} */
+
+/**
+ * @name XFRM SP Direction Translations
+ * @{
+ */
+static const struct trans_tbl sp_dir[] = {
+	__ADD(XFRM_POLICY_IN, in),
+	__ADD(XFRM_POLICY_OUT, out),
+	__ADD(XFRM_POLICY_FWD, fwd),
+	__ADD(XFRM_POLICY_MASK, mask),
+};
+
+char* xfrmnl_sp_dir2str(int dir, char *buf, size_t len)
+{
+	return __type2str (dir, buf, len, sp_dir, ARRAY_SIZE(sp_dir));
+}
+
+int xfrmnl_sp_str2dir(const char *name)
+{
+	return __str2type (name, sp_dir, ARRAY_SIZE(sp_dir));
+}
+
+int xfrmnl_sp_index2dir (unsigned int index)
+{
+	return index & 0x7;
+}
+/** @} */
+
+/**
+ * @name XFRM SP Share Translations
+ * @{
+ */
+static const struct trans_tbl sp_share[] = {
+	__ADD(XFRM_SHARE_ANY, any),
+	__ADD(XFRM_SHARE_SESSION, session),
+	__ADD(XFRM_SHARE_USER, user),
+	__ADD(XFRM_SHARE_UNIQUE, unique),
+};
+
+char* xfrmnl_sp_share2str(int share, char *buf, size_t len)
+{
+	return __type2str (share, buf, len, sp_share, ARRAY_SIZE(sp_share));
+}
+
+int xfrmnl_sp_str2share(const char *name)
+{
+	return __str2type (name, sp_share, ARRAY_SIZE(sp_share));
+}
+/** @} */
+
+static void xfrm_sp_dump_line(struct nl_object *a, struct nl_dump_params *p)
+{
+	struct xfrmnl_sp*   sp  =   (struct xfrmnl_sp *) a;
+	char                dir[32], action[32], share[32], flags[32];
+	char                dst[INET6_ADDRSTRLEN+5], src[INET6_ADDRSTRLEN+5];
+	time_t              add_time, use_time;
+	struct tm           *add_time_tm, *use_time_tm;
+
+	nl_addr2str(xfrmnl_sel_get_saddr (sp->sel), src, sizeof(src));
+	nl_addr2str (xfrmnl_sel_get_daddr (sp->sel), dst, sizeof (dst));
+	nl_af2str (xfrmnl_sel_get_family (sp->sel), dir, 32);
+	nl_dump_line(p, "src %s dst %s family: %s\n", src, dst, dir);
+	nl_dump_line (p, "src port/mask: %d/%d dst port/mask: %d/%d\n",
+	              xfrmnl_sel_get_dport (sp->sel), xfrmnl_sel_get_dportmask (sp->sel),
+	              xfrmnl_sel_get_sport (sp->sel), xfrmnl_sel_get_sportmask (sp->sel));
+	nl_dump_line (p, "protocol: %s ifindex: %u uid: %u\n",
+	              nl_ip_proto2str (xfrmnl_sel_get_proto (sp->sel), dir, sizeof(dir)),
+	              xfrmnl_sel_get_ifindex (sp->sel),
+	              xfrmnl_sel_get_userid (sp->sel));
+
+	xfrmnl_sp_dir2str (sp->dir, dir, 32);
+	xfrmnl_sp_action2str (sp->action, action, 32);
+	xfrmnl_sp_share2str (sp->share, share, 32);
+	xfrmnl_sp_flags2str (sp->flags, flags, 32);
+	nl_dump_line(p, "\tdir: %s action: %s index: %u priority: %u share: %s flags: %s(0x%x) \n",
+	             dir, action, sp->index, sp->priority, share, flags, sp->flags);
+
+	nl_dump_line(p, "\tlifetime configuration: \n");
+	if (sp->lft->soft_byte_limit == XFRM_INF)
+		sprintf (dir, "INF");
+	else
+		sprintf (dir, "%" PRIu64, sp->lft->soft_byte_limit);
+	if (sp->lft->soft_packet_limit == XFRM_INF)
+		sprintf (action, "INF");
+	else
+		sprintf (action, "%" PRIu64, sp->lft->soft_packet_limit);
+	if (sp->lft->hard_byte_limit == XFRM_INF)
+		sprintf (flags, "INF");
+	else
+		sprintf (flags, "%" PRIu64, sp->lft->hard_byte_limit);
+	if (sp->lft->hard_packet_limit == XFRM_INF)
+		sprintf (share, "INF");
+	else
+		sprintf (share, "%" PRIu64, sp->lft->hard_packet_limit);
+	nl_dump_line(p, "\t\tsoft limit: %s (bytes), %s (packets) \n", dir, action);
+	nl_dump_line(p, "\t\thard limit: %s (bytes), %s (packets) \n", flags, share);
+	nl_dump_line(p, "\t\tsoft add_time: %llu (seconds), soft use_time: %llu (seconds) \n",
+	             sp->lft->soft_add_expires_seconds, sp->lft->soft_use_expires_seconds);
+	nl_dump_line(p, "\t\thard add_time: %llu (seconds), hard use_time: %llu (seconds) \n",
+	             sp->lft->hard_add_expires_seconds, sp->lft->hard_use_expires_seconds);
+
+	nl_dump_line(p, "\tlifetime current: \n");
+	nl_dump_line(p, "\t\t%llu bytes, %llu packets\n", sp->curlft.bytes, sp->curlft.packets);
+
+	if (sp->curlft.add_time != 0)
+	{
+		add_time = sp->curlft.add_time;
+		add_time_tm = gmtime (&add_time);
+		strftime (dst, INET6_ADDRSTRLEN+5, "%Y-%m-%d %H-%M-%S", add_time_tm);
+	}
+	else
+	{
+		sprintf (dst, "%s", "-");
+	}
+
+	if (sp->curlft.use_time != 0)
+	{
+		use_time = sp->curlft.use_time;
+		use_time_tm = gmtime (&use_time);
+		strftime (src, INET6_ADDRSTRLEN+5, "%Y-%m-%d %H-%M-%S", use_time_tm);
+	}
+	else
+	{
+		sprintf (src, "%s", "-");
+	}
+	nl_dump_line(p, "\t\tadd_time: %s, use_time: %s\n", dst, src);
+
+	if (sp->ce_mask & XFRM_SP_ATTR_SECCTX)
+	{
+		nl_dump_line(p, "\tUser security context: \n");
+		nl_dump_line(p, "\t\tlen: %d exttype: %d Algo: %d DOI: %d ctxlen: %d\n",
+		             sp->sec_ctx->len, sp->sec_ctx->exttype,
+		             sp->sec_ctx->ctx_alg, sp->sec_ctx->ctx_doi, sp->sec_ctx->ctx_len);
+		nl_dump_line (p, "\t\tctx: %s \n", sp->sec_ctx->ctx);
+	}
+
+	xfrmnl_sp_type2str (sp->uptype.type, flags, 32);
+	if (sp->ce_mask & XFRM_SP_ATTR_POLTYPE)
+		nl_dump_line(p, "\tUser policy type: %s\n", flags);
+
+	if (sp->ce_mask & XFRM_SP_ATTR_TMPL)
+	{
+		struct xfrmnl_user_tmpl*    utmpl;
+
+		nl_dump_line(p, "\tUser template: \n");
+
+		nl_list_for_each_entry(utmpl, &sp->usertmpl_list, utmpl_list)
+			xfrmnl_user_tmpl_dump (utmpl, p);
+	}
+
+	if (sp->ce_mask & XFRM_SP_ATTR_MARK)
+		nl_dump_line(p, "\tMark mask: 0x%x Mark value: 0x%x\n", sp->mark.m, sp->mark.v);
+
+	nl_dump(p, "\n");
+}
+
+static void xfrm_sp_dump_details(struct nl_object *a, struct nl_dump_params *p)
+{
+	xfrm_sp_dump_line(a, p);
+}
+
+static void xfrm_sp_dump_stats(struct nl_object *a, struct nl_dump_params *p)
+{
+	xfrm_sp_dump_details(a, p);
+
+	return;
+}
+
+/**
+ * @name XFRM SP Object Allocation/Freeage
+ * @{
+ */
+
+struct xfrmnl_sp* xfrmnl_sp_alloc(void)
+{
+	return (struct xfrmnl_sp*) nl_object_alloc(&xfrm_sp_obj_ops);
+}
+
+void xfrmnl_sp_put(struct xfrmnl_sp* sp)
+{
+	nl_object_put((struct nl_object *) sp);
+}
+
+/** @} */
+
+/**
+ * @name SP Cache Managament
+ * @{
+ */
+
+/**
+ * Build a SP cache including all SPs currently configured in the kernel.
+ * @arg sock		Netlink socket.
+ * @arg result		Pointer to store resulting cache.
+ *
+ * Allocates a new SP cache, initializes it properly and updates it
+ * to include all SPs currently configured in the kernel.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int xfrmnl_sp_alloc_cache(struct nl_sock *sock, struct nl_cache **result)
+{
+	return nl_cache_alloc_and_fill(&xfrmnl_sp_ops, sock, result);
+}
+
+/**
+ * Look up a SP by policy id and direction
+ * @arg cache		SP cache
+ * @arg index		Policy Id
+ * @arg dir         direction
+ * @return sp handle or NULL if no match was found.
+ */
+struct xfrmnl_sp* xfrmnl_sp_get(struct nl_cache* cache, unsigned int index, unsigned int dir)
+{
+	struct xfrmnl_sp *sp;
+
+	//nl_list_for_each_entry(sp, &cache->c_items, ce_list) {
+	for (sp = (struct xfrmnl_sp*)nl_cache_get_first (cache);
+	     sp != NULL;
+	     sp = (struct xfrmnl_sp*)nl_cache_get_next ((struct nl_object*)sp))
+	{
+		if (sp->index == index && sp->dir == dir)
+		{
+			nl_object_get((struct nl_object *) sp);
+			return sp;
+		}
+	}
+
+	return NULL;
+}
+
+
+/** @} */
+
+
+static struct nla_policy xfrm_sp_policy[XFRMA_MAX+1] = {
+	[XFRMA_POLICY]          = { .minlen = sizeof(struct xfrm_userpolicy_info)},
+	[XFRMA_SEC_CTX]         = { .minlen = sizeof(struct xfrm_sec_ctx) },
+	[XFRMA_TMPL]            = { .minlen = sizeof(struct xfrm_user_tmpl) },
+	[XFRMA_POLICY_TYPE]     = { .minlen = sizeof(struct xfrm_userpolicy_type)},
+	[XFRMA_MARK]            = { .minlen = sizeof(struct xfrm_mark) },
+};
+
+static int xfrm_sp_request_update(struct nl_cache *c, struct nl_sock *h)
+{
+	struct xfrm_userpolicy_id sp_id;
+
+	memset (&sp_id, 0, sizeof (sp_id));
+	return nl_send_simple (h, XFRM_MSG_GETPOLICY, NLM_F_DUMP,
+	                       &sp_id, sizeof (sp_id));
+}
+
+int xfrmnl_sp_parse(struct nlmsghdr *n, struct xfrmnl_sp **result)
+{
+	struct xfrmnl_sp                *sp;
+	struct nlattr                   *tb[XFRMA_MAX + 1];
+	struct xfrm_userpolicy_info     *sp_info;
+	int                             len, err;
+	struct nl_addr*                 addr;
+
+	sp = xfrmnl_sp_alloc();
+	if (!sp) {
+		err = -NLE_NOMEM;
+		goto errout;
+	}
+
+	sp->ce_msgtype = n->nlmsg_type;
+	if (n->nlmsg_type == XFRM_MSG_DELPOLICY)
+	{
+		sp_info = (struct xfrm_userpolicy_info*)((char *)nlmsg_data(n) + sizeof (struct xfrm_userpolicy_id) + NLA_HDRLEN);
+	}
+	else
+	{
+		sp_info = nlmsg_data(n);
+	}
+
+	err = nlmsg_parse(n, sizeof(struct xfrm_userpolicy_info), tb, XFRMA_MAX, xfrm_sp_policy);
+	if (err < 0)
+	{
+		printf ("parse error: %d \n", err);
+		goto errout;
+	}
+
+	if (sp_info->sel.family == AF_INET)
+		addr    = nl_addr_build (sp_info->sel.family, &sp_info->sel.daddr.a4, sizeof (sp_info->sel.daddr.a4));
+	else
+		addr    = nl_addr_build (sp_info->sel.family, &sp_info->sel.daddr.a6, sizeof (sp_info->sel.daddr.a6));
+	nl_addr_set_prefixlen (addr, sp_info->sel.prefixlen_d);
+	xfrmnl_sel_set_daddr (sp->sel, addr);
+	xfrmnl_sel_set_prefixlen_d (sp->sel, sp_info->sel.prefixlen_d);
+
+	if (sp_info->sel.family == AF_INET)
+		addr    = nl_addr_build (sp_info->sel.family, &sp_info->sel.saddr.a4, sizeof (sp_info->sel.saddr.a4));
+	else
+		addr    = nl_addr_build (sp_info->sel.family, &sp_info->sel.saddr.a6, sizeof (sp_info->sel.saddr.a6));
+	nl_addr_set_prefixlen (addr, sp_info->sel.prefixlen_s);
+	xfrmnl_sel_set_saddr (sp->sel, addr);
+	xfrmnl_sel_set_prefixlen_s (sp->sel, sp_info->sel.prefixlen_s);
+
+	xfrmnl_sel_set_dport (sp->sel, ntohs (sp_info->sel.dport));
+	xfrmnl_sel_set_dportmask (sp->sel, ntohs (sp_info->sel.dport_mask));
+	xfrmnl_sel_set_sport (sp->sel, ntohs (sp_info->sel.sport));
+	xfrmnl_sel_set_sportmask (sp->sel, ntohs (sp_info->sel.sport_mask));
+	xfrmnl_sel_set_family (sp->sel, sp_info->sel.family);
+	xfrmnl_sel_set_proto (sp->sel, sp_info->sel.proto);
+	xfrmnl_sel_set_ifindex (sp->sel, sp_info->sel.ifindex);
+	xfrmnl_sel_set_userid (sp->sel, sp_info->sel.user);
+	sp->ce_mask             |= XFRM_SP_ATTR_SEL;
+
+	sp->lft->soft_byte_limit    =   sp_info->lft.soft_byte_limit;
+	sp->lft->hard_byte_limit    =   sp_info->lft.hard_byte_limit;
+	sp->lft->soft_packet_limit  =   sp_info->lft.soft_packet_limit;
+	sp->lft->hard_packet_limit  =   sp_info->lft.hard_packet_limit;
+	sp->lft->soft_add_expires_seconds   =   sp_info->lft.soft_add_expires_seconds;
+	sp->lft->hard_add_expires_seconds   =   sp_info->lft.hard_add_expires_seconds;
+	sp->lft->soft_use_expires_seconds   =   sp_info->lft.soft_use_expires_seconds;
+	sp->lft->hard_use_expires_seconds   =   sp_info->lft.hard_use_expires_seconds;
+	sp->ce_mask             |= XFRM_SP_ATTR_LTIME_CFG;
+
+	sp->curlft.bytes        = sp_info->curlft.bytes;
+	sp->curlft.packets      = sp_info->curlft.packets;
+	sp->curlft.add_time     = sp_info->curlft.add_time;
+	sp->curlft.use_time     = sp_info->curlft.use_time;
+	sp->ce_mask             |= XFRM_SP_ATTR_LTIME_CUR;
+
+	sp->priority            = sp_info->priority;
+	sp->index               = sp_info->index;
+	sp->dir                 = sp_info->dir;
+	sp->action              = sp_info->action;
+	sp->flags               = sp_info->flags;
+	sp->share               = sp_info->share;
+	sp->ce_mask             |= (XFRM_SP_ATTR_PRIO | XFRM_SP_ATTR_INDEX |
+	                            XFRM_SP_ATTR_DIR | XFRM_SP_ATTR_ACTION |
+	                            XFRM_SP_ATTR_FLAGS | XFRM_SP_ATTR_SHARE);
+
+	if (tb[XFRMA_SEC_CTX]) {
+		struct xfrm_user_sec_ctx* ctx = nla_data(tb[XFRMA_SEC_CTX]);
+		len = sizeof (struct xfrmnl_user_sec_ctx) + ctx->ctx_len;
+		if ((sp->sec_ctx = calloc (1, len)) == NULL)
+		{
+			err = -NLE_NOMEM;
+			goto errout;
+		}
+		memcpy ((void *)sp->sec_ctx, (void *)ctx, len);
+		sp->ce_mask     |= XFRM_SP_ATTR_SECCTX;
+	}
+
+	if (tb[XFRMA_POLICY_TYPE]) {
+		struct xfrm_userpolicy_type* up = nla_data(tb[XFRMA_POLICY_TYPE]);
+		memcpy ((void *)&sp->uptype, (void *)up, sizeof (struct xfrm_userpolicy_type));
+		sp->ce_mask     |= XFRM_SP_ATTR_POLTYPE;
+	}
+
+	if (tb[XFRMA_TMPL]) {
+		struct xfrm_user_tmpl*      tmpl = nla_data(tb[XFRMA_TMPL]);
+		struct xfrmnl_user_tmpl*    sputmpl;
+		uint32_t                    i;
+		uint32_t                    num_tmpls = nla_len(tb[XFRMA_TMPL]) / sizeof (*tmpl);
+		struct  nl_addr*            addr;
+
+		for (i = 0; (i < num_tmpls) && (tmpl); i ++, tmpl++)
+		{
+			if ((sputmpl = xfrmnl_user_tmpl_alloc ()) == NULL)
+			{
+				err = -NLE_NOMEM;
+				goto errout;
+			}
+
+			if (tmpl->family == AF_INET)
+				addr = nl_addr_build(tmpl->family, &tmpl->id.daddr.a4, sizeof (tmpl->id.daddr.a4));
+			else
+				addr = nl_addr_build(tmpl->family, &tmpl->id.daddr.a6, sizeof (tmpl->id.daddr.a6));
+			xfrmnl_user_tmpl_set_daddr (sputmpl, addr);
+			xfrmnl_user_tmpl_set_spi (sputmpl, ntohl(tmpl->id.spi));
+			xfrmnl_user_tmpl_set_proto (sputmpl, tmpl->id.proto);
+			xfrmnl_user_tmpl_set_family (sputmpl, tmpl->family);
+
+			if (tmpl->family == AF_INET)
+				addr = nl_addr_build(tmpl->family, &tmpl->saddr.a4, sizeof (tmpl->saddr.a4));
+			else
+				addr = nl_addr_build(tmpl->family, &tmpl->saddr.a6, sizeof (tmpl->saddr.a6));
+			xfrmnl_user_tmpl_set_saddr (sputmpl, addr);
+
+			xfrmnl_user_tmpl_set_reqid (sputmpl, tmpl->reqid);
+			xfrmnl_user_tmpl_set_mode (sputmpl, tmpl->mode);
+			xfrmnl_user_tmpl_set_share (sputmpl, tmpl->share);
+			xfrmnl_user_tmpl_set_optional (sputmpl, tmpl->optional);
+			xfrmnl_user_tmpl_set_aalgos (sputmpl, tmpl->aalgos);
+			xfrmnl_user_tmpl_set_ealgos (sputmpl, tmpl->ealgos);
+			xfrmnl_user_tmpl_set_calgos (sputmpl, tmpl->calgos);
+			xfrmnl_sp_add_usertemplate (sp, sputmpl);
+
+			sp->ce_mask     |=  XFRM_SP_ATTR_TMPL;
+		}
+	}
+
+	if (tb[XFRMA_MARK]) {
+		struct xfrm_mark* m =   nla_data(tb[XFRMA_MARK]);
+		sp->mark.m  =   m->m;
+		sp->mark.v  =   m->v;
+		sp->ce_mask |= XFRM_SP_ATTR_MARK;
+	}
+
+	*result = sp;
+	return 0;
+
+errout:
+	xfrmnl_sp_put(sp);
+	return err;
+}
+
+static int xfrm_sp_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+				struct nlmsghdr *n, struct nl_parser_param *pp)
+{
+	struct xfrmnl_sp*   sp;
+	int                 err;
+
+	if ((err = xfrmnl_sp_parse(n, &sp)) < 0)
+	{
+		printf ("received error: %d \n", err);
+		return err;
+	}
+
+	err = pp->pp_cb((struct nl_object *) sp, pp);
+
+	xfrmnl_sp_put(sp);
+	return err;
+}
+
+/**
+ * @name XFRM SP Get
+ * @{
+ */
+
+int xfrmnl_sp_build_get_request(unsigned int index, unsigned int dir, unsigned int mark_v, unsigned int mark_m, struct nl_msg **result)
+{
+	struct nl_msg               *msg;
+	struct xfrm_userpolicy_id   spid;
+	struct xfrm_mark            mark;
+
+	memset(&spid, 0, sizeof(spid));
+	spid.index          = index;
+	spid.dir            = dir;
+
+	if (!(msg = nlmsg_alloc_simple(XFRM_MSG_GETPOLICY, 0)))
+		return -NLE_NOMEM;
+
+	if (nlmsg_append(msg, &spid, sizeof(spid), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	if ((mark_m & mark_v) != 0)
+	{
+		memset(&mark, 0, sizeof(struct xfrm_mark));
+		mark.m = mark_m;
+		mark.v = mark_v;
+
+		NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrm_mark), &mark);
+	}
+
+	*result = msg;
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+int xfrmnl_sp_get_kernel(struct nl_sock* sock, unsigned int index, unsigned int dir, unsigned int mark_v, unsigned int mark_m, struct xfrmnl_sp** result)
+{
+	struct nl_msg       *msg = NULL;
+	struct nl_object    *obj;
+	int err;
+
+	if ((err = xfrmnl_sp_build_get_request(index, dir, mark_m, mark_v, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto(sock, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	if ((err = nl_pickup(sock, &xfrm_sp_msg_parser, &obj)) < 0)
+		return err;
+
+	/* We have used xfrm_sp_msg_parser(), object is definitely a xfrm ae */
+	*result = (struct xfrmnl_sp *) obj;
+
+	/* If an object has been returned, we also need to wait for the ACK */
+	if (err == 0 && obj)
+		nl_wait_for_ack(sock);
+
+	return 0;
+}
+
+/** @} */
+
+static int build_xfrm_sp_message(struct xfrmnl_sp *tmpl, int cmd, int flags, struct nl_msg **result)
+{
+	struct nl_msg*              msg;
+	struct xfrm_userpolicy_info sp_info;
+	uint32_t                    len;
+	struct nl_addr*             addr;
+
+	if (!(tmpl->ce_mask & XFRM_SP_ATTR_DIR) ||
+			(!(tmpl->ce_mask & XFRM_SP_ATTR_INDEX) &&
+			 !(tmpl->ce_mask & XFRM_SP_ATTR_SEL)))
+		return -NLE_MISSING_ATTR;
+
+	memset ((void*)&sp_info, 0, sizeof (sp_info));
+	if (tmpl->ce_mask & XFRM_SP_ATTR_SEL)
+	{
+		addr = xfrmnl_sel_get_daddr (tmpl->sel);
+		memcpy ((void*)&sp_info.sel.daddr, (void*)nl_addr_get_binary_addr (addr), sizeof (uint8_t) * nl_addr_get_len (addr));
+		addr = xfrmnl_sel_get_saddr (tmpl->sel);
+		memcpy ((void*)&sp_info.sel.saddr, (void*)nl_addr_get_binary_addr (addr), sizeof (uint8_t) * nl_addr_get_len (addr));
+		sp_info.sel.dport       =   htons (xfrmnl_sel_get_dport (tmpl->sel));
+		sp_info.sel.dport_mask  =   htons (xfrmnl_sel_get_dportmask (tmpl->sel));
+		sp_info.sel.sport       =   htons (xfrmnl_sel_get_sport (tmpl->sel));
+		sp_info.sel.sport_mask  =   htons (xfrmnl_sel_get_sportmask (tmpl->sel));
+		sp_info.sel.family      =   xfrmnl_sel_get_family (tmpl->sel);
+		sp_info.sel.prefixlen_d =   xfrmnl_sel_get_prefixlen_d (tmpl->sel);
+		sp_info.sel.prefixlen_s =   xfrmnl_sel_get_prefixlen_s (tmpl->sel);
+		sp_info.sel.proto       =   xfrmnl_sel_get_proto (tmpl->sel);
+		sp_info.sel.ifindex     =   xfrmnl_sel_get_ifindex (tmpl->sel);
+		sp_info.sel.user        =   xfrmnl_sel_get_userid (tmpl->sel);
+	}
+
+	if (tmpl->ce_mask & XFRM_SP_ATTR_LTIME_CFG)
+	{
+		sp_info.lft.soft_byte_limit = xfrmnl_ltime_cfg_get_soft_bytelimit (tmpl->lft);
+		sp_info.lft.hard_byte_limit = xfrmnl_ltime_cfg_get_hard_bytelimit (tmpl->lft);
+		sp_info.lft.soft_packet_limit = xfrmnl_ltime_cfg_get_soft_packetlimit (tmpl->lft);
+		sp_info.lft.hard_packet_limit = xfrmnl_ltime_cfg_get_hard_packetlimit (tmpl->lft);
+		sp_info.lft.soft_add_expires_seconds = xfrmnl_ltime_cfg_get_soft_addexpires (tmpl->lft);
+		sp_info.lft.hard_add_expires_seconds = xfrmnl_ltime_cfg_get_hard_addexpires (tmpl->lft);
+		sp_info.lft.soft_use_expires_seconds = xfrmnl_ltime_cfg_get_soft_useexpires (tmpl->lft);
+		sp_info.lft.hard_use_expires_seconds = xfrmnl_ltime_cfg_get_hard_useexpires (tmpl->lft);
+	}
+
+	//Skip current lifetime: cur lifetime can be updated only via AE
+
+	if (tmpl->ce_mask & XFRM_SP_ATTR_PRIO)
+		sp_info.priority    = tmpl->priority;
+
+	if (tmpl->ce_mask & XFRM_SP_ATTR_INDEX)
+		sp_info.index       = tmpl->index;
+
+	if (tmpl->ce_mask & XFRM_SP_ATTR_DIR)
+		sp_info.dir         = tmpl->dir;
+
+	if (tmpl->ce_mask & XFRM_SP_ATTR_ACTION)
+		sp_info.action      = tmpl->action;
+
+	if (tmpl->ce_mask & XFRM_SP_ATTR_FLAGS)
+		sp_info.flags       = tmpl->flags;
+
+	if (tmpl->ce_mask & XFRM_SP_ATTR_SHARE)
+		sp_info.share       = tmpl->share;
+
+	msg = nlmsg_alloc_simple(cmd, flags);
+	if (!msg)
+		return -NLE_NOMEM;
+
+	if (nlmsg_append(msg, &sp_info, sizeof(sp_info), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	if (tmpl->ce_mask & XFRM_SP_ATTR_SECCTX) {
+		len = (sizeof (struct xfrm_user_sec_ctx)) + tmpl->sec_ctx->ctx_len;
+		NLA_PUT (msg, XFRMA_SEC_CTX, len, tmpl->sec_ctx);
+	}
+
+	if (tmpl->ce_mask & XFRM_SP_ATTR_POLTYPE) {
+		len = sizeof (struct xfrm_userpolicy_type);
+		NLA_PUT (msg, XFRMA_POLICY_TYPE, len, &tmpl->uptype);
+	}
+
+	if (tmpl->ce_mask & XFRM_SP_ATTR_TMPL) {
+		struct nlattr*              tmpls;
+		struct xfrmnl_user_tmpl*    utmpl;
+		struct nl_addr*             addr;
+
+		if (!(tmpls = nla_nest_start(msg, XFRMA_TMPL)))
+			goto nla_put_failure;
+
+		nl_list_for_each_entry(utmpl, &tmpl->usertmpl_list, utmpl_list) {
+			struct xfrm_user_tmpl*  tmpl;
+
+			tmpl = nlmsg_reserve(msg, sizeof(*tmpl), NLMSG_ALIGNTO);
+			if (!tmpl)
+				goto nla_put_failure;
+			addr = xfrmnl_user_tmpl_get_daddr (utmpl);
+			memcpy ((void *)&tmpl->id.daddr, nl_addr_get_binary_addr (addr),
+			         nl_addr_get_len (addr));
+			tmpl->id.spi    =   htonl(xfrmnl_user_tmpl_get_spi (utmpl));
+			tmpl->id.proto  =   xfrmnl_user_tmpl_get_proto (utmpl);
+			tmpl->family    =   xfrmnl_user_tmpl_get_family (utmpl);
+			addr = xfrmnl_user_tmpl_get_saddr (utmpl);
+			memcpy ((void *)&tmpl->saddr, nl_addr_get_binary_addr (addr),
+			        nl_addr_get_len (addr));
+			tmpl->reqid     =   xfrmnl_user_tmpl_get_reqid (utmpl);
+			tmpl->mode      =   xfrmnl_user_tmpl_get_mode (utmpl);
+			tmpl->share     =   xfrmnl_user_tmpl_get_share (utmpl);
+			tmpl->optional  =   xfrmnl_user_tmpl_get_optional (utmpl);
+			tmpl->aalgos    =   xfrmnl_user_tmpl_get_aalgos (utmpl);
+			tmpl->ealgos    =   xfrmnl_user_tmpl_get_ealgos (utmpl);
+			tmpl->calgos    =   xfrmnl_user_tmpl_get_calgos (utmpl);
+		}
+		nla_nest_end(msg, tmpls);
+	}
+
+	if (tmpl->ce_mask & XFRM_SP_ATTR_MARK) {
+		NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrm_mark), &tmpl->mark);
+	}
+
+	*result = msg;
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+/**
+ * @name XFRM SP Add
+ * @{
+ */
+
+int xfrmnl_sp_build_add_request(struct xfrmnl_sp* tmpl, int flags, struct nl_msg **result)
+{
+	return build_xfrm_sp_message (tmpl, XFRM_MSG_NEWPOLICY, flags, result);
+}
+
+int xfrmnl_sp_add(struct nl_sock* sk, struct xfrmnl_sp* tmpl, int flags)
+{
+	int             err;
+	struct nl_msg   *msg;
+
+	if ((err = xfrmnl_sp_build_add_request(tmpl, flags, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return nl_wait_for_ack(sk);
+}
+
+/**
+ * @name XFRM SP Update
+ * @{
+ */
+
+int xfrmnl_sp_build_update_request(struct xfrmnl_sp* tmpl, int flags, struct nl_msg **result)
+{
+	return build_xfrm_sp_message (tmpl, XFRM_MSG_UPDPOLICY, flags, result);
+}
+
+int xfrmnl_sp_update(struct nl_sock* sk, struct xfrmnl_sp* tmpl, int flags)
+{
+	int             err;
+	struct nl_msg   *msg;
+
+	if ((err = xfrmnl_sp_build_update_request(tmpl, flags, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return nl_wait_for_ack(sk);
+}
+
+/** @} */
+
+/**
+ * \brief      Builds a xfrm_sp_delete_message. Uses either index and direction
+ *             or security-context (not set is a valid value), selector and
+ *             direction for identification.
+ *             Returns error if necessary values aren't set.
+ *
+ * \param      tmpl    The policy template.
+ * \param      cmd     The command. Should be XFRM_MSG_DELPOLICY.
+ * \param      flags   Additional flags
+ * \param      result  Resulting message.
+ *
+ * \return     0 if successful, else error value < 0
+ */
+static int build_xfrm_sp_delete_message(struct xfrmnl_sp *tmpl, int cmd, int flags, struct nl_msg **result)
+{
+	struct nl_msg*              msg;
+	struct xfrm_userpolicy_id   spid;
+	struct nl_addr*             addr;
+	uint32_t 					len;
+
+	if (!(tmpl->ce_mask & XFRM_SP_ATTR_DIR) ||
+			(!(tmpl->ce_mask & XFRM_SP_ATTR_INDEX) &&
+			 !(tmpl->ce_mask & XFRM_SP_ATTR_SEL)))
+		return -NLE_MISSING_ATTR;
+
+	memset(&spid, 0, sizeof(spid));
+	spid.dir            = tmpl->dir;
+	if(tmpl->ce_mask & XFRM_SP_ATTR_INDEX)
+		spid.index          = tmpl->index;
+
+	if (tmpl->ce_mask & XFRM_SP_ATTR_SEL)
+	{
+		addr = xfrmnl_sel_get_daddr (tmpl->sel);
+		memcpy ((void*)&spid.sel.daddr, (void*)nl_addr_get_binary_addr (addr), sizeof (uint8_t) * nl_addr_get_len (addr));
+		addr = xfrmnl_sel_get_saddr (tmpl->sel);
+		memcpy ((void*)&spid.sel.saddr, (void*)nl_addr_get_binary_addr (addr), sizeof (uint8_t) * nl_addr_get_len (addr));
+		spid.sel.dport       =   htons (xfrmnl_sel_get_dport (tmpl->sel));
+		spid.sel.dport_mask  =   htons (xfrmnl_sel_get_dportmask (tmpl->sel));
+		spid.sel.sport       =   htons (xfrmnl_sel_get_sport (tmpl->sel));
+		spid.sel.sport_mask  =   htons (xfrmnl_sel_get_sportmask (tmpl->sel));
+		spid.sel.family      =   xfrmnl_sel_get_family (tmpl->sel);
+		spid.sel.prefixlen_d =   xfrmnl_sel_get_prefixlen_d (tmpl->sel);
+		spid.sel.prefixlen_s =   xfrmnl_sel_get_prefixlen_s (tmpl->sel);
+		spid.sel.proto       =   xfrmnl_sel_get_proto (tmpl->sel);
+		spid.sel.ifindex     =   xfrmnl_sel_get_ifindex (tmpl->sel);
+		spid.sel.user        =   xfrmnl_sel_get_userid (tmpl->sel);
+	}
+
+	msg = nlmsg_alloc_simple(cmd, flags);
+	if (!msg)
+		return -NLE_NOMEM;
+
+	if (nlmsg_append(msg, &spid, sizeof(spid), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	if (tmpl->ce_mask & XFRM_SP_ATTR_SECCTX) {
+		len = (sizeof (struct xfrm_user_sec_ctx)) + tmpl->sec_ctx->ctx_len;
+		NLA_PUT (msg, XFRMA_SEC_CTX, len, tmpl->sec_ctx);
+	}
+
+	if (tmpl->ce_mask & XFRM_SP_ATTR_MARK) {
+		len = sizeof (struct xfrm_mark);
+		NLA_PUT (msg, XFRMA_MARK, len, &tmpl->mark);
+	}
+
+	*result = msg;
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+/**
+ * @name XFRM SA Delete
+ * @{
+ */
+
+int xfrmnl_sp_build_delete_request(struct xfrmnl_sp* tmpl, int flags, struct nl_msg **result)
+{
+	return build_xfrm_sp_delete_message (tmpl, XFRM_MSG_DELPOLICY, flags, result);
+}
+
+int xfrmnl_sp_delete(struct nl_sock* sk, struct xfrmnl_sp* tmpl, int flags)
+{
+	int             err;
+	struct nl_msg   *msg;
+
+	if ((err = xfrmnl_sp_build_delete_request(tmpl, flags, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return nl_wait_for_ack(sk);
+}
+
+/** @} */
+
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+struct xfrmnl_sel* xfrmnl_sp_get_sel (struct xfrmnl_sp* sp)
+{
+	if (sp->ce_mask & XFRM_SP_ATTR_SEL)
+		return sp->sel;
+	else
+		return NULL;
+}
+
+int xfrmnl_sp_set_sel (struct xfrmnl_sp* sp, struct xfrmnl_sel* sel)
+{
+	/* Release any previously held selector object from the SP */
+	if (sp->sel)
+		xfrmnl_sel_put (sp->sel);
+
+	/* Increment ref count on new selector and save it in the SP */
+	xfrmnl_sel_get (sel);
+	sp->sel     =   sel;
+	sp->ce_mask |=  XFRM_SP_ATTR_SEL;
+
+	return 0;
+}
+
+struct xfrmnl_ltime_cfg* xfrmnl_sp_get_lifetime_cfg (struct xfrmnl_sp* sp)
+{
+	if (sp->ce_mask & XFRM_SP_ATTR_LTIME_CFG)
+		return sp->lft;
+	else
+		return NULL;
+}
+
+int xfrmnl_sp_set_lifetime_cfg (struct xfrmnl_sp* sp, struct xfrmnl_ltime_cfg* ltime)
+{
+	/* Release any previously held lifetime cfg object from the SP */
+	if (sp->lft)
+		xfrmnl_ltime_cfg_put (sp->lft);
+
+	/* Increment ref count on new lifetime object and save it in the SP */
+	xfrmnl_ltime_cfg_get (ltime);
+	sp->lft     =   ltime;
+	sp->ce_mask |=  XFRM_SP_ATTR_LTIME_CFG;
+
+	return 0;
+}
+
+int xfrmnl_sp_get_curlifetime (struct xfrmnl_sp* sa, unsigned long long int* curr_bytes,
+                               unsigned long long int* curr_packets, unsigned long long int* curr_add_time, unsigned long long int* curr_use_time)
+{
+	if (sa == NULL || curr_bytes == NULL || curr_packets == NULL || curr_add_time == NULL || curr_use_time == NULL)
+		return -1;
+
+	*curr_bytes     =   sa->curlft.bytes;
+	*curr_packets   =   sa->curlft.packets;
+	*curr_add_time  =   sa->curlft.add_time;
+	*curr_use_time  =   sa->curlft.use_time;
+
+	return 0;
+}
+
+int xfrmnl_sp_get_priority (struct xfrmnl_sp* sp)
+{
+	if (sp->ce_mask & XFRM_SP_ATTR_PRIO)
+		return sp->priority;
+	else
+		return -1;
+}
+
+int xfrmnl_sp_set_priority (struct xfrmnl_sp* sp, unsigned int prio)
+{
+	sp->priority    = prio;
+	sp->ce_mask     |= XFRM_SP_ATTR_PRIO;
+
+	return 0;
+}
+
+int xfrmnl_sp_get_index (struct xfrmnl_sp* sp)
+{
+	if (sp->ce_mask & XFRM_SP_ATTR_INDEX)
+		return sp->index;
+	else
+		return -1;
+}
+
+int xfrmnl_sp_set_index (struct xfrmnl_sp* sp, unsigned int index)
+{
+	sp->index       = index;
+	sp->ce_mask     |= XFRM_SP_ATTR_INDEX;
+
+	return 0;
+}
+
+int xfrmnl_sp_get_dir (struct xfrmnl_sp* sp)
+{
+	if (sp->ce_mask & XFRM_SP_ATTR_DIR)
+		return sp->dir;
+	else
+		return -1;
+}
+
+int xfrmnl_sp_set_dir (struct xfrmnl_sp* sp, unsigned int dir)
+{
+	sp->dir         = dir;
+	sp->ce_mask     |= XFRM_SP_ATTR_DIR;
+
+	return 0;
+}
+
+int xfrmnl_sp_get_action (struct xfrmnl_sp* sp)
+{
+	if (sp->ce_mask & XFRM_SP_ATTR_ACTION)
+		return sp->action;
+	else
+		return -1;
+}
+
+int xfrmnl_sp_set_action (struct xfrmnl_sp* sp, unsigned int action)
+{
+	sp->action      = action;
+	sp->ce_mask     |= XFRM_SP_ATTR_ACTION;
+
+	return 0;
+}
+
+int xfrmnl_sp_get_flags (struct xfrmnl_sp* sp)
+{
+	if (sp->ce_mask & XFRM_SP_ATTR_FLAGS)
+		return sp->flags;
+	else
+		return -1;
+}
+
+int xfrmnl_sp_set_flags (struct xfrmnl_sp* sp, unsigned int flags)
+{
+	sp->flags       = flags;
+	sp->ce_mask     |= XFRM_SP_ATTR_FLAGS;
+
+	return 0;
+}
+
+int xfrmnl_sp_get_share (struct xfrmnl_sp* sp)
+{
+	if (sp->ce_mask & XFRM_SP_ATTR_SHARE)
+		return sp->share;
+	else
+		return -1;
+}
+
+int xfrmnl_sp_set_share (struct xfrmnl_sp* sp, unsigned int share)
+{
+	sp->share       = share;
+	sp->ce_mask     |= XFRM_SP_ATTR_SHARE;
+
+	return 0;
+}
+
+/**
+ * Get the security context.
+ *
+ * @arg sp              The xfrmnl_sp object.
+ * @arg len             An optional output value for the ctx_str length including the xfrmnl_sp header.
+ * @arg exttype         An optional output value.
+ * @arg alg             An optional output value for the security context algorithm.
+ * @arg doi             An optional output value for the security context domain of interpretation.
+ * @arg ctx_len         An optional output value for the security context length, including the
+ *                      terminating null byte ('\0').
+ * @arg ctx_str         An optional buffer large enough for the security context string. It must
+ *                      contain at least @ctx_len bytes. You are advised to create the ctx_str
+ *                      buffer one element larger and ensure NUL termination yourself.
+ *
+ * Warning: you must ensure that @ctx_str is large enough. If you don't know the length before-hand,
+ * call xfrmnl_sp_get_sec_ctx() without @ctx_str argument to query only the required buffer size.
+ * This modified API is available in all versions of libnl3 that support the capability
+ * @def NL_CAPABILITY_XFRM_SP_SEC_CTX_LEN (@see nl_has_capability for further information).
+ *
+ * @return 0 on success or a negative error code.
+ */
+int xfrmnl_sp_get_sec_ctx (struct xfrmnl_sp* sp, unsigned int* len, unsigned int* exttype, unsigned int* alg, unsigned int* doi, unsigned int* ctx_len, char* ctx_str)
+{
+	if (sp->ce_mask & XFRM_SP_ATTR_SECCTX)
+	{
+		if (len)
+			*len = sizeof (struct xfrmnl_user_sec_ctx) + sp->sec_ctx->ctx_len;
+		if (exttype)
+			*exttype = sp->sec_ctx->exttype;
+		if (alg)
+			*alg = sp->sec_ctx->ctx_alg;
+		if (doi)
+			*doi = sp->sec_ctx->ctx_doi;
+		if (ctx_len)
+			*ctx_len = sp->sec_ctx->ctx_len;
+		if (ctx_str)
+			memcpy ((void *)ctx_str, (void *)sp->sec_ctx->ctx, sp->sec_ctx->ctx_len);
+	}
+	else
+		return -1;
+
+	return 0;
+}
+/**
+ * @brief      Set security context (ctx_str) for XFRM Polixy.
+ *
+ * @param      sp       XFRM Policy
+ * @param      len      !!! depricated unused parameter !!!
+ * @param      exttype  netlink message attribute - probably XFRMA_SEC_CTX
+ * @param      alg      security context algorithm
+ * @param      doi      security context domain interpretation
+ * @param      ctx_len  Length of the context string.
+ * @param      ctx_str  The context string.
+ *
+ * @return     0 if sucessfull, else -1
+ */
+int xfrmnl_sp_set_sec_ctx (struct xfrmnl_sp* sp, unsigned int len __attribute__((unused)), unsigned int exttype, unsigned int alg, unsigned int doi, unsigned int ctx_len, char* ctx_str)
+{
+	/* Free up the old context string and allocate new one */
+	if (sp->sec_ctx)
+		free (sp->sec_ctx);
+	if ((sp->sec_ctx = calloc (1, sizeof (struct xfrmnl_user_sec_ctx) + 1 + ctx_len)) == NULL)
+		return -1;
+
+	/* Save the new info */
+	sp->sec_ctx->len        =   sizeof (struct xfrmnl_user_sec_ctx) + ctx_len;
+	sp->sec_ctx->exttype    =   exttype;
+	sp->sec_ctx->ctx_alg    =   alg;
+	sp->sec_ctx->ctx_doi    =   doi;
+	sp->sec_ctx->ctx_len    =   ctx_len;
+	memcpy ((void *)sp->sec_ctx->ctx, (void *)ctx_str, ctx_len);
+	sp->sec_ctx->ctx[ctx_len] = '\0';
+
+	sp->ce_mask |= XFRM_SP_ATTR_SECCTX;
+
+	return 0;
+}
+
+int xfrmnl_sp_get_userpolicy_type (struct xfrmnl_sp* sp)
+{
+	if (sp->ce_mask & XFRM_SP_ATTR_POLTYPE)
+		return sp->uptype.type;
+	else
+		return -1;
+}
+
+int xfrmnl_sp_set_userpolicy_type (struct xfrmnl_sp* sp, unsigned int type)
+{
+	sp->uptype.type = type;
+	sp->ce_mask     |= XFRM_SP_ATTR_POLTYPE;
+
+	return 0;
+}
+
+void xfrmnl_sp_add_usertemplate(struct xfrmnl_sp *sp, struct xfrmnl_user_tmpl *utmpl)
+{
+	nl_list_add_tail(&utmpl->utmpl_list, &sp->usertmpl_list);
+	sp->nr_user_tmpl++;
+	sp->ce_mask |= XFRM_SP_ATTR_TMPL;
+}
+
+void xfrmnl_sp_remove_usertemplate(struct xfrmnl_sp *sp, struct xfrmnl_user_tmpl *utmpl)
+{
+	if (sp->ce_mask & XFRM_SP_ATTR_TMPL) {
+		sp->nr_user_tmpl--;
+		nl_list_del(&utmpl->utmpl_list);
+	}
+}
+
+struct nl_list_head *xfrmnl_sp_get_usertemplates(struct xfrmnl_sp *sp)
+{
+	if (sp->ce_mask & XFRM_SP_ATTR_TMPL)
+		return &sp->usertmpl_list;
+
+	return NULL;
+}
+
+int xfrmnl_sp_get_nusertemplates(struct xfrmnl_sp *sp)
+{
+	if (sp->ce_mask & XFRM_SP_ATTR_TMPL)
+		return sp->nr_user_tmpl;
+
+	return 0;
+}
+
+void xfrmnl_sp_foreach_usertemplate(struct xfrmnl_sp *r,
+                                    void (*cb)(struct xfrmnl_user_tmpl *, void *),
+                                    void *arg)
+{
+	struct xfrmnl_user_tmpl *utmpl;
+
+	if (r->ce_mask & XFRM_SP_ATTR_TMPL) {
+		nl_list_for_each_entry(utmpl, &r->usertmpl_list, utmpl_list) {
+			cb(utmpl, arg);
+		}
+	}
+}
+
+struct xfrmnl_user_tmpl *xfrmnl_sp_usertemplate_n(struct xfrmnl_sp *r, int n)
+{
+	struct xfrmnl_user_tmpl *utmpl;
+	uint32_t i;
+
+	if (r->ce_mask & XFRM_SP_ATTR_TMPL && r->nr_user_tmpl > n) {
+		i = 0;
+		nl_list_for_each_entry(utmpl, &r->usertmpl_list, utmpl_list) {
+			if (i == n) return utmpl;
+			i++;
+		}
+	}
+		return NULL;
+}
+
+int xfrmnl_sp_get_mark (struct xfrmnl_sp* sp, unsigned int* mark_mask, unsigned int* mark_value)
+{
+	if (mark_mask == NULL || mark_value == NULL)
+		return -1;
+
+	if (sp->ce_mask & XFRM_SP_ATTR_MARK)
+	{
+		*mark_mask  =   sp->mark.m;
+		*mark_value  =   sp->mark.v;
+
+		return 0;
+	}
+	else
+		return -1;
+}
+
+int xfrmnl_sp_set_mark (struct xfrmnl_sp* sp, unsigned int value, unsigned int mask)
+{
+	sp->mark.v  = value;
+	sp->mark.m  = mask;
+	sp->ce_mask |= XFRM_SP_ATTR_MARK;
+
+	return 0;
+}
+
+/** @} */
+
+static struct nl_object_ops xfrm_sp_obj_ops = {
+	.oo_name        =   "xfrm/sp",
+	.oo_size        =   sizeof(struct xfrmnl_sp),
+	.oo_constructor =   xfrm_sp_alloc_data,
+	.oo_free_data   =   xfrm_sp_free_data,
+	.oo_clone       =   xfrm_sp_clone,
+	.oo_dump        =   {
+	                        [NL_DUMP_LINE]      =   xfrm_sp_dump_line,
+	                        [NL_DUMP_DETAILS]   =   xfrm_sp_dump_details,
+	                        [NL_DUMP_STATS]     =   xfrm_sp_dump_stats,
+	                    },
+	.oo_compare     =   xfrm_sp_compare,
+	.oo_attrs2str   =   xfrm_sp_attrs2str,
+	.oo_id_attrs    =   (XFRM_SP_ATTR_SEL | XFRM_SP_ATTR_INDEX | XFRM_SP_ATTR_DIR),
+};
+
+static struct nl_af_group xfrm_sp_groups[] = {
+	{ AF_UNSPEC, XFRMNLGRP_POLICY },
+	{ END_OF_GROUP_LIST },
+};
+
+static struct nl_cache_ops xfrmnl_sp_ops = {
+	.co_name            = "xfrm/sp",
+	.co_hdrsize         = sizeof(struct xfrm_userpolicy_info),
+	.co_msgtypes        = {
+	                        { XFRM_MSG_NEWPOLICY, NL_ACT_NEW, "new" },
+	                        { XFRM_MSG_DELPOLICY, NL_ACT_DEL, "del" },
+	                        { XFRM_MSG_GETPOLICY, NL_ACT_GET, "get" },
+	                        { XFRM_MSG_UPDPOLICY, NL_ACT_NEW, "update" },
+	                        END_OF_MSGTYPES_LIST,
+	                     },
+	.co_protocol        = NETLINK_XFRM,
+	.co_groups          = xfrm_sp_groups,
+	.co_request_update  = xfrm_sp_request_update,
+	.co_msg_parser      = xfrm_sp_msg_parser,
+	.co_obj_ops         = &xfrm_sp_obj_ops,
+};
+
+/**
+ * @name XFRM SA Cache Managament
+ * @{
+ */
+
+static void __attribute__ ((constructor)) xfrm_sp_init(void)
+{
+	nl_cache_mngt_register(&xfrmnl_sp_ops);
+}
+
+static void __attribute__ ((destructor)) xfrm_sp_exit(void)
+{
+	nl_cache_mngt_unregister(&xfrmnl_sp_ops);
+}
+
+/** @} */
diff --git a/lib/xfrm/template.c b/lib/xfrm/template.c
new file mode 100644
index 0000000..0561869
--- /dev/null
+++ b/lib/xfrm/template.c
@@ -0,0 +1,341 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *    Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *    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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  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.
+ *
+ */
+/**
+ * @ingroup xfrmnl
+ * @defgroup XFRM User Template Object
+ *
+ * Abstract data type representing XFRM SA properties
+ *
+ * @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/xfrm/template.h>
+ * ~~~~
+ */
+
+#include <netlink/xfrm/template.h>
+#include <netlink-private/netlink.h>
+
+void xfrmnl_user_tmpl_free(struct xfrmnl_user_tmpl* utmpl)
+{
+	if (!utmpl)
+		return;
+
+	nl_addr_put (utmpl->id.daddr);
+	nl_addr_put (utmpl->saddr);
+	free(utmpl);
+}
+
+/**
+ * @name Creating User Template Object
+ * @{
+ */
+
+/**
+ * Allocate new user template object.
+ * @return Newly allocated user template object or NULL
+ */
+struct xfrmnl_user_tmpl* xfrmnl_user_tmpl_alloc()
+{
+	struct xfrmnl_user_tmpl* utmpl;
+
+	utmpl = calloc(1, sizeof(struct xfrmnl_user_tmpl));
+	if (!utmpl)
+		return NULL;
+
+	nl_init_list_head(&utmpl->utmpl_list);
+
+	return utmpl;
+}
+
+/**
+ * Clone existing user template object.
+ * @arg utmpl		Selector object.
+ * @return Newly allocated user template object being a duplicate of the
+ *         specified user template object or NULL if a failure occured.
+ */
+struct xfrmnl_user_tmpl* xfrmnl_user_tmpl_clone(struct xfrmnl_user_tmpl* utmpl)
+{
+	struct xfrmnl_user_tmpl* new;
+
+	new = xfrmnl_user_tmpl_alloc();
+	if (!new)
+		return NULL;
+
+	memcpy(new, utmpl, sizeof(struct xfrmnl_user_tmpl));
+	new->id.daddr = nl_addr_clone (utmpl->id.daddr);
+	new->saddr    = nl_addr_clone (utmpl->saddr);
+
+	return new;
+}
+
+/** @} */
+
+/**
+ * @name XFRM Template Mode Translations
+ * @{
+ */
+static const struct trans_tbl tmpl_modes[] = {
+	__ADD(XFRM_MODE_TRANSPORT, transport),
+	__ADD(XFRM_MODE_TUNNEL, tunnel),
+	__ADD(XFRM_MODE_ROUTEOPTIMIZATION, route optimization),
+	__ADD(XFRM_MODE_IN_TRIGGER, in trigger),
+	__ADD(XFRM_MODE_BEET, beet),
+};
+
+char* xfrmnl_user_tmpl_mode2str(int mode, char *buf, size_t len)
+{
+	return __type2str (mode, buf, len, tmpl_modes, ARRAY_SIZE(tmpl_modes));
+}
+
+int xfrmnl_user_tmpl_str2mode(const char *name)
+{
+	return __str2type (name, tmpl_modes, ARRAY_SIZE(tmpl_modes));
+}
+/** @} */
+
+/**
+ * @name Miscellaneous
+ * @{
+ */
+
+/**
+ * Compares two user template objects.
+ * @arg a		A user template object.
+ * @arg b		Another user template object.
+ *
+ * @return Non zero if difference is found, 0 otherwise if both
+ * the objects are identical.
+ */
+int xfrmnl_user_tmpl_cmp(struct xfrmnl_user_tmpl* a, struct xfrmnl_user_tmpl* b)
+{
+	/* Check for any differences */
+	if ((nl_addr_cmp_prefix (a->id.daddr, b->id.daddr) != 0) ||
+	    (a->id.spi != b->id.spi) ||
+	    (a->id.proto && (a->id.proto != b->id.proto)) ||
+	    (nl_addr_cmp_prefix (a->saddr, b->saddr) != 0) ||
+	    (a->family != b->family) ||
+	    (a->reqid != b->reqid) ||
+	    (a->mode != b->mode) ||
+	    (a->share != b->share) ||
+	    (a->aalgos != b->aalgos) ||
+	    (a->ealgos != b->ealgos) ||
+	    (a->calgos != b->calgos))
+		return 1;
+
+	/* The objects are identical */
+	return 0;
+}
+
+void xfrmnl_user_tmpl_dump(struct xfrmnl_user_tmpl* tmpl, struct nl_dump_params *p)
+{
+	char    dst[INET6_ADDRSTRLEN+5], src[INET6_ADDRSTRLEN+5];
+	char    buf [128];
+
+	nl_dump_line(p, "\t\tsrc %s dst %s family: %s \n",
+	             nl_addr2str(tmpl->saddr, src, sizeof(src)),
+	             nl_addr2str (tmpl->id.daddr, dst, sizeof (dst)),
+	             nl_af2str (tmpl->family, buf, 128));
+	nl_dump_line (p, "\t\tprotocol: %s spi: 0x%x reqid: %u mode: %s\n",
+	              nl_ip_proto2str (tmpl->id.proto, buf, sizeof(buf)),
+	              tmpl->id.spi, tmpl->reqid,
+	              xfrmnl_user_tmpl_mode2str (tmpl->mode, buf, 128));
+	nl_dump_line (p, "\t\tAuth Algo: 0x%x Crypto Algo: 0x%x Compr Algo: 0x%x\n",
+	              tmpl->aalgos, tmpl->ealgos, tmpl->calgos);
+
+	return;
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+struct nl_addr* xfrmnl_user_tmpl_get_daddr (struct xfrmnl_user_tmpl* utmpl)
+{
+	return utmpl->id.daddr;
+}
+
+int xfrmnl_user_tmpl_set_daddr (struct xfrmnl_user_tmpl* utmpl, struct nl_addr* addr)
+{
+	/* Increment reference counter on this to keep this address
+	 * object around while user template in use */
+	nl_addr_get(addr);
+
+	utmpl->id.daddr = addr;
+
+	return 0;
+}
+
+int xfrmnl_user_tmpl_get_spi (struct xfrmnl_user_tmpl* utmpl)
+{
+	return utmpl->id.spi;
+}
+
+int xfrmnl_user_tmpl_set_spi (struct xfrmnl_user_tmpl* utmpl, unsigned int spi)
+{
+	utmpl->id.spi = spi;
+
+	return 0;
+}
+
+int xfrmnl_user_tmpl_get_proto (struct xfrmnl_user_tmpl* utmpl)
+{
+	return utmpl->id.proto;
+}
+
+int xfrmnl_user_tmpl_set_proto (struct xfrmnl_user_tmpl* utmpl, unsigned int protocol)
+{
+	utmpl->id.proto = protocol;
+
+	return 0;
+}
+
+int xfrmnl_user_tmpl_get_family(struct xfrmnl_user_tmpl *utmpl)
+{
+	return utmpl->family;
+}
+
+int xfrmnl_user_tmpl_set_family(struct xfrmnl_user_tmpl *utmpl, unsigned int family)
+{
+	utmpl->family = family;
+
+	return 0;
+}
+
+struct nl_addr* xfrmnl_user_tmpl_get_saddr (struct xfrmnl_user_tmpl* utmpl)
+{
+	return utmpl->saddr;
+}
+
+int xfrmnl_user_tmpl_set_saddr (struct xfrmnl_user_tmpl* utmpl, struct nl_addr* addr)
+{
+	/* Increment reference counter on this to keep this address
+	 * object around while user template in use */
+	nl_addr_get(addr);
+
+	utmpl->saddr = addr;
+
+	return 0;
+}
+
+int xfrmnl_user_tmpl_get_reqid (struct xfrmnl_user_tmpl* utmpl)
+{
+	return utmpl->reqid;
+}
+
+int xfrmnl_user_tmpl_set_reqid (struct xfrmnl_user_tmpl* utmpl, unsigned int reqid)
+{
+	utmpl->reqid = reqid;
+
+	return 0;
+}
+
+int xfrmnl_user_tmpl_get_mode (struct xfrmnl_user_tmpl* utmpl)
+{
+	return utmpl->mode;
+}
+
+int xfrmnl_user_tmpl_set_mode (struct xfrmnl_user_tmpl* utmpl, unsigned int mode)
+{
+	utmpl->mode = mode;
+
+	return 0;
+}
+
+int xfrmnl_user_tmpl_get_share (struct xfrmnl_user_tmpl* utmpl)
+{
+	return utmpl->share;
+}
+
+int xfrmnl_user_tmpl_set_share (struct xfrmnl_user_tmpl* utmpl, unsigned int share)
+{
+	utmpl->share = share;
+
+	return 0;
+}
+
+int xfrmnl_user_tmpl_get_optional (struct xfrmnl_user_tmpl* utmpl)
+{
+	return utmpl->optional;
+}
+
+int xfrmnl_user_tmpl_set_optional (struct xfrmnl_user_tmpl* utmpl, unsigned int optional)
+{
+	utmpl->optional = optional;
+
+	return 0;
+}
+
+int xfrmnl_user_tmpl_get_aalgos (struct xfrmnl_user_tmpl* utmpl)
+{
+	return utmpl->aalgos;
+}
+
+int xfrmnl_user_tmpl_set_aalgos (struct xfrmnl_user_tmpl* utmpl, unsigned int aalgos)
+{
+	utmpl->aalgos = aalgos;
+
+	return 0;
+}
+
+int xfrmnl_user_tmpl_get_ealgos (struct xfrmnl_user_tmpl* utmpl)
+{
+	return utmpl->ealgos;
+}
+
+int xfrmnl_user_tmpl_set_ealgos (struct xfrmnl_user_tmpl* utmpl, unsigned int ealgos)
+{
+	utmpl->ealgos = ealgos;
+
+	return 0;
+}
+
+int xfrmnl_user_tmpl_get_calgos (struct xfrmnl_user_tmpl* utmpl)
+{
+	return utmpl->calgos;
+}
+
+int xfrmnl_user_tmpl_set_calgos (struct xfrmnl_user_tmpl* utmpl, unsigned int calgos)
+{
+	utmpl->calgos = calgos;
+
+	return 0;
+}
+
+/** @} */
diff --git a/libnl-3.sym b/libnl-3.sym
new file mode 100644
index 0000000..82d2f07
--- /dev/null
+++ b/libnl-3.sym
@@ -0,0 +1,365 @@
+libnl_3 {
+global:
+	# these functions are in private header files and should have never
+	# been exported. We might hide them later.
+	nl_cache_parse;
+
+	# these functions are in private header files and should have never
+	# been exported. They are used by libnl internals
+	__flags2str;
+	__list_str2type;
+	__list_type2str;
+	__nl_read_num_str_file;
+	__str2flags;
+	__str2type;
+	__trans_list_add;
+	__trans_list_clear;
+	__type2str;
+
+	# internal symbols that are in public headers
+	__nl_cache_mngt_require;
+
+	# variables
+	nl_debug;
+	nl_debug_dp;
+
+	nl_addr2str;
+	nl_addr_alloc;
+	nl_addr_alloc_attr;
+	nl_addr_build;
+	nl_addr_clone;
+	nl_addr_cmp;
+	nl_addr_cmp_prefix;
+	nl_addr_fill_sockaddr;
+	nl_addr_get;
+	nl_addr_get_binary_addr;
+	nl_addr_get_family;
+	nl_addr_get_len;
+	nl_addr_get_prefixlen;
+	nl_addr_guess_family;
+	nl_addr_info;
+	nl_addr_iszero;
+	nl_addr_parse;
+	nl_addr_put;
+	nl_addr_resolve;
+	nl_addr_set_binary_addr;
+	nl_addr_set_family;
+	nl_addr_set_prefixlen;
+	nl_addr_shared;
+	nl_addr_valid;
+	nl_af2str;
+	nl_auto_complete;
+	nl_cache_add;
+	nl_cache_alloc;
+	nl_cache_alloc_and_fill;
+	nl_cache_alloc_name;
+	nl_cache_clear;
+	nl_cache_clone;
+	nl_cache_dump;
+	nl_cache_dump_filter;
+	nl_cache_find;
+	nl_cache_foreach;
+	nl_cache_foreach_filter;
+	nl_cache_free;
+	nl_cache_get;
+	nl_cache_get_first;
+	nl_cache_get_last;
+	nl_cache_get_next;
+	nl_cache_get_ops;
+	nl_cache_get_prev;
+	nl_cache_include;
+	nl_cache_is_empty;
+	nl_cache_mark_all;
+	nl_cache_mngr_add;
+	nl_cache_mngr_add_cache;
+	nl_cache_mngr_alloc;
+	nl_cache_mngr_data_ready;
+	nl_cache_mngr_free;
+	nl_cache_mngr_get_fd;
+	nl_cache_mngr_info;
+	nl_cache_mngr_poll;
+	nl_cache_mngt_provide;
+	nl_cache_mngt_register;
+	nl_cache_mngt_require;
+	nl_cache_mngt_require_safe;
+	nl_cache_mngt_unprovide;
+	nl_cache_mngt_unregister;
+	nl_cache_move;
+	nl_cache_nitems;
+	nl_cache_nitems_filter;
+	nl_cache_ops_associate;
+	nl_cache_ops_associate_safe;
+	nl_cache_ops_foreach;
+	nl_cache_ops_get;
+	nl_cache_ops_lookup;
+	nl_cache_ops_lookup_safe;
+	nl_cache_ops_put;
+	nl_cache_ops_set_flags;
+	nl_cache_parse_and_add;
+	nl_cache_pickup;
+	nl_cache_put;
+	nl_cache_refill;
+	nl_cache_remove;
+	nl_cache_resync;
+	nl_cache_search;
+	nl_cache_set_arg1;
+	nl_cache_set_arg2;
+	nl_cache_set_flags;
+	nl_cache_subset;
+	nl_cancel_down_bits;
+	nl_cancel_down_bytes;
+	nl_cancel_down_us;
+	nl_cb_active_type;
+	nl_cb_alloc;
+	nl_cb_clone;
+	nl_cb_err;
+	nl_cb_get;
+	nl_cb_overwrite_recv;
+	nl_cb_overwrite_recvmsgs;
+	nl_cb_overwrite_send;
+	nl_cb_put;
+	nl_cb_set;
+	nl_cb_set_all;
+	nl_close;
+	nl_complete_msg;
+	nl_connect;
+	nl_data_alloc;
+	nl_data_alloc_attr;
+	nl_data_append;
+	nl_data_clone;
+	nl_data_cmp;
+	nl_data_free;
+	nl_data_get;
+	nl_data_get_size;
+	nl_dump;
+	nl_dump_line;
+	nl_ether_proto2str;
+	nl_get_psched_hz;
+	nl_get_user_hz;
+	nl_geterror;
+	nl_has_capability;
+	nl_hash;
+	nl_hash_any;
+	nl_hash_table_add;
+	nl_hash_table_alloc;
+	nl_hash_table_del;
+	nl_hash_table_free;
+	nl_hash_table_lookup;
+	nl_ip_proto2str;
+	nl_join_groups;
+	nl_llproto2str;
+	nl_msec2str;
+	nl_msg_dump;
+	nl_msg_parse;
+	nl_msgtype_lookup;
+	nl_new_line;
+	nl_nlfamily2str;
+	nl_nlmsg_flags2str;
+	nl_nlmsgtype2str;
+	nl_object_alloc;
+	nl_object_alloc_name;
+	nl_object_attr_list;
+	nl_object_attrs2str;
+	nl_object_clone;
+	nl_object_diff;
+	nl_object_dump;
+	nl_object_dump_buf;
+	nl_object_free;
+	nl_object_get;
+	nl_object_get_cache;
+	nl_object_get_id_attrs;
+	nl_object_get_msgtype;
+	nl_object_get_ops;
+	nl_object_get_refcnt;
+	nl_object_get_type;
+	nl_object_identical;
+	nl_object_is_marked;
+	nl_object_keygen;
+	nl_object_mark;
+	nl_object_match_filter;
+	nl_object_put;
+	nl_object_shared;
+	nl_object_unmark;
+	nl_object_update;
+	nl_perror;
+	nl_pickup;
+	nl_prob2int;
+	nl_rate2str;
+	nl_recv;
+	nl_recvmsgs;
+	nl_recvmsgs_default;
+	nl_recvmsgs_report;
+	nl_send;
+	nl_send_auto;
+	nl_send_auto_complete;
+	nl_send_iovec;
+	nl_send_simple;
+	nl_send_sync;
+	nl_sendmsg;
+	nl_sendto;
+	nl_size2int;
+	nl_size2str;
+	nl_socket_add_membership;
+	nl_socket_add_memberships;
+	nl_socket_alloc;
+	nl_socket_alloc_cb;
+	nl_socket_disable_auto_ack;
+	nl_socket_disable_msg_peek;
+	nl_socket_disable_seq_check;
+	nl_socket_drop_membership;
+	nl_socket_drop_memberships;
+	nl_socket_enable_auto_ack;
+	nl_socket_enable_msg_peek;
+	nl_socket_free;
+	nl_socket_get_cb;
+	nl_socket_get_fd;
+	nl_socket_get_local_port;
+	nl_socket_get_msg_buf_size;
+	nl_socket_get_peer_groups;
+	nl_socket_get_peer_port;
+	nl_socket_modify_cb;
+	nl_socket_modify_err_cb;
+	nl_socket_recv_pktinfo;
+	nl_socket_set_buffer_size;
+	nl_socket_set_cb;
+	nl_socket_set_local_port;
+	nl_socket_set_msg_buf_size;
+	nl_socket_set_nonblocking;
+	nl_socket_set_passcred;
+	nl_socket_set_peer_groups;
+	nl_socket_set_peer_port;
+	nl_socket_use_seq;
+	nl_str2af;
+	nl_str2ether_proto;
+	nl_str2ip_proto;
+	nl_str2llproto;
+	nl_str2msec;
+	nl_str2nlfamily;
+	nl_str2nlmsgtype;
+	nl_syserr2nlerr;
+	nl_ticks2us;
+	nl_us2ticks;
+	nl_ver_maj;
+	nl_ver_mic;
+	nl_ver_min;
+	nl_ver_num;
+	nl_wait_for_ack;
+	nla_attr_size;
+	nla_data;
+	nla_find;
+	nla_get_flag;
+	nla_get_msecs;
+	nla_get_string;
+	nla_get_u16;
+	nla_get_u32;
+	nla_get_u64;
+	nla_get_u8;
+	nla_is_nested;
+	nla_len;
+	nla_memcmp;
+	nla_memcpy;
+	nla_nest_cancel;
+	nla_nest_end;
+	nla_nest_start;
+	nla_next;
+	nla_ok;
+	nla_padlen;
+	nla_parse;
+	nla_parse_nested;
+	nla_put;
+	nla_put_addr;
+	nla_put_data;
+	nla_put_flag;
+	nla_put_msecs;
+	nla_put_nested;
+	nla_put_string;
+	nla_put_u16;
+	nla_put_u32;
+	nla_put_u64;
+	nla_put_u8;
+	nla_reserve;
+	nla_strcmp;
+	nla_strdup;
+	nla_strlcpy;
+	nla_total_size;
+	nla_type;
+	nla_validate;
+	nlmsg_alloc;
+	nlmsg_alloc_simple;
+	nlmsg_alloc_size;
+	nlmsg_append;
+	nlmsg_attrdata;
+	nlmsg_attrlen;
+	nlmsg_convert;
+	nlmsg_data;
+	nlmsg_datalen;
+	nlmsg_expand;
+	nlmsg_find_attr;
+	nlmsg_free;
+	nlmsg_get;
+	nlmsg_get_creds;
+	nlmsg_get_dst;
+	nlmsg_get_max_size;
+	nlmsg_get_proto;
+	nlmsg_get_src;
+	nlmsg_hdr;
+	nlmsg_inherit;
+	nlmsg_next;
+	nlmsg_ok;
+	nlmsg_padlen;
+	nlmsg_parse;
+	nlmsg_put;
+	nlmsg_reserve;
+	nlmsg_set_creds;
+	nlmsg_set_default_size;
+	nlmsg_set_dst;
+	nlmsg_set_proto;
+	nlmsg_set_src;
+	nlmsg_size;
+	nlmsg_tail;
+	nlmsg_total_size;
+	nlmsg_valid_hdr;
+	nlmsg_validate;
+
+	# The following symbols were added during the development of 3.2.26.
+	# Keep them in libnl_3 to avoid breaking users.
+	nl_cache_pickup_checkdup;
+	nl_pickup_keep_syserr;
+
+local:
+	*;
+};
+
+libnl_3_2_26 {
+global:
+	nl_socket_set_fd;
+} libnl_3;
+
+libnl_3_2_27 {
+global:
+	nla_get_s8;
+	nla_put_s8;
+	nla_get_s16;
+	nla_put_s16;
+	nla_get_s32;
+	nla_put_s32;
+	nla_get_s64;
+	nla_put_s64;
+} libnl_3_2_26;
+
+libnl_3_2_28 {
+global:
+	nl_object_diff64;
+} libnl_3_2_27;
+
+libnl_3_2_29 {
+global:
+	nl_cache_include_v2;
+	nl_cache_mngr_add_cache_v2;
+	nl_strerror_l;
+} libnl_3_2_28;
+
+libnl_3_5 {
+global:
+	nla_nest_end_keep_empty;
+} libnl_3_2_29;
diff --git a/libnl-cli-3.sym b/libnl-cli-3.sym
new file mode 100644
index 0000000..71ff2eb
--- /dev/null
+++ b/libnl-cli-3.sym
@@ -0,0 +1,117 @@
+libnl_3 {
+global:
+	nl_cli_addr_alloc;
+	nl_cli_addr_parse;
+	nl_cli_addr_parse_broadcast;
+	nl_cli_addr_parse_dev;
+	nl_cli_addr_parse_family;
+	nl_cli_addr_parse_label;
+	nl_cli_addr_parse_local;
+	nl_cli_addr_parse_peer;
+	nl_cli_addr_parse_preferred;
+	nl_cli_addr_parse_scope;
+	nl_cli_addr_parse_valid;
+	nl_cli_alloc_cache;
+	nl_cli_alloc_socket;
+	nl_cli_class_alloc;
+	nl_cli_class_alloc_cache;
+	nl_cli_cls_alloc;
+	nl_cli_cls_alloc_cache;
+	nl_cli_cls_parse_ematch;
+	nl_cli_cls_parse_proto;
+	nl_cli_confirm;
+	nl_cli_connect;
+	nl_cli_ct_alloc;
+	nl_cli_ct_alloc_cache;
+	nl_cli_ct_parse_dst;
+	nl_cli_ct_parse_dst_port;
+	nl_cli_ct_parse_family;
+	nl_cli_ct_parse_id;
+	nl_cli_ct_parse_mark;
+	nl_cli_ct_parse_protocol;
+	nl_cli_ct_parse_src;
+	nl_cli_ct_parse_src_port;
+	nl_cli_ct_parse_status;
+	nl_cli_ct_parse_tcp_state;
+	nl_cli_ct_parse_timeout;
+	nl_cli_ct_parse_use;
+	nl_cli_ct_parse_zone;
+	nl_cli_exp_alloc;
+	nl_cli_exp_alloc_cache;
+	nl_cli_exp_parse_class;
+	nl_cli_exp_parse_dst;
+	nl_cli_exp_parse_dst_port;
+	nl_cli_exp_parse_family;
+	nl_cli_exp_parse_flags;
+	nl_cli_exp_parse_fn;
+	nl_cli_exp_parse_helper_name;
+	nl_cli_exp_parse_icmp_code;
+	nl_cli_exp_parse_icmp_id;
+	nl_cli_exp_parse_icmp_type;
+	nl_cli_exp_parse_id;
+	nl_cli_exp_parse_l4protonum;
+	nl_cli_exp_parse_nat_dir;
+	nl_cli_exp_parse_src;
+	nl_cli_exp_parse_src_port;
+	nl_cli_exp_parse_timeout;
+	nl_cli_exp_parse_zone;
+	nl_cli_fatal;
+	nl_cli_link_alloc;
+	nl_cli_link_alloc_cache;
+	nl_cli_link_alloc_cache_family;
+	nl_cli_link_parse_family;
+	nl_cli_link_parse_ifalias;
+	nl_cli_link_parse_ifindex;
+	nl_cli_link_parse_mtu;
+	nl_cli_link_parse_name;
+	nl_cli_link_parse_txqlen;
+	nl_cli_link_parse_weight;
+	nl_cli_load_module;
+	nl_cli_neigh_alloc;
+	nl_cli_neigh_parse_dev;
+	nl_cli_neigh_parse_dst;
+	nl_cli_neigh_parse_family;
+	nl_cli_neigh_parse_lladdr;
+	nl_cli_neigh_parse_state;
+	nl_cli_parse_dumptype;
+	nl_cli_parse_u32;
+	nl_cli_print_version;
+	nl_cli_qdisc_alloc;
+	nl_cli_route_alloc;
+	nl_cli_route_alloc_cache;
+	nl_cli_route_parse_dst;
+	nl_cli_route_parse_family;
+	nl_cli_route_parse_iif;
+	nl_cli_route_parse_metric;
+	nl_cli_route_parse_nexthop;
+	nl_cli_route_parse_pref_src;
+	nl_cli_route_parse_prio;
+	nl_cli_route_parse_protocol;
+	nl_cli_route_parse_scope;
+	nl_cli_route_parse_src;
+	nl_cli_route_parse_table;
+	nl_cli_route_parse_type;
+	nl_cli_rule_alloc;
+	nl_cli_rule_alloc_cache;
+	nl_cli_rule_parse_family;
+	nl_cli_tc_lookup;
+	nl_cli_tc_parse_dev;
+	nl_cli_tc_parse_handle;
+	nl_cli_tc_parse_kind;
+	nl_cli_tc_parse_linktype;
+	nl_cli_tc_parse_mpu;
+	nl_cli_tc_parse_mtu;
+	nl_cli_tc_parse_overhead;
+	nl_cli_tc_parse_parent;
+	nl_cli_tc_register;
+	nl_cli_tc_unregister;
+local:
+	*;
+};
+
+libnl_3_2_28 {
+global:
+	nl_cli_alloc_cache_flags;
+	nl_cli_link_alloc_cache_flags;
+	nl_cli_link_alloc_cache_family_flags;
+} libnl_3;
diff --git a/libnl-genl-3.sym b/libnl-genl-3.sym
new file mode 100644
index 0000000..0f9616d
--- /dev/null
+++ b/libnl-genl-3.sym
@@ -0,0 +1,51 @@
+libnl_3 {
+global:
+	# these functions are in private header files and should have never
+	# been exported. We might hide them later.
+	genl_resolve_id;
+
+	genl_connect;
+	genl_ctrl_alloc_cache;
+	genl_ctrl_resolve;
+	genl_ctrl_resolve_grp;
+	genl_ctrl_search;
+	genl_ctrl_search_by_name;
+	genl_family_add_grp;
+	genl_family_add_op;
+	genl_family_alloc;
+	genl_family_get_hdrsize;
+	genl_family_get_id;
+	genl_family_get_maxattr;
+	genl_family_get_name;
+	genl_family_get_version;
+	genl_family_ops;
+	genl_family_put;
+	genl_family_set_hdrsize;
+	genl_family_set_id;
+	genl_family_set_maxattr;
+	genl_family_set_name;
+	genl_family_set_version;
+	genl_handle_msg;
+	genl_mngt_resolve;
+	genl_op2name;
+	genl_ops_resolve;
+	genl_register;
+	genl_register_family;
+	genl_send_simple;
+	genl_unregister;
+	genl_unregister_family;
+	genlmsg_attrdata;
+	genlmsg_attrlen;
+	genlmsg_data;
+	genlmsg_hdr;
+	genlmsg_len;
+	genlmsg_parse;
+	genlmsg_put;
+	genlmsg_user_data;
+	genlmsg_user_datalen;
+	genlmsg_user_hdr;
+	genlmsg_valid_hdr;
+	genlmsg_validate;
+local:
+	*;
+};
diff --git a/libnl-idiag-3.sym b/libnl-idiag-3.sym
new file mode 100644
index 0000000..c56cef5
--- /dev/null
+++ b/libnl-idiag-3.sym
@@ -0,0 +1,109 @@
+libnl_3 {
+global:
+	# ops structure
+	idiagnl_meminfo_obj_ops;
+	idiagnl_msg_obj_ops;
+	idiagnl_req_obj_ops;
+	idiagnl_vegasinfo_obj_ops;
+
+	idiagnl_attrs2str;
+	idiagnl_connect;
+	idiagnl_exts2str;
+	idiagnl_meminfo_alloc;
+	idiagnl_meminfo_get;
+	idiagnl_meminfo_get_fmem;
+	idiagnl_meminfo_get_rmem;
+	idiagnl_meminfo_get_tmem;
+	idiagnl_meminfo_get_wmem;
+	idiagnl_meminfo_put;
+	idiagnl_meminfo_set_fmem;
+	idiagnl_meminfo_set_rmem;
+	idiagnl_meminfo_set_tmem;
+	idiagnl_meminfo_set_wmem;
+	idiagnl_msg_alloc;
+	idiagnl_msg_alloc_cache;
+	idiagnl_msg_get;
+	idiagnl_msg_get_cong;
+	idiagnl_msg_get_dport;
+	idiagnl_msg_get_dst;
+	idiagnl_msg_get_expires;
+	idiagnl_msg_get_family;
+	idiagnl_msg_get_ifindex;
+	idiagnl_msg_get_inode;
+	idiagnl_msg_get_meminfo;
+	idiagnl_msg_get_retrans;
+	idiagnl_msg_get_rqueue;
+	idiagnl_msg_get_shutdown;
+	idiagnl_msg_get_sport;
+	idiagnl_msg_get_src;
+	idiagnl_msg_get_state;
+	idiagnl_msg_get_tclass;
+	idiagnl_msg_get_tcpinfo;
+	idiagnl_msg_get_timer;
+	idiagnl_msg_get_tos;
+	idiagnl_msg_get_uid;
+	idiagnl_msg_get_vegasinfo;
+	idiagnl_msg_get_wqueue;
+	idiagnl_msg_parse;
+	idiagnl_msg_put;
+	idiagnl_msg_set_cong;
+	idiagnl_msg_set_dport;
+	idiagnl_msg_set_dst;
+	idiagnl_msg_set_expires;
+	idiagnl_msg_set_family;
+	idiagnl_msg_set_ifindex;
+	idiagnl_msg_set_inode;
+	idiagnl_msg_set_meminfo;
+	idiagnl_msg_set_retrans;
+	idiagnl_msg_set_rqueue;
+	idiagnl_msg_set_shutdown;
+	idiagnl_msg_set_sport;
+	idiagnl_msg_set_src;
+	idiagnl_msg_set_state;
+	idiagnl_msg_set_tclass;
+	idiagnl_msg_set_tcpinfo;
+	idiagnl_msg_set_timer;
+	idiagnl_msg_set_tos;
+	idiagnl_msg_set_uid;
+	idiagnl_msg_set_vegasinfo;
+	idiagnl_msg_set_wqueue;
+	idiagnl_req_alloc;
+	idiagnl_req_get;
+	idiagnl_req_get_dbs;
+	idiagnl_req_get_dst;
+	idiagnl_req_get_ext;
+	idiagnl_req_get_family;
+	idiagnl_req_get_ifindex;
+	idiagnl_req_get_src;
+	idiagnl_req_get_states;
+	idiagnl_req_parse;
+	idiagnl_req_put;
+	idiagnl_req_set_dbs;
+	idiagnl_req_set_dst;
+	idiagnl_req_set_ext;
+	idiagnl_req_set_family;
+	idiagnl_req_set_ifindex;
+	idiagnl_req_set_src;
+	idiagnl_req_set_states;
+	idiagnl_send_simple;
+	idiagnl_shutdown2str;
+	idiagnl_state2str;
+	idiagnl_str2state;
+	idiagnl_str2timer;
+	idiagnl_tcpopts2str;
+	idiagnl_tcpstate2str;
+	idiagnl_timer2str;
+	idiagnl_vegasinfo_alloc;
+	idiagnl_vegasinfo_get;
+	idiagnl_vegasinfo_get_enabled;
+	idiagnl_vegasinfo_get_minrtt;
+	idiagnl_vegasinfo_get_rtt;
+	idiagnl_vegasinfo_get_rttcnt;
+	idiagnl_vegasinfo_put;
+	idiagnl_vegasinfo_set_enabled;
+	idiagnl_vegasinfo_set_minrtt;
+	idiagnl_vegasinfo_set_rtt;
+	idiagnl_vegasinfo_set_rttcnt;
+local:
+	*;
+};
diff --git a/libnl-nf-3.sym b/libnl-nf-3.sym
new file mode 100644
index 0000000..504e2dd
--- /dev/null
+++ b/libnl-nf-3.sym
@@ -0,0 +1,316 @@
+libnl_3 {
+global:
+	# ops structure
+	ct_obj_ops;
+	exp_obj_ops;
+	log_msg_obj_ops;
+	log_obj_ops;
+	queue_msg_obj_ops;
+	queue_obj_ops;
+
+	nfnl_connect;
+	nfnl_ct_add;
+	nfnl_ct_alloc;
+	nfnl_ct_alloc_cache;
+	nfnl_ct_build_add_request;
+	nfnl_ct_build_delete_request;
+	nfnl_ct_build_query_request;
+	nfnl_ct_del;
+	nfnl_ct_dump_request;
+	nfnl_ct_get;
+	nfnl_ct_get_bytes;
+	nfnl_ct_get_dst;
+	nfnl_ct_get_dst_port;
+	nfnl_ct_get_family;
+	nfnl_ct_get_icmp_code;
+	nfnl_ct_get_icmp_id;
+	nfnl_ct_get_icmp_type;
+	nfnl_ct_get_id;
+	nfnl_ct_get_mark;
+	nfnl_ct_get_packets;
+	nfnl_ct_get_proto;
+	nfnl_ct_get_src;
+	nfnl_ct_get_src_port;
+	nfnl_ct_get_status;
+	nfnl_ct_get_tcp_state;
+	nfnl_ct_get_timeout;
+	nfnl_ct_get_timestamp;
+	nfnl_ct_get_use;
+	nfnl_ct_get_zone;
+	nfnl_ct_put;
+	nfnl_ct_query;
+	nfnl_ct_set_bytes;
+	nfnl_ct_set_dst;
+	nfnl_ct_set_dst_port;
+	nfnl_ct_set_family;
+	nfnl_ct_set_icmp_code;
+	nfnl_ct_set_icmp_id;
+	nfnl_ct_set_icmp_type;
+	nfnl_ct_set_id;
+	nfnl_ct_set_mark;
+	nfnl_ct_set_packets;
+	nfnl_ct_set_proto;
+	nfnl_ct_set_src;
+	nfnl_ct_set_src_port;
+	nfnl_ct_set_status;
+	nfnl_ct_set_tcp_state;
+	nfnl_ct_set_timeout;
+	nfnl_ct_set_timestamp;
+	nfnl_ct_set_use;
+	nfnl_ct_set_zone;
+	nfnl_ct_status2str;
+	nfnl_ct_str2status;
+	nfnl_ct_str2tcp_state;
+	nfnl_ct_tcp_state2str;
+	nfnl_ct_test_bytes;
+	nfnl_ct_test_dst_port;
+	nfnl_ct_test_icmp_code;
+	nfnl_ct_test_icmp_id;
+	nfnl_ct_test_icmp_type;
+	nfnl_ct_test_id;
+	nfnl_ct_test_mark;
+	nfnl_ct_test_packets;
+	nfnl_ct_test_proto;
+	nfnl_ct_test_src_port;
+	nfnl_ct_test_status;
+	nfnl_ct_test_tcp_state;
+	nfnl_ct_test_timeout;
+	nfnl_ct_test_timestamp;
+	nfnl_ct_test_use;
+	nfnl_ct_test_zone;
+	nfnl_ct_unset_status;
+	nfnl_exp_add;
+	nfnl_exp_alloc;
+	nfnl_exp_alloc_cache;
+	nfnl_exp_build_add_request;
+	nfnl_exp_build_delete_request;
+	nfnl_exp_build_query_request;
+	nfnl_exp_del;
+	nfnl_exp_dump_request;
+	nfnl_exp_flags2str;
+	nfnl_exp_get;
+	nfnl_exp_get_class;
+	nfnl_exp_get_dst;
+	nfnl_exp_get_dst_port;
+	nfnl_exp_get_family;
+	nfnl_exp_get_flags;
+	nfnl_exp_get_fn;
+	nfnl_exp_get_helper_name;
+	nfnl_exp_get_icmp_code;
+	nfnl_exp_get_icmp_id;
+	nfnl_exp_get_icmp_type;
+	nfnl_exp_get_id;
+	nfnl_exp_get_l4protonum;
+	nfnl_exp_get_nat_dir;
+	nfnl_exp_get_src;
+	nfnl_exp_get_src_port;
+	nfnl_exp_get_timeout;
+	nfnl_exp_get_zone;
+	nfnl_exp_put;
+	nfnl_exp_query;
+	nfnl_exp_set_class;
+	nfnl_exp_set_dst;
+	nfnl_exp_set_family;
+	nfnl_exp_set_flags;
+	nfnl_exp_set_fn;
+	nfnl_exp_set_helper_name;
+	nfnl_exp_set_icmp;
+	nfnl_exp_set_id;
+	nfnl_exp_set_l4protonum;
+	nfnl_exp_set_nat_dir;
+	nfnl_exp_set_ports;
+	nfnl_exp_set_src;
+	nfnl_exp_set_timeout;
+	nfnl_exp_set_zone;
+	nfnl_exp_str2flags;
+	nfnl_exp_test_class;
+	nfnl_exp_test_dst;
+	nfnl_exp_test_flags;
+	nfnl_exp_test_fn;
+	nfnl_exp_test_helper_name;
+	nfnl_exp_test_icmp;
+	nfnl_exp_test_id;
+	nfnl_exp_test_l4protonum;
+	nfnl_exp_test_nat_dir;
+	nfnl_exp_test_ports;
+	nfnl_exp_test_src;
+	nfnl_exp_test_timeout;
+	nfnl_exp_test_zone;
+	nfnl_exp_unset_flags;
+	nfnl_inet_hook2str;
+	nfnl_log_alloc;
+	nfnl_log_build_change_request;
+	nfnl_log_build_create_request;
+	nfnl_log_build_delete_request;
+	nfnl_log_build_pf_bind;
+	nfnl_log_build_pf_unbind;
+	nfnl_log_change;
+	nfnl_log_copy_mode2str;
+	nfnl_log_create;
+	nfnl_log_delete;
+	nfnl_log_flags2str;
+	nfnl_log_get;
+	nfnl_log_get_alloc_size;
+	nfnl_log_get_copy_mode;
+	nfnl_log_get_copy_range;
+	nfnl_log_get_flush_timeout;
+	nfnl_log_get_group;
+	nfnl_log_get_queue_threshold;
+	nfnl_log_msg_alloc;
+	nfnl_log_msg_get;
+	nfnl_log_msg_get_family;
+	nfnl_log_msg_get_gid;
+	nfnl_log_msg_get_hook;
+	nfnl_log_msg_get_hwaddr;
+	nfnl_log_msg_get_hwproto;
+	nfnl_log_msg_get_indev;
+	nfnl_log_msg_get_mark;
+	nfnl_log_msg_get_outdev;
+	nfnl_log_msg_get_payload;
+	nfnl_log_msg_get_physindev;
+	nfnl_log_msg_get_physoutdev;
+	nfnl_log_msg_get_prefix;
+	nfnl_log_msg_get_seq;
+	nfnl_log_msg_get_seq_global;
+	nfnl_log_msg_get_timestamp;
+	nfnl_log_msg_get_uid;
+	nfnl_log_msg_put;
+	nfnl_log_msg_set_family;
+	nfnl_log_msg_set_gid;
+	nfnl_log_msg_set_hook;
+	nfnl_log_msg_set_hwaddr;
+	nfnl_log_msg_set_hwproto;
+	nfnl_log_msg_set_indev;
+	nfnl_log_msg_set_mark;
+	nfnl_log_msg_set_outdev;
+	nfnl_log_msg_set_payload;
+	nfnl_log_msg_set_physindev;
+	nfnl_log_msg_set_physoutdev;
+	nfnl_log_msg_set_prefix;
+	nfnl_log_msg_set_seq;
+	nfnl_log_msg_set_seq_global;
+	nfnl_log_msg_set_timestamp;
+	nfnl_log_msg_set_uid;
+	nfnl_log_msg_test_gid;
+	nfnl_log_msg_test_hook;
+	nfnl_log_msg_test_hwproto;
+	nfnl_log_msg_test_mark;
+	nfnl_log_msg_test_seq;
+	nfnl_log_msg_test_seq_global;
+	nfnl_log_msg_test_uid;
+	nfnl_log_pf_bind;
+	nfnl_log_pf_unbind;
+	nfnl_log_put;
+	nfnl_log_set_alloc_size;
+	nfnl_log_set_copy_mode;
+	nfnl_log_set_copy_range;
+	nfnl_log_set_flags;
+	nfnl_log_set_flush_timeout;
+	nfnl_log_set_group;
+	nfnl_log_set_queue_threshold;
+	nfnl_log_str2copy_mode;
+	nfnl_log_str2flags;
+	nfnl_log_test_alloc_size;
+	nfnl_log_test_copy_mode;
+	nfnl_log_test_copy_range;
+	nfnl_log_test_flush_timeout;
+	nfnl_log_test_group;
+	nfnl_log_test_queue_threshold;
+	nfnl_log_unset_flags;
+	nfnl_queue_alloc;
+	nfnl_queue_build_change_request;
+	nfnl_queue_build_create_request;
+	nfnl_queue_build_delete_request;
+	nfnl_queue_build_pf_bind;
+	nfnl_queue_build_pf_unbind;
+	nfnl_queue_change;
+	nfnl_queue_copy_mode2str;
+	nfnl_queue_create;
+	nfnl_queue_delete;
+	nfnl_queue_get;
+	nfnl_queue_get_copy_mode;
+	nfnl_queue_get_copy_range;
+	nfnl_queue_get_group;
+	nfnl_queue_get_maxlen;
+	nfnl_queue_msg_alloc;
+	nfnl_queue_msg_build_verdict;
+	nfnl_queue_msg_build_verdict_batch;
+	nfnl_queue_msg_get;
+	nfnl_queue_msg_get_family;
+	nfnl_queue_msg_get_group;
+	nfnl_queue_msg_get_hook;
+	nfnl_queue_msg_get_hwaddr;
+	nfnl_queue_msg_get_hwproto;
+	nfnl_queue_msg_get_indev;
+	nfnl_queue_msg_get_mark;
+	nfnl_queue_msg_get_outdev;
+	nfnl_queue_msg_get_packetid;
+	nfnl_queue_msg_get_payload;
+	nfnl_queue_msg_get_physindev;
+	nfnl_queue_msg_get_physoutdev;
+	nfnl_queue_msg_get_timestamp;
+	nfnl_queue_msg_get_verdict;
+	nfnl_queue_msg_put;
+	nfnl_queue_msg_send_verdict;
+	nfnl_queue_msg_send_verdict_batch;
+	nfnl_queue_msg_send_verdict_payload;
+	nfnl_queue_msg_set_family;
+	nfnl_queue_msg_set_group;
+	nfnl_queue_msg_set_hook;
+	nfnl_queue_msg_set_hwaddr;
+	nfnl_queue_msg_set_hwproto;
+	nfnl_queue_msg_set_indev;
+	nfnl_queue_msg_set_mark;
+	nfnl_queue_msg_set_outdev;
+	nfnl_queue_msg_set_packetid;
+	nfnl_queue_msg_set_payload;
+	nfnl_queue_msg_set_physindev;
+	nfnl_queue_msg_set_physoutdev;
+	nfnl_queue_msg_set_timestamp;
+	nfnl_queue_msg_set_verdict;
+	nfnl_queue_msg_test_family;
+	nfnl_queue_msg_test_group;
+	nfnl_queue_msg_test_hook;
+	nfnl_queue_msg_test_hwaddr;
+	nfnl_queue_msg_test_hwproto;
+	nfnl_queue_msg_test_indev;
+	nfnl_queue_msg_test_mark;
+	nfnl_queue_msg_test_outdev;
+	nfnl_queue_msg_test_packetid;
+	nfnl_queue_msg_test_payload;
+	nfnl_queue_msg_test_physindev;
+	nfnl_queue_msg_test_physoutdev;
+	nfnl_queue_msg_test_timestamp;
+	nfnl_queue_msg_test_verdict;
+	nfnl_queue_pf_bind;
+	nfnl_queue_pf_unbind;
+	nfnl_queue_put;
+	nfnl_queue_set_copy_mode;
+	nfnl_queue_set_copy_range;
+	nfnl_queue_set_group;
+	nfnl_queue_set_maxlen;
+	nfnl_queue_socket_alloc;
+	nfnl_queue_str2copy_mode;
+	nfnl_queue_test_copy_mode;
+	nfnl_queue_test_copy_range;
+	nfnl_queue_test_group;
+	nfnl_queue_test_maxlen;
+	nfnl_send_simple;
+	nfnl_str2inet_hook;
+	nfnl_str2verdict;
+	nfnl_verdict2str;
+	nfnlmsg_alloc_simple;
+	nfnlmsg_ct_group;
+	nfnlmsg_ct_parse;
+	nfnlmsg_exp_group;
+	nfnlmsg_exp_parse;
+	nfnlmsg_family;
+	nfnlmsg_log_msg_parse;
+	nfnlmsg_put;
+	nfnlmsg_queue_msg_parse;
+	nfnlmsg_res_id;
+	nfnlmsg_subsys;
+	nfnlmsg_subtype;
+local:
+	*;
+};
diff --git a/libnl-route-3.sym b/libnl-route-3.sym
new file mode 100644
index 0000000..4a65503
--- /dev/null
+++ b/libnl-route-3.sym
@@ -0,0 +1,1152 @@
+libnl_3 {
+global:
+	# these functions are in private header files and should have never
+	# been exported. We might hide them later.
+	rtnl_link_af_alloc;
+	rtnl_link_af_data;
+	rtnl_link_af_data_compare;
+	rtnl_link_af_ops_lookup;
+	rtnl_link_af_ops_put;
+	rtnl_link_af_register;
+	rtnl_link_af_unregister;
+	rtnl_link_info_ops_lookup;
+	rtnl_link_info_ops_put;
+	rtnl_link_register_info;
+	rtnl_link_unregister_info;
+	rtnl_tc_build_rate_table;
+	rtnl_tc_clone;
+	rtnl_tc_compare;
+	rtnl_tc_data;
+	rtnl_tc_data_check;
+	rtnl_tc_dump_details;
+	rtnl_tc_dump_line;
+	rtnl_tc_dump_stats;
+	rtnl_tc_free_data;
+	rtnl_tc_msg_build;
+	rtnl_tc_msg_parse;
+	rtnl_tc_register;
+	rtnl_tc_type_register;
+	rtnl_tc_type_unregister;
+	rtnl_tc_unregister;
+
+	# these functions are in private header files and should have never
+	# been exported. They are used by libnl internals
+	rtnl_tc_get_ops;
+	rtnl_tc_lookup_ops;
+
+	# internal symbols that are in public headers
+	rtln_link_policy;
+
+	# ops structure
+	route_obj_ops;
+
+	flnl_lookup;
+	flnl_lookup_build_request;
+	flnl_request_alloc;
+	flnl_request_get_addr;
+	flnl_request_get_fwmark;
+	flnl_request_get_scope;
+	flnl_request_get_table;
+	flnl_request_get_tos;
+	flnl_request_set_addr;
+	flnl_request_set_fwmark;
+	flnl_request_set_scope;
+	flnl_request_set_table;
+	flnl_request_set_tos;
+	flnl_result_alloc;
+	flnl_result_alloc_cache;
+	flnl_result_get_error;
+	flnl_result_get_nexthop_sel;
+	flnl_result_get_prefixlen;
+	flnl_result_get_scope;
+	flnl_result_get_table_id;
+	flnl_result_get_type;
+	flnl_result_put;
+	nl_ovl_strategy2str;
+	nl_police2str;
+	nl_rtgen_request;
+	nl_rtntype2str;
+	nl_str2ovl_strategy;
+	nl_str2police;
+	nl_str2rtntype;
+	rtnl_act_add;
+	rtnl_act_alloc;
+	rtnl_act_append;
+	rtnl_act_build_add_request;
+	rtnl_act_build_change_request;
+	rtnl_act_build_delete_request;
+	rtnl_act_change;
+	rtnl_act_delete;
+	rtnl_act_fill;
+	rtnl_act_get;
+	rtnl_act_parse;
+	rtnl_act_put;
+	rtnl_act_put_all;
+	rtnl_act_remove;
+	rtnl_addr_add;
+	rtnl_addr_alloc;
+	rtnl_addr_alloc_cache;
+	rtnl_addr_build_add_request;
+	rtnl_addr_build_delete_request;
+	rtnl_addr_delete;
+	rtnl_addr_flags2str;
+	rtnl_addr_get;
+	rtnl_addr_get_anycast;
+	rtnl_addr_get_broadcast;
+	rtnl_addr_get_create_time;
+	rtnl_addr_get_family;
+	rtnl_addr_get_flags;
+	rtnl_addr_get_ifindex;
+	rtnl_addr_get_label;
+	rtnl_addr_get_last_update_time;
+	rtnl_addr_get_link;
+	rtnl_addr_get_local;
+	rtnl_addr_get_multicast;
+	rtnl_addr_get_peer;
+	rtnl_addr_get_preferred_lifetime;
+	rtnl_addr_get_prefixlen;
+	rtnl_addr_get_scope;
+	rtnl_addr_get_valid_lifetime;
+	rtnl_addr_put;
+	rtnl_addr_set_anycast;
+	rtnl_addr_set_broadcast;
+	rtnl_addr_set_family;
+	rtnl_addr_set_flags;
+	rtnl_addr_set_ifindex;
+	rtnl_addr_set_label;
+	rtnl_addr_set_link;
+	rtnl_addr_set_local;
+	rtnl_addr_set_multicast;
+	rtnl_addr_set_peer;
+	rtnl_addr_set_preferred_lifetime;
+	rtnl_addr_set_prefixlen;
+	rtnl_addr_set_scope;
+	rtnl_addr_set_valid_lifetime;
+	rtnl_addr_str2flags;
+	rtnl_addr_unset_flags;
+	rtnl_basic_add_action;
+	rtnl_basic_del_action;
+	rtnl_basic_get_ematch;
+	rtnl_basic_get_target;
+	rtnl_basic_set_ematch;
+	rtnl_basic_set_target;
+	rtnl_cgroup_get_ematch;
+	rtnl_cgroup_set_ematch;
+	rtnl_class_add;
+	rtnl_class_alloc;
+	rtnl_class_alloc_cache;
+	rtnl_class_build_add_request;
+	rtnl_class_build_delete_request;
+	rtnl_class_delete;
+	rtnl_class_dsmark_get_bitmask;
+	rtnl_class_dsmark_get_value;
+	rtnl_class_dsmark_set_bitmask;
+	rtnl_class_dsmark_set_value;
+	rtnl_class_foreach_child;
+	rtnl_class_foreach_cls;
+	rtnl_class_get;
+	rtnl_class_leaf_qdisc;
+	rtnl_class_put;
+	rtnl_classid_generate;
+	rtnl_cls_add;
+	rtnl_cls_alloc;
+	rtnl_cls_alloc_cache;
+	rtnl_cls_build_add_request;
+	rtnl_cls_build_change_request;
+	rtnl_cls_build_delete_request;
+	rtnl_cls_change;
+	rtnl_cls_delete;
+	rtnl_cls_get_prio;
+	rtnl_cls_get_protocol;
+	rtnl_cls_put;
+	rtnl_cls_set_prio;
+	rtnl_cls_set_protocol;
+	rtnl_ematch_add_child;
+	rtnl_ematch_alloc;
+	rtnl_ematch_cmp_get;
+	rtnl_ematch_cmp_set;
+	rtnl_ematch_data;
+	rtnl_ematch_fill_attr;
+	rtnl_ematch_free;
+	rtnl_ematch_get_flags;
+	rtnl_ematch_lookup_ops;
+	rtnl_ematch_lookup_ops_by_name;
+	rtnl_ematch_meta_set_lvalue;
+	rtnl_ematch_meta_set_operand;
+	rtnl_ematch_meta_set_rvalue;
+	rtnl_ematch_nbyte_get_layer;
+	rtnl_ematch_nbyte_get_len;
+	rtnl_ematch_nbyte_get_offset;
+	rtnl_ematch_nbyte_get_pattern;
+	rtnl_ematch_nbyte_set_offset;
+	rtnl_ematch_nbyte_set_pattern;
+	rtnl_ematch_offset2txt;
+	rtnl_ematch_opnd2txt;
+	rtnl_ematch_parse_attr;
+	rtnl_ematch_parse_expr;
+	rtnl_ematch_register;
+	rtnl_ematch_set_flags;
+	rtnl_ematch_set_kind;
+	rtnl_ematch_set_name;
+	rtnl_ematch_set_ops;
+	rtnl_ematch_text_get_algo;
+	rtnl_ematch_text_get_from_layer;
+	rtnl_ematch_text_get_from_offset;
+	rtnl_ematch_text_get_len;
+	rtnl_ematch_text_get_pattern;
+	rtnl_ematch_text_get_to_layer;
+	rtnl_ematch_text_get_to_offset;
+	rtnl_ematch_text_set_algo;
+	rtnl_ematch_text_set_from;
+	rtnl_ematch_text_set_pattern;
+	rtnl_ematch_text_set_to;
+	rtnl_ematch_tree_add;
+	rtnl_ematch_tree_alloc;
+	rtnl_ematch_tree_dump;
+	rtnl_ematch_tree_free;
+	rtnl_ematch_unlink;
+	rtnl_ematch_unset_flags;
+	rtnl_fw_set_classid;
+	rtnl_fw_set_mask;
+	rtnl_htb_get_cbuffer;
+	rtnl_htb_get_ceil;
+	rtnl_htb_get_defcls;
+	rtnl_htb_get_level;
+	rtnl_htb_get_prio;
+	rtnl_htb_get_quantum;
+	rtnl_htb_get_rate2quantum;
+	rtnl_htb_get_rate;
+	rtnl_htb_get_rbuffer;
+	rtnl_htb_set_cbuffer;
+	rtnl_htb_set_ceil;
+	rtnl_htb_set_defcls;
+	rtnl_htb_set_level;
+	rtnl_htb_set_prio;
+	rtnl_htb_set_quantum;
+	rtnl_htb_set_rate2quantum;
+	rtnl_htb_set_rate;
+	rtnl_htb_set_rbuffer;
+	rtnl_link_add;
+	rtnl_link_alloc;
+	rtnl_link_alloc_cache;
+	rtnl_link_bond_add;
+	rtnl_link_bond_alloc;
+	rtnl_link_bond_enslave;
+	rtnl_link_bond_enslave_ifindex;
+	rtnl_link_bond_release;
+	rtnl_link_bond_release_ifindex;
+	rtnl_link_bridge_add;
+	rtnl_link_bridge_alloc;
+	rtnl_link_bridge_flags2str;
+	rtnl_link_bridge_get_cost;
+	rtnl_link_bridge_get_flags;
+	rtnl_link_bridge_get_port_state;
+	rtnl_link_bridge_get_priority;
+	rtnl_link_bridge_has_ext_info;
+	rtnl_link_bridge_set_cost;
+	rtnl_link_bridge_set_flags;
+	rtnl_link_bridge_set_port_state;
+	rtnl_link_bridge_set_priority;
+	rtnl_link_bridge_str2flags;
+	rtnl_link_bridge_unset_flags;
+	rtnl_link_build_add_request;
+	rtnl_link_build_change_request;
+	rtnl_link_build_delete_request;
+	rtnl_link_build_get_request;
+	rtnl_link_can_berr;
+	rtnl_link_can_berr_rx;
+	rtnl_link_can_berr_tx;
+	rtnl_link_can_ctrlmode2str;
+	rtnl_link_can_freq;
+	rtnl_link_can_get_bitrate;
+	rtnl_link_can_get_bittiming;
+	rtnl_link_can_get_bt_const;
+	rtnl_link_can_get_ctrlmode;
+	rtnl_link_can_get_restart_ms;
+	rtnl_link_can_get_sample_point;
+	rtnl_link_can_restart;
+	rtnl_link_can_set_bitrate;
+	rtnl_link_can_set_bittiming;
+	rtnl_link_can_set_ctrlmode;
+	rtnl_link_can_set_restart_ms;
+	rtnl_link_can_set_sample_point;
+	rtnl_link_can_state;
+	rtnl_link_can_str2ctrlmode;
+	rtnl_link_can_unset_ctrlmode;
+	rtnl_link_carrier2str;
+	rtnl_link_change;
+	rtnl_link_delete;
+	rtnl_link_enslave;
+	rtnl_link_enslave_ifindex;
+	rtnl_link_fill_info;
+	rtnl_link_flags2str;
+	rtnl_link_get;
+	rtnl_link_get_addr;
+	rtnl_link_get_arptype;
+	rtnl_link_get_broadcast;
+	rtnl_link_get_by_name;
+	rtnl_link_get_carrier;
+	rtnl_link_get_family;
+	rtnl_link_get_flags;
+	rtnl_link_get_group;
+	rtnl_link_get_ifalias;
+	rtnl_link_get_ifindex;
+	rtnl_link_get_info_type;
+	rtnl_link_get_kernel;
+	rtnl_link_get_link;
+	rtnl_link_get_linkmode;
+	rtnl_link_get_master;
+	rtnl_link_get_mtu;
+	rtnl_link_get_name;
+	rtnl_link_get_ns_fd;
+	rtnl_link_get_ns_pid;
+	rtnl_link_get_num_rx_queues;
+	rtnl_link_get_num_tx_queues;
+	rtnl_link_get_num_vf;
+	rtnl_link_get_operstate;
+	rtnl_link_get_phys_port_id;
+	rtnl_link_get_pmtudisc;
+	rtnl_link_get_promiscuity;
+	rtnl_link_get_qdisc;
+	rtnl_link_get_stat;
+	rtnl_link_get_txqlen;
+	rtnl_link_get_type;
+	rtnl_link_get_weight;
+	rtnl_link_i2name;
+	rtnl_link_inet_devconf2str;
+	rtnl_link_inet_get_conf;
+	rtnl_link_inet_set_conf;
+	rtnl_link_inet_str2devconf;
+	rtnl_link_info_parse;
+	rtnl_link_ip6_tnl_add;
+	rtnl_link_ip6_tnl_alloc;
+	rtnl_link_ip6_tnl_get_encaplimit;
+	rtnl_link_ip6_tnl_get_flags;
+	rtnl_link_ip6_tnl_get_flowinfo;
+	rtnl_link_ip6_tnl_get_link;
+	rtnl_link_ip6_tnl_get_local;
+	rtnl_link_ip6_tnl_get_proto;
+	rtnl_link_ip6_tnl_get_remote;
+	rtnl_link_ip6_tnl_get_tos;
+	rtnl_link_ip6_tnl_get_ttl;
+	rtnl_link_ip6_tnl_set_encaplimit;
+	rtnl_link_ip6_tnl_set_flags;
+	rtnl_link_ip6_tnl_set_flowinfo;
+	rtnl_link_ip6_tnl_set_link;
+	rtnl_link_ip6_tnl_set_local;
+	rtnl_link_ip6_tnl_set_proto;
+	rtnl_link_ip6_tnl_set_remote;
+	rtnl_link_ip6_tnl_set_tos;
+	rtnl_link_ip6_tnl_set_ttl;
+	rtnl_link_ipgre_add;
+	rtnl_link_ipgre_alloc;
+	rtnl_link_ipgre_get_iflags;
+	rtnl_link_ipgre_get_ikey;
+	rtnl_link_ipgre_get_link;
+	rtnl_link_ipgre_get_local;
+	rtnl_link_ipgre_get_oflags;
+	rtnl_link_ipgre_get_okey;
+	rtnl_link_ipgre_get_remote;
+	rtnl_link_ipgre_get_tos;
+	rtnl_link_ipgre_get_ttl;
+	rtnl_link_ipgre_set_iflags;
+	rtnl_link_ipgre_set_ikey;
+	rtnl_link_ipgre_set_link;
+	rtnl_link_ipgre_set_local;
+	rtnl_link_ipgre_set_oflags;
+	rtnl_link_ipgre_set_okey;
+	rtnl_link_ipgre_set_pmtudisc;
+	rtnl_link_ipgre_set_remote;
+	rtnl_link_ipgre_set_tos;
+	rtnl_link_ipgre_set_ttl;
+	rtnl_link_ipip_add;
+	rtnl_link_ipip_alloc;
+	rtnl_link_ipip_get_link;
+	rtnl_link_ipip_get_local;
+	rtnl_link_ipip_get_pmtudisc;
+	rtnl_link_ipip_get_remote;
+	rtnl_link_ipip_get_tos;
+	rtnl_link_ipip_get_ttl;
+	rtnl_link_ipip_set_link;
+	rtnl_link_ipip_set_local;
+	rtnl_link_ipip_set_pmtudisc;
+	rtnl_link_ipip_set_remote;
+	rtnl_link_ipip_set_tos;
+	rtnl_link_ipip_set_ttl;
+	rtnl_link_ipvti_add;
+	rtnl_link_ipvti_alloc;
+	rtnl_link_ipvti_get_ikey;
+	rtnl_link_ipvti_get_link;
+	rtnl_link_ipvti_get_local;
+	rtnl_link_ipvti_get_okey;
+	rtnl_link_ipvti_get_remote;
+	rtnl_link_ipvti_set_ikey;
+	rtnl_link_ipvti_set_link;
+	rtnl_link_ipvti_set_local;
+	rtnl_link_ipvti_set_okey;
+	rtnl_link_ipvti_set_remote;
+	rtnl_link_is_bridge;
+	rtnl_link_is_can;
+	rtnl_link_is_ip6_tnl;
+	rtnl_link_is_ipgre;
+	rtnl_link_is_ipip;
+	rtnl_link_is_ipvti;
+	rtnl_link_is_macvlan;
+	rtnl_link_is_sit;
+	rtnl_link_is_veth;
+	rtnl_link_is_vlan;
+	rtnl_link_is_vxlan;
+	rtnl_link_macvlan_alloc;
+	rtnl_link_macvlan_flags2str;
+	rtnl_link_macvlan_get_flags;
+	rtnl_link_macvlan_get_mode;
+	rtnl_link_macvlan_mode2str;
+	rtnl_link_macvlan_set_flags;
+	rtnl_link_macvlan_set_mode;
+	rtnl_link_macvlan_str2flags;
+	rtnl_link_macvlan_str2mode;
+	rtnl_link_macvlan_unset_flags;
+	rtnl_link_mode2str;
+	rtnl_link_name2i;
+	rtnl_link_operstate2str;
+	rtnl_link_put;
+	rtnl_link_release;
+	rtnl_link_release_ifindex;
+	rtnl_link_set_addr;
+	rtnl_link_set_arptype;
+	rtnl_link_set_broadcast;
+	rtnl_link_set_carrier;
+	rtnl_link_set_family;
+	rtnl_link_set_flags;
+	rtnl_link_set_group;
+	rtnl_link_set_ifalias;
+	rtnl_link_set_ifindex;
+	rtnl_link_set_info_type;
+	rtnl_link_set_link;
+	rtnl_link_set_linkmode;
+	rtnl_link_set_master;
+	rtnl_link_set_mtu;
+	rtnl_link_set_name;
+	rtnl_link_set_ns_fd;
+	rtnl_link_set_ns_pid;
+	rtnl_link_set_num_rx_queues;
+	rtnl_link_set_num_tx_queues;
+	rtnl_link_set_operstate;
+	rtnl_link_set_promiscuity;
+	rtnl_link_set_qdisc;
+	rtnl_link_set_stat;
+	rtnl_link_set_txqlen;
+	rtnl_link_set_type;
+	rtnl_link_set_weight;
+	rtnl_link_sit_add;
+	rtnl_link_sit_alloc;
+	rtnl_link_sit_get_flags;
+	rtnl_link_sit_get_link;
+	rtnl_link_sit_get_local;
+	rtnl_link_sit_get_pmtudisc;
+	rtnl_link_sit_get_proto;
+	rtnl_link_sit_get_remote;
+	rtnl_link_sit_get_tos;
+	rtnl_link_sit_get_ttl;
+	rtnl_link_sit_set_flags;
+	rtnl_link_sit_set_link;
+	rtnl_link_sit_set_local;
+	rtnl_link_sit_set_pmtudisc;
+	rtnl_link_sit_set_proto;
+	rtnl_link_sit_set_remote;
+	rtnl_link_sit_set_tos;
+	rtnl_link_sit_set_ttl;
+	rtnl_link_stat2str;
+	rtnl_link_str2carrier;
+	rtnl_link_str2flags;
+	rtnl_link_str2mode;
+	rtnl_link_str2operstate;
+	rtnl_link_str2stat;
+	rtnl_link_unset_flags;
+	rtnl_link_veth_add;
+	rtnl_link_veth_alloc;
+	rtnl_link_veth_get_peer;
+	rtnl_link_veth_release;
+	rtnl_link_vlan_alloc;
+	rtnl_link_vlan_flags2str;
+	rtnl_link_vlan_get_egress_map;
+	rtnl_link_vlan_get_flags;
+	rtnl_link_vlan_get_id;
+	rtnl_link_vlan_get_ingress_map;
+	rtnl_link_vlan_get_protocol;
+	rtnl_link_vlan_set_egress_map;
+	rtnl_link_vlan_set_flags;
+	rtnl_link_vlan_set_id;
+	rtnl_link_vlan_set_ingress_map;
+	rtnl_link_vlan_set_protocol;
+	rtnl_link_vlan_str2flags;
+	rtnl_link_vlan_unset_flags;
+	rtnl_link_vxlan_alloc;
+	rtnl_link_vxlan_disable_l2miss;
+	rtnl_link_vxlan_disable_l3miss;
+	rtnl_link_vxlan_disable_learning;
+	rtnl_link_vxlan_disable_proxy;
+	rtnl_link_vxlan_disable_rsc;
+	rtnl_link_vxlan_enable_l2miss;
+	rtnl_link_vxlan_enable_l3miss;
+	rtnl_link_vxlan_enable_learning;
+	rtnl_link_vxlan_enable_proxy;
+	rtnl_link_vxlan_enable_rsc;
+	rtnl_link_vxlan_get_ageing;
+	rtnl_link_vxlan_get_group;
+	rtnl_link_vxlan_get_id;
+	rtnl_link_vxlan_get_l2miss;
+	rtnl_link_vxlan_get_l3miss;
+	rtnl_link_vxlan_get_learning;
+	rtnl_link_vxlan_get_limit;
+	rtnl_link_vxlan_get_link;
+	rtnl_link_vxlan_get_local;
+	rtnl_link_vxlan_get_port_range;
+	rtnl_link_vxlan_get_proxy;
+	rtnl_link_vxlan_get_rsc;
+	rtnl_link_vxlan_get_tos;
+	rtnl_link_vxlan_get_ttl;
+	rtnl_link_vxlan_set_ageing;
+	rtnl_link_vxlan_set_group;
+	rtnl_link_vxlan_set_id;
+	rtnl_link_vxlan_set_l2miss;
+	rtnl_link_vxlan_set_l3miss;
+	rtnl_link_vxlan_set_learning;
+	rtnl_link_vxlan_set_limit;
+	rtnl_link_vxlan_set_link;
+	rtnl_link_vxlan_set_local;
+	rtnl_link_vxlan_set_port_range;
+	rtnl_link_vxlan_set_proxy;
+	rtnl_link_vxlan_set_rsc;
+	rtnl_link_vxlan_set_tos;
+	rtnl_link_vxlan_set_ttl;
+	rtnl_meta_value_alloc_id;
+	rtnl_meta_value_alloc_int;
+	rtnl_meta_value_alloc_var;
+	rtnl_meta_value_put;
+	rtnl_mirred_get_action;
+	rtnl_mirred_get_ifindex;
+	rtnl_mirred_get_policy;
+	rtnl_mirred_set_action;
+	rtnl_mirred_set_ifindex;
+	rtnl_mirred_set_policy;
+	rtnl_neigh_add;
+	rtnl_neigh_alloc;
+	rtnl_neigh_alloc_cache;
+	rtnl_neigh_build_add_request;
+	rtnl_neigh_build_delete_request;
+	rtnl_neigh_delete;
+	rtnl_neigh_flags2str;
+	rtnl_neigh_get;
+	rtnl_neigh_get_dst;
+	rtnl_neigh_get_family;
+	rtnl_neigh_get_flags;
+	rtnl_neigh_get_ifindex;
+	rtnl_neigh_get_lladdr;
+	rtnl_neigh_get_state;
+	rtnl_neigh_get_type;
+	rtnl_neigh_parse;
+	rtnl_neigh_put;
+	rtnl_neigh_set_dst;
+	rtnl_neigh_set_family;
+	rtnl_neigh_set_flags;
+	rtnl_neigh_set_ifindex;
+	rtnl_neigh_set_lladdr;
+	rtnl_neigh_set_state;
+	rtnl_neigh_set_type;
+	rtnl_neigh_state2str;
+	rtnl_neigh_str2flag;
+	rtnl_neigh_str2state;
+	rtnl_neigh_unset_flags;
+	rtnl_neigh_unset_state;
+	rtnl_neightbl_alloc;
+	rtnl_neightbl_alloc_cache;
+	rtnl_neightbl_build_change_request;
+	rtnl_neightbl_change;
+	rtnl_neightbl_get;
+	rtnl_neightbl_put;
+	rtnl_neightbl_set_anycast_delay;
+	rtnl_neightbl_set_app_probes;
+	rtnl_neightbl_set_base_reachable_time;
+	rtnl_neightbl_set_delay_probe_time;
+	rtnl_neightbl_set_dev;
+	rtnl_neightbl_set_family;
+	rtnl_neightbl_set_gc_interval;
+	rtnl_neightbl_set_gc_stale_time;
+	rtnl_neightbl_set_gc_tresh1;
+	rtnl_neightbl_set_gc_tresh2;
+	rtnl_neightbl_set_gc_tresh3;
+	rtnl_neightbl_set_locktime;
+	rtnl_neightbl_set_mcast_probes;
+	rtnl_neightbl_set_name;
+	rtnl_neightbl_set_proxy_delay;
+	rtnl_neightbl_set_proxy_queue_len;
+	rtnl_neightbl_set_queue_len;
+	rtnl_neightbl_set_retrans_time;
+	rtnl_neightbl_set_ucast_probes;
+	rtnl_netem_get_corruption_correlation;
+	rtnl_netem_get_corruption_probability;
+	rtnl_netem_get_delay;
+	rtnl_netem_get_delay_correlation;
+	rtnl_netem_get_delay_distribution;
+	rtnl_netem_get_delay_distribution_size;
+	rtnl_netem_get_duplicate;
+	rtnl_netem_get_duplicate_correlation;
+	rtnl_netem_get_gap;
+	rtnl_netem_get_jitter;
+	rtnl_netem_get_limit;
+	rtnl_netem_get_loss;
+	rtnl_netem_get_loss_correlation;
+	rtnl_netem_get_reorder_correlation;
+	rtnl_netem_get_reorder_probability;
+	rtnl_netem_set_corruption_correlation;
+	rtnl_netem_set_corruption_probability;
+	rtnl_netem_set_delay;
+	rtnl_netem_set_delay_correlation;
+	rtnl_netem_set_delay_distribution;
+	rtnl_netem_set_duplicate;
+	rtnl_netem_set_duplicate_correlation;
+	rtnl_netem_set_gap;
+	rtnl_netem_set_jitter;
+	rtnl_netem_set_limit;
+	rtnl_netem_set_loss;
+	rtnl_netem_set_loss_correlation;
+	rtnl_netem_set_reorder_correlation;
+	rtnl_netem_set_reorder_probability;
+	rtnl_pktloc_add;
+	rtnl_pktloc_alloc;
+	rtnl_pktloc_foreach;
+	rtnl_pktloc_lookup;
+	rtnl_pktloc_put;
+	rtnl_prio2str;
+	rtnl_qdisc_add;
+	rtnl_qdisc_alloc;
+	rtnl_qdisc_alloc_cache;
+	rtnl_qdisc_build_add_request;
+	rtnl_qdisc_build_change_request;
+	rtnl_qdisc_build_delete_request;
+	rtnl_qdisc_build_update_request;
+	rtnl_qdisc_change;
+	rtnl_qdisc_delete;
+	rtnl_qdisc_dsmark_get_default_index;
+	rtnl_qdisc_dsmark_get_indices;
+	rtnl_qdisc_dsmark_get_set_tc_index;
+	rtnl_qdisc_dsmark_set_default_index;
+	rtnl_qdisc_dsmark_set_indices;
+	rtnl_qdisc_dsmark_set_set_tc_index;
+	rtnl_qdisc_fifo_get_limit;
+	rtnl_qdisc_fifo_set_limit;
+	rtnl_qdisc_foreach_child;
+	rtnl_qdisc_foreach_cls;
+	rtnl_qdisc_fq_codel_get_ecn;
+	rtnl_qdisc_fq_codel_get_flows;
+	rtnl_qdisc_fq_codel_get_interval;
+	rtnl_qdisc_fq_codel_get_limit;
+	rtnl_qdisc_fq_codel_get_quantum;
+	rtnl_qdisc_fq_codel_get_target;
+	rtnl_qdisc_fq_codel_set_ecn;
+	rtnl_qdisc_fq_codel_set_flows;
+	rtnl_qdisc_fq_codel_set_interval;
+	rtnl_qdisc_fq_codel_set_limit;
+	rtnl_qdisc_fq_codel_set_quantum;
+	rtnl_qdisc_fq_codel_set_target;
+	rtnl_qdisc_get;
+	rtnl_qdisc_get_by_parent;
+	rtnl_qdisc_plug_buffer;
+	rtnl_qdisc_plug_release_indefinite;
+	rtnl_qdisc_plug_release_one;
+	rtnl_qdisc_plug_set_limit;
+	rtnl_qdisc_prio_get_bands;
+	rtnl_qdisc_prio_get_priomap;
+	rtnl_qdisc_prio_set_bands;
+	rtnl_qdisc_prio_set_priomap;
+	rtnl_qdisc_put;
+	rtnl_qdisc_tbf_get_limit;
+	rtnl_qdisc_tbf_get_peakrate;
+	rtnl_qdisc_tbf_get_peakrate_bucket;
+	rtnl_qdisc_tbf_get_peakrate_cell;
+	rtnl_qdisc_tbf_get_rate;
+	rtnl_qdisc_tbf_get_rate_bucket;
+	rtnl_qdisc_tbf_get_rate_cell;
+	rtnl_qdisc_tbf_set_limit;
+	rtnl_qdisc_tbf_set_limit_by_latency;
+	rtnl_qdisc_tbf_set_peakrate;
+	rtnl_qdisc_tbf_set_rate;
+	rtnl_qdisc_update;
+	rtnl_realms2str;
+	rtnl_red_get_limit;
+	rtnl_red_set_limit;
+	rtnl_route_add;
+	rtnl_route_add_nexthop;
+	rtnl_route_alloc;
+	rtnl_route_alloc_cache;
+	rtnl_route_build_add_request;
+	rtnl_route_build_del_request;
+	rtnl_route_build_msg;
+	rtnl_route_delete;
+	rtnl_route_foreach_nexthop;
+	rtnl_route_get;
+	rtnl_route_get_dst;
+	rtnl_route_get_family;
+	rtnl_route_get_flags;
+	rtnl_route_get_iif;
+	rtnl_route_get_metric;
+	rtnl_route_get_nexthops;
+	rtnl_route_get_nnexthops;
+	rtnl_route_get_pref_src;
+	rtnl_route_get_priority;
+	rtnl_route_get_protocol;
+	rtnl_route_get_scope;
+	rtnl_route_get_src;
+	rtnl_route_get_table;
+	rtnl_route_get_tos;
+	rtnl_route_get_type;
+	rtnl_route_guess_scope;
+	rtnl_route_metric2str;
+	rtnl_route_nexthop_n;
+	rtnl_route_nh_alloc;
+	rtnl_route_nh_clone;
+	rtnl_route_nh_compare;
+	rtnl_route_nh_dump;
+	rtnl_route_nh_flags2str;
+	rtnl_route_nh_free;
+	rtnl_route_nh_get_flags;
+	rtnl_route_nh_get_gateway;
+	rtnl_route_nh_get_ifindex;
+	rtnl_route_nh_get_realms;
+	rtnl_route_nh_get_weight;
+	rtnl_route_nh_set_flags;
+	rtnl_route_nh_set_gateway;
+	rtnl_route_nh_set_ifindex;
+	rtnl_route_nh_set_realms;
+	rtnl_route_nh_set_weight;
+	rtnl_route_nh_str2flags;
+	rtnl_route_nh_unset_flags;
+	rtnl_route_parse;
+	rtnl_route_proto2str;
+	rtnl_route_put;
+	rtnl_route_read_protocol_names;
+	rtnl_route_read_table_names;
+	rtnl_route_remove_nexthop;
+	rtnl_route_set_dst;
+	rtnl_route_set_family;
+	rtnl_route_set_flags;
+	rtnl_route_set_iif;
+	rtnl_route_set_metric;
+	rtnl_route_set_pref_src;
+	rtnl_route_set_priority;
+	rtnl_route_set_protocol;
+	rtnl_route_set_scope;
+	rtnl_route_set_src;
+	rtnl_route_set_table;
+	rtnl_route_set_tos;
+	rtnl_route_set_type;
+	rtnl_route_str2metric;
+	rtnl_route_str2proto;
+	rtnl_route_str2table;
+	rtnl_route_table2str;
+	rtnl_route_unset_flags;
+	rtnl_route_unset_metric;
+	rtnl_rule_add;
+	rtnl_rule_alloc;
+	rtnl_rule_alloc_cache;
+	rtnl_rule_build_add_request;
+	rtnl_rule_build_delete_request;
+	rtnl_rule_delete;
+	rtnl_rule_get_action;
+	rtnl_rule_get_dsfield;
+	rtnl_rule_get_dst;
+	rtnl_rule_get_family;
+	rtnl_rule_get_goto;
+	rtnl_rule_get_iif;
+	rtnl_rule_get_mark;
+	rtnl_rule_get_mask;
+	rtnl_rule_get_oif;
+	rtnl_rule_get_prio;
+	rtnl_rule_get_realms;
+	rtnl_rule_get_src;
+	rtnl_rule_get_table;
+	rtnl_rule_put;
+	rtnl_rule_set_action;
+	rtnl_rule_set_dsfield;
+	rtnl_rule_set_dst;
+	rtnl_rule_set_family;
+	rtnl_rule_set_goto;
+	rtnl_rule_set_iif;
+	rtnl_rule_set_mark;
+	rtnl_rule_set_mask;
+	rtnl_rule_set_oif;
+	rtnl_rule_set_prio;
+	rtnl_rule_set_realms;
+	rtnl_rule_set_src;
+	rtnl_rule_set_table;
+	rtnl_scope2str;
+	rtnl_sfq_get_divisor;
+	rtnl_sfq_get_limit;
+	rtnl_sfq_get_perturb;
+	rtnl_sfq_get_quantum;
+	rtnl_sfq_set_limit;
+	rtnl_sfq_set_perturb;
+	rtnl_sfq_set_quantum;
+	rtnl_str2prio;
+	rtnl_str2scope;
+	rtnl_tc_calc_bufsize;
+	rtnl_tc_calc_cell_log;
+	rtnl_tc_calc_txtime;
+	rtnl_tc_get_handle;
+	rtnl_tc_get_ifindex;
+	rtnl_tc_get_kind;
+	rtnl_tc_get_link;
+	rtnl_tc_get_linktype;
+	rtnl_tc_get_mpu;
+	rtnl_tc_get_mtu;
+	rtnl_tc_get_overhead;
+	rtnl_tc_get_parent;
+	rtnl_tc_get_stat;
+	rtnl_tc_handle2str;
+	rtnl_tc_read_classid_file;
+	rtnl_tc_set_handle;
+	rtnl_tc_set_ifindex;
+	rtnl_tc_set_kind;
+	rtnl_tc_set_link;
+	rtnl_tc_set_linktype;
+	rtnl_tc_set_mpu;
+	rtnl_tc_set_mtu;
+	rtnl_tc_set_overhead;
+	rtnl_tc_set_parent;
+	rtnl_tc_str2handle;
+	rtnl_u32_add_action;
+	rtnl_u32_add_key;
+	rtnl_u32_add_key_in6_addr;
+	rtnl_u32_add_key_in_addr;
+	rtnl_u32_add_key_uint16;
+	rtnl_u32_add_key_uint32;
+	rtnl_u32_add_key_uint8;
+	rtnl_u32_del_action;
+	rtnl_u32_get_key;
+	rtnl_u32_set_classid;
+	rtnl_u32_set_cls_terminal;
+	rtnl_u32_set_divisor;
+	rtnl_u32_set_flags;
+	rtnl_u32_set_handle;
+	rtnl_u32_set_hashmask;
+	rtnl_u32_set_hashtable;
+	rtnl_u32_set_link;
+
+	# The following symbols were added during the development of 3.2.26.
+	# Keep them in libnl_3 to avoid breaking users.
+	rtnl_class_hfsc_get_fsc;
+	rtnl_class_hfsc_get_rsc;
+	rtnl_class_hfsc_get_usc;
+	rtnl_class_hfsc_set_fsc;
+	rtnl_class_hfsc_set_rsc;
+	rtnl_class_hfsc_set_usc;
+	rtnl_link_inet6_addrgenmode2str;
+	rtnl_link_inet6_get_addr_gen_mode;
+	rtnl_link_inet6_get_token;
+	rtnl_link_inet6_set_addr_gen_mode;
+	rtnl_link_inet6_set_token;
+	rtnl_link_inet6_str2addrgenmode;
+	rtnl_qdisc_hfsc_get_defcls;
+	rtnl_qdisc_hfsc_set_defcls;
+	rtnl_u32_add_mark;
+	rtnl_u32_del_mark;
+
+local:
+	*;
+};
+
+libnl_3_2_26 {
+global:
+	rtnl_neigh_get_vlan;
+	rtnl_neigh_set_vlan;
+	rtnl_skbedit_get_action;
+	rtnl_skbedit_get_mark;
+	rtnl_skbedit_get_priority;
+	rtnl_skbedit_get_queue_mapping;
+	rtnl_skbedit_set_action;
+	rtnl_skbedit_set_mark;
+	rtnl_skbedit_set_priority;
+	rtnl_skbedit_set_queue_mapping;
+	rtnl_tc_stat2str;
+	rtnl_tc_str2stat;
+	rtnl_u32_get_classid;
+} libnl_3;
+
+libnl_3_2_27 {
+global:
+	rtnl_link_get_link_netnsid;
+	rtnl_link_ipvlan_alloc;
+	rtnl_link_is_ipvlan;
+	rtnl_link_ipvlan_mode2str;
+	rtnl_link_ipvlan_str2mode;
+	rtnl_link_ipvlan_set_mode;
+	rtnl_link_ipvlan_get_mode;
+	rtnl_link_set_link_netnsid;
+} libnl_3_2_26;
+
+libnl_3_2_28 {
+global:
+	rtnl_link_alloc_cache_flags;
+	rtnl_link_bridge_get_port_vlan;
+	rtnl_link_bridge_has_vlan;
+	rtnl_link_bridge_pvid;
+	rtnl_link_is_macvtap;
+	rtnl_link_is_vrf;
+	rtnl_link_ipgretap_add;
+	rtnl_link_ipgretap_alloc;
+	rtnl_link_macsec_alloc;
+	rtnl_link_macsec_set_sci;
+	rtnl_link_macsec_get_sci;
+	rtnl_link_macsec_set_port;
+	rtnl_link_macsec_get_port;
+	rtnl_link_macsec_set_cipher_suite;
+	rtnl_link_macsec_get_cipher_suite;
+	rtnl_link_macsec_set_icv_len;
+	rtnl_link_macsec_get_icv_len;
+	rtnl_link_macsec_set_protect;
+	rtnl_link_macsec_get_protect;
+	rtnl_link_macsec_set_encrypt;
+	rtnl_link_macsec_get_encrypt;
+	rtnl_link_macsec_set_encoding_sa;
+	rtnl_link_macsec_get_encoding_sa;
+	rtnl_link_macsec_set_validation_type;
+	rtnl_link_macsec_get_validation_type;
+	rtnl_link_macsec_set_replay_protect;
+	rtnl_link_macsec_get_replay_protect;
+	rtnl_link_macsec_set_window;
+	rtnl_link_macsec_get_window;
+	rtnl_link_macsec_set_send_sci;
+	rtnl_link_macsec_get_send_sci;
+	rtnl_link_macsec_set_end_station;
+	rtnl_link_macsec_get_end_station;
+	rtnl_link_macsec_set_scb;
+	rtnl_link_macsec_get_scb;
+	rtnl_link_macvtap_alloc;
+	rtnl_link_macvtap_flags2str;
+	rtnl_link_macvtap_get_flags;
+	rtnl_link_macvtap_get_mode;
+	rtnl_link_macvtap_mode2str;
+	rtnl_link_macvtap_set_flags;
+	rtnl_link_macvtap_set_mode;
+	rtnl_link_macvtap_str2flags;
+	rtnl_link_macvtap_str2mode;
+	rtnl_link_macvtap_unset_flags;
+	rtnl_link_sit_get_ip6rd_prefix;
+	rtnl_link_sit_get_ip6rd_prefixlen;
+	rtnl_link_sit_get_ip6rd_relay_prefix;
+	rtnl_link_sit_get_ip6rd_relay_prefixlen;
+	rtnl_link_sit_set_ip6rd_prefix;
+	rtnl_link_sit_set_ip6rd_prefixlen;
+	rtnl_link_sit_set_ip6rd_relay_prefix;
+	rtnl_link_sit_set_ip6rd_relay_prefixlen;
+	rtnl_link_vrf_alloc;
+	rtnl_link_vrf_get_tableid;
+	rtnl_link_vrf_set_tableid;
+	rtnl_neigh_alloc_cache_flags;
+} libnl_3_2_27;
+
+libnl_3_2_29 {
+global:
+	rtnl_gact_set_action;
+	rtnl_gact_get_action;
+	rtnl_link_bridge_portstate2str;
+	rtnl_link_bridge_str2portstate;
+	rtnl_link_bridge_set_self;
+	rtnl_link_bridge_get_hwmode;
+	rtnl_link_bridge_set_hwmode;
+	rtnl_link_bridge_hwmode2str;
+	rtnl_link_bridge_str2hwmode;
+	rtnl_link_get_carrier_changes;
+	rtnl_link_get_gso_max_segs;
+	rtnl_link_get_gso_max_size;
+	rtnl_link_get_phys_port_name;
+	rtnl_link_get_phys_switch_id;
+	rtnl_link_ipgre_get_pmtudisc;
+	rtnl_link_is_ipgretap;
+	rtnl_link_macvlan_add_macaddr;
+	rtnl_link_macvlan_count_macaddr;
+	rtnl_link_macvlan_del_macaddr;
+	rtnl_link_macvlan_get_macaddr;
+	rtnl_link_macvlan_get_macmode;
+	rtnl_link_macvlan_macmode2str;
+	rtnl_link_macvlan_set_macmode;
+	rtnl_link_macvlan_str2macmode;
+	rtnl_link_ppp_alloc;
+	rtnl_link_ppp_set_fd;
+	rtnl_link_ppp_get_fd;
+	rtnl_link_vxlan_get_collect_metadata;
+	rtnl_link_vxlan_get_flags;
+	rtnl_link_vxlan_get_label;
+	rtnl_link_vxlan_get_port;
+	rtnl_link_vxlan_get_remcsum_rx;
+	rtnl_link_vxlan_get_remcsum_tx;
+	rtnl_link_vxlan_get_udp_csum;
+	rtnl_link_vxlan_get_udp_zero_csum6_rx;
+	rtnl_link_vxlan_get_udp_zero_csum6_tx;
+	rtnl_link_vxlan_set_collect_metadata;
+	rtnl_link_vxlan_set_flags;
+	rtnl_link_vxlan_set_label;
+	rtnl_link_vxlan_set_port;
+	rtnl_link_vxlan_set_remcsum_rx;
+	rtnl_link_vxlan_set_remcsum_tx;
+	rtnl_link_vxlan_set_udp_csum;
+	rtnl_link_vxlan_set_udp_zero_csum6_rx;
+	rtnl_link_vxlan_set_udp_zero_csum6_tx;
+	rtnl_link_has_vf_list;
+	rtnl_link_set_vf_list;
+	rtnl_link_unset_vf_list;
+	rtnl_link_vf_add;
+	rtnl_link_vf_alloc;
+	rtnl_link_vf_free;
+	rtnl_link_vf_get;
+	rtnl_link_vf_put;
+	rtnl_link_vf_get_addr;
+	rtnl_link_vf_set_addr;
+	rtnl_link_vf_set_ib_node_guid;
+	rtnl_link_vf_set_ib_port_guid;
+	rtnl_link_vf_get_index;
+	rtnl_link_vf_set_index;
+	rtnl_link_vf_get_linkstate;
+	rtnl_link_vf_set_linkstate;
+	rtnl_link_vf_get_rate;
+	rtnl_link_vf_set_rate;
+	rtnl_link_vf_get_rss_query_en;
+	rtnl_link_vf_set_rss_query_en;
+	rtnl_link_vf_get_spoofchk;
+	rtnl_link_vf_set_spoofchk;
+	rtnl_link_vf_get_stat;
+	rtnl_link_vf_get_trust;
+	rtnl_link_vf_set_trust;
+	rtnl_link_vf_get_vlans;
+	rtnl_link_vf_set_vlans;
+	rtnl_link_vf_vlan_alloc;
+	rtnl_link_vf_vlan_free;
+	rtnl_link_vf_vlan_put;
+	rtnl_link_vf_linkstate2str;
+	rtnl_link_vf_str2linkstate;
+	rtnl_link_vf_vlanproto2str;
+	rtnl_link_vf_str2vlanproto;
+	rtnl_link_vf_str2guid;
+	rtnl_u32_set_selector;
+} libnl_3_2_28;
+
+libnl_3_4 {
+global:
+	rtnl_act_next;
+	rtnl_basic_get_action;
+	rtnl_link_inet6_flags2str;
+	rtnl_link_inet6_get_flags;
+	rtnl_link_inet6_set_flags;
+	rtnl_link_inet6_str2flags;
+	rtnl_netconf_get_all;
+	rtnl_netconf_get_by_idx;
+	rtnl_netconf_get_default;
+	rtnl_netconf_get_family;
+	rtnl_netconf_get_forwarding;
+	rtnl_netconf_get_ifindex;
+	rtnl_netconf_get_input;
+	rtnl_netconf_get_mc_forwarding;
+	rtnl_netconf_get_rp_filter;
+	rtnl_netconf_put;
+	rtnl_rule_get_l3mdev;
+	rtnl_rule_set_l3mdev;
+	rtnl_u32_get_action;
+	rtnl_route_nh_set_newdst;
+	rtnl_route_nh_get_newdst;
+	rtnl_route_nh_set_via;
+	rtnl_route_nh_get_via;
+	rtnl_route_set_ttl_propagate;
+	rtnl_route_get_ttl_propagate;
+	rtnl_route_nh_encap_mpls;
+} libnl_3_2_29;
+
+libnl_3_5 {
+global:
+	rtnl_class_get_by_parent;
+	rtnl_cls_cache_set_tc_params;
+	rtnl_ematch_tree_clone;
+	rtnl_htb_get_ceil64;
+	rtnl_htb_get_rate64;
+	rtnl_htb_set_ceil64;
+	rtnl_htb_set_rate64;
+	rtnl_link_geneve_alloc;
+	rtnl_link_geneve_get_flags;
+	rtnl_link_geneve_get_id;
+	rtnl_link_geneve_get_label;
+	rtnl_link_geneve_get_port;
+	rtnl_link_geneve_get_remote;
+	rtnl_link_geneve_get_tos;
+	rtnl_link_geneve_get_ttl;
+	rtnl_link_geneve_get_udp_csum;
+	rtnl_link_geneve_get_udp_zero_csum6_rx;
+	rtnl_link_geneve_get_udp_zero_csum6_tx;
+	rtnl_link_geneve_set_flags;
+	rtnl_link_geneve_set_id;
+	rtnl_link_geneve_set_label;
+	rtnl_link_geneve_set_port;
+	rtnl_link_geneve_set_remote;
+	rtnl_link_geneve_set_tos;
+	rtnl_link_geneve_set_ttl;
+	rtnl_link_geneve_set_udp_csum;
+	rtnl_link_geneve_set_udp_zero_csum6_rx;
+	rtnl_link_geneve_set_udp_zero_csum6_tx;
+	rtnl_link_get_slave_type;
+	rtnl_link_is_geneve;
+	rtnl_link_is_xfrmi;
+	rtnl_link_set_slave_type;
+	rtnl_link_xfrmi_alloc;
+	rtnl_link_xfrmi_get_if_id;
+	rtnl_link_xfrmi_get_link;
+	rtnl_link_xfrmi_set_if_id;
+	rtnl_link_xfrmi_set_link;
+	rtnl_mall_append_action;
+	rtnl_mall_del_action;
+	rtnl_mall_get_classid;
+	rtnl_mall_get_first_action;
+	rtnl_mall_get_flags;
+	rtnl_mall_set_classid;
+	rtnl_mall_set_flags;
+	rtnl_neigh_get_by_vlan;
+	rtnl_neigh_get_master;
+	rtnl_neigh_set_master;
+	rtnl_netem_set_delay_distribution_data;
+	rtnl_qdisc_mqprio_get_hw_offload;
+	rtnl_qdisc_mqprio_get_max_rate;
+	rtnl_qdisc_mqprio_get_min_rate;
+	rtnl_qdisc_mqprio_get_mode;
+	rtnl_qdisc_mqprio_get_num_tc;
+	rtnl_qdisc_mqprio_get_priomap;
+	rtnl_qdisc_mqprio_get_queue;
+	rtnl_qdisc_mqprio_get_shaper;
+	rtnl_qdisc_mqprio_hw_offload;
+	rtnl_qdisc_mqprio_set_max_rate;
+	rtnl_qdisc_mqprio_set_min_rate;
+	rtnl_qdisc_mqprio_set_mode;
+	rtnl_qdisc_mqprio_set_num_tc;
+	rtnl_qdisc_mqprio_set_priomap;
+	rtnl_qdisc_mqprio_set_queue;
+	rtnl_qdisc_mqprio_set_shaper;
+	rtnl_rule_get_dport;
+	rtnl_rule_get_ipproto;
+	rtnl_rule_get_protocol;
+	rtnl_rule_get_sport;
+	rtnl_rule_set_dport;
+	rtnl_rule_set_dport_range;
+	rtnl_rule_set_ipproto;
+	rtnl_rule_set_protocol;
+	rtnl_rule_set_sport;
+	rtnl_rule_set_sport_range;
+	rtnl_tc_get_chain;
+	rtnl_tc_set_chain;
+	rtnl_vlan_get_action;
+	rtnl_vlan_get_mode;
+	rtnl_vlan_get_protocol;
+	rtnl_vlan_get_vlan_id;
+	rtnl_vlan_get_vlan_prio;
+	rtnl_vlan_set_action;
+	rtnl_vlan_set_mode;
+	rtnl_vlan_set_protocol;
+	rtnl_vlan_set_vlan_id;
+	rtnl_vlan_set_vlan_prio;
+} libnl_3_4;
diff --git a/libnl-xfrm-3.0.pc.in b/libnl-xfrm-3.0.pc.in
new file mode 100644
index 0000000..48ffb70
--- /dev/null
+++ b/libnl-xfrm-3.0.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libnl-xfrm
+Description: Netlink Routing Family Library
+Version: @PACKAGE_VERSION@
+Requires: libnl-3.0
+Libs: -L${libdir} -lnl-xfrm-@MAJ_VERSION@
+Cflags: -I${includedir}/libnl@MAJ_VERSION@
diff --git a/libnl-xfrm-3.sym b/libnl-xfrm-3.sym
new file mode 100644
index 0000000..3706f33
--- /dev/null
+++ b/libnl-xfrm-3.sym
@@ -0,0 +1,246 @@
+libnl_3 {
+global:
+	xfrmnl_ae_alloc;
+	xfrmnl_ae_build_get_request;
+	xfrmnl_ae_flags2str;
+	xfrmnl_ae_get_curlifetime;
+	xfrmnl_ae_get_daddr;
+	xfrmnl_ae_get_family;
+	xfrmnl_ae_get_flags;
+	xfrmnl_ae_get_kernel;
+	xfrmnl_ae_get_mark;
+	xfrmnl_ae_get_proto;
+	xfrmnl_ae_get_replay_maxage;
+	xfrmnl_ae_get_replay_maxdiff;
+	xfrmnl_ae_get_replay_state;
+	xfrmnl_ae_get_replay_state_esn;
+	xfrmnl_ae_get_reqid;
+	xfrmnl_ae_get_saddr;
+	xfrmnl_ae_get_spi;
+	xfrmnl_ae_parse;
+	xfrmnl_ae_put;
+	xfrmnl_ae_set;
+	xfrmnl_ae_set_curlifetime;
+	xfrmnl_ae_set_daddr;
+	xfrmnl_ae_set_family;
+	xfrmnl_ae_set_flags;
+	xfrmnl_ae_set_mark;
+	xfrmnl_ae_set_proto;
+	xfrmnl_ae_set_replay_maxage;
+	xfrmnl_ae_set_replay_maxdiff;
+	xfrmnl_ae_set_replay_state;
+	xfrmnl_ae_set_replay_state_esn;
+	xfrmnl_ae_set_reqid;
+	xfrmnl_ae_set_saddr;
+	xfrmnl_ae_set_spi;
+	xfrmnl_ae_str2flag;
+	xfrmnl_ltime_cfg_alloc;
+	xfrmnl_ltime_cfg_clone;
+	xfrmnl_ltime_cfg_cmp;
+	xfrmnl_ltime_cfg_get;
+	xfrmnl_ltime_cfg_get_hard_addexpires;
+	xfrmnl_ltime_cfg_get_hard_bytelimit;
+	xfrmnl_ltime_cfg_get_hard_packetlimit;
+	xfrmnl_ltime_cfg_get_hard_useexpires;
+	xfrmnl_ltime_cfg_get_soft_addexpires;
+	xfrmnl_ltime_cfg_get_soft_bytelimit;
+	xfrmnl_ltime_cfg_get_soft_packetlimit;
+	xfrmnl_ltime_cfg_get_soft_useexpires;
+	xfrmnl_ltime_cfg_put;
+	xfrmnl_ltime_cfg_set_hard_addexpires;
+	xfrmnl_ltime_cfg_set_hard_bytelimit;
+	xfrmnl_ltime_cfg_set_hard_packetlimit;
+	xfrmnl_ltime_cfg_set_hard_useexpires;
+	xfrmnl_ltime_cfg_set_soft_addexpires;
+	xfrmnl_ltime_cfg_set_soft_bytelimit;
+	xfrmnl_ltime_cfg_set_soft_packetlimit;
+	xfrmnl_ltime_cfg_set_soft_useexpires;
+	xfrmnl_ltime_cfg_shared;
+	xfrmnl_sa_add;
+	xfrmnl_sa_alloc;
+	xfrmnl_sa_alloc_cache;
+	xfrmnl_sa_build_add_request;
+	xfrmnl_sa_build_delete_request;
+	xfrmnl_sa_build_get_request;
+	xfrmnl_sa_build_update_request;
+	xfrmnl_sa_delete;
+	xfrmnl_sa_flags2str;
+	xfrmnl_sa_get;
+	xfrmnl_sa_get_aead_params;
+	xfrmnl_sa_get_auth_params;
+	xfrmnl_sa_get_coaddr;
+	xfrmnl_sa_get_comp_params;
+	xfrmnl_sa_get_crypto_params;
+	xfrmnl_sa_get_curlifetime;
+	xfrmnl_sa_get_daddr;
+	xfrmnl_sa_get_encap_tmpl;
+	xfrmnl_sa_get_family;
+	xfrmnl_sa_get_flags;
+	xfrmnl_sa_get_kernel;
+	xfrmnl_sa_get_lifetime_cfg;
+	xfrmnl_sa_get_mark;
+	xfrmnl_sa_get_mode;
+	xfrmnl_sa_get_proto;
+	xfrmnl_sa_get_replay_maxage;
+	xfrmnl_sa_get_replay_maxdiff;
+	xfrmnl_sa_get_replay_state;
+	xfrmnl_sa_get_replay_state_esn;
+	xfrmnl_sa_get_replay_window;
+	xfrmnl_sa_get_reqid;
+	xfrmnl_sa_get_saddr;
+	xfrmnl_sa_get_sec_ctx;
+	xfrmnl_sa_get_sel;
+	xfrmnl_sa_get_seq;
+	xfrmnl_sa_get_spi;
+	xfrmnl_sa_get_stats;
+	xfrmnl_sa_get_tfcpad;
+	xfrmnl_sa_is_expiry_reached;
+	xfrmnl_sa_is_hardexpiry_reached;
+	xfrmnl_sa_mode2str;
+	xfrmnl_sa_parse;
+	xfrmnl_sa_put;
+	xfrmnl_sa_set_aead_params;
+	xfrmnl_sa_set_auth_params;
+	xfrmnl_sa_set_coaddr;
+	xfrmnl_sa_set_comp_params;
+	xfrmnl_sa_set_crypto_params;
+	xfrmnl_sa_set_daddr;
+	xfrmnl_sa_set_encap_tmpl;
+	xfrmnl_sa_set_family;
+	xfrmnl_sa_set_flags;
+	xfrmnl_sa_set_lifetime_cfg;
+	xfrmnl_sa_set_mark;
+	xfrmnl_sa_set_mode;
+	xfrmnl_sa_set_proto;
+	xfrmnl_sa_set_replay_maxage;
+	xfrmnl_sa_set_replay_maxdiff;
+	xfrmnl_sa_set_replay_state;
+	xfrmnl_sa_set_replay_state_esn;
+	xfrmnl_sa_set_replay_window;
+	xfrmnl_sa_set_reqid;
+	xfrmnl_sa_set_saddr;
+	xfrmnl_sa_set_sec_ctx;
+	xfrmnl_sa_set_sel;
+	xfrmnl_sa_set_spi;
+	xfrmnl_sa_set_tfcpad;
+	xfrmnl_sa_str2flag;
+	xfrmnl_sa_str2mode;
+	xfrmnl_sa_update;
+	xfrmnl_sel_alloc;
+	xfrmnl_sel_clone;
+	xfrmnl_sel_cmp;
+	xfrmnl_sel_dump;
+	xfrmnl_sel_get;
+	xfrmnl_sel_get_daddr;
+	xfrmnl_sel_get_dport;
+	xfrmnl_sel_get_dportmask;
+	xfrmnl_sel_get_family;
+	xfrmnl_sel_get_ifindex;
+	xfrmnl_sel_get_prefixlen_d;
+	xfrmnl_sel_get_prefixlen_s;
+	xfrmnl_sel_get_proto;
+	xfrmnl_sel_get_saddr;
+	xfrmnl_sel_get_sport;
+	xfrmnl_sel_get_sportmask;
+	xfrmnl_sel_get_userid;
+	xfrmnl_sel_put;
+	xfrmnl_sel_set_daddr;
+	xfrmnl_sel_set_dport;
+	xfrmnl_sel_set_dportmask;
+	xfrmnl_sel_set_family;
+	xfrmnl_sel_set_ifindex;
+	xfrmnl_sel_set_prefixlen_d;
+	xfrmnl_sel_set_prefixlen_s;
+	xfrmnl_sel_set_proto;
+	xfrmnl_sel_set_saddr;
+	xfrmnl_sel_set_sport;
+	xfrmnl_sel_set_sportmask;
+	xfrmnl_sel_set_userid;
+	xfrmnl_sel_shared;
+	xfrmnl_sp_action2str;
+	xfrmnl_sp_add;
+	xfrmnl_sp_add_usertemplate;
+	xfrmnl_sp_alloc;
+	xfrmnl_sp_alloc_cache;
+	xfrmnl_sp_build_add_request;
+	xfrmnl_sp_build_delete_request;
+	xfrmnl_sp_build_get_request;
+	xfrmnl_sp_build_update_request;
+	xfrmnl_sp_delete;
+	xfrmnl_sp_dir2str;
+	xfrmnl_sp_flags2str;
+	xfrmnl_sp_foreach_usertemplate;
+	xfrmnl_sp_get;
+	xfrmnl_sp_get_action;
+	xfrmnl_sp_get_curlifetime;
+	xfrmnl_sp_get_dir;
+	xfrmnl_sp_get_flags;
+	xfrmnl_sp_get_index;
+	xfrmnl_sp_get_kernel;
+	xfrmnl_sp_get_lifetime_cfg;
+	xfrmnl_sp_get_mark;
+	xfrmnl_sp_get_nusertemplates;
+	xfrmnl_sp_get_priority;
+	xfrmnl_sp_get_sec_ctx;
+	xfrmnl_sp_get_sel;
+	xfrmnl_sp_get_share;
+	xfrmnl_sp_get_userpolicy_type;
+	xfrmnl_sp_get_usertemplates;
+	xfrmnl_sp_index2dir;
+	xfrmnl_sp_parse;
+	xfrmnl_sp_put;
+	xfrmnl_sp_remove_usertemplate;
+	xfrmnl_sp_set_action;
+	xfrmnl_sp_set_dir;
+	xfrmnl_sp_set_flags;
+	xfrmnl_sp_set_index;
+	xfrmnl_sp_set_lifetime_cfg;
+	xfrmnl_sp_set_mark;
+	xfrmnl_sp_set_priority;
+	xfrmnl_sp_set_sec_ctx;
+	xfrmnl_sp_set_sel;
+	xfrmnl_sp_set_share;
+	xfrmnl_sp_set_userpolicy_type;
+	xfrmnl_sp_share2str;
+	xfrmnl_sp_str2action;
+	xfrmnl_sp_str2dir;
+	xfrmnl_sp_str2flag;
+	xfrmnl_sp_str2share;
+	xfrmnl_sp_str2type;
+	xfrmnl_sp_type2str;
+	xfrmnl_sp_update;
+	xfrmnl_sp_usertemplate_n;
+	xfrmnl_user_tmpl_alloc;
+	xfrmnl_user_tmpl_clone;
+	xfrmnl_user_tmpl_cmp;
+	xfrmnl_user_tmpl_dump;
+	xfrmnl_user_tmpl_free;
+	xfrmnl_user_tmpl_get_aalgos;
+	xfrmnl_user_tmpl_get_calgos;
+	xfrmnl_user_tmpl_get_daddr;
+	xfrmnl_user_tmpl_get_ealgos;
+	xfrmnl_user_tmpl_get_family;
+	xfrmnl_user_tmpl_get_mode;
+	xfrmnl_user_tmpl_get_optional;
+	xfrmnl_user_tmpl_get_proto;
+	xfrmnl_user_tmpl_get_reqid;
+	xfrmnl_user_tmpl_get_saddr;
+	xfrmnl_user_tmpl_get_share;
+	xfrmnl_user_tmpl_get_spi;
+	xfrmnl_user_tmpl_mode2str;
+	xfrmnl_user_tmpl_set_aalgos;
+	xfrmnl_user_tmpl_set_calgos;
+	xfrmnl_user_tmpl_set_daddr;
+	xfrmnl_user_tmpl_set_ealgos;
+	xfrmnl_user_tmpl_set_family;
+	xfrmnl_user_tmpl_set_mode;
+	xfrmnl_user_tmpl_set_optional;
+	xfrmnl_user_tmpl_set_proto;
+	xfrmnl_user_tmpl_set_reqid;
+	xfrmnl_user_tmpl_set_saddr;
+	xfrmnl_user_tmpl_set_share;
+	xfrmnl_user_tmpl_set_spi;
+	xfrmnl_user_tmpl_str2mode;
+local:
+	*;
+};
diff --git a/libnl.sym.in b/libnl.sym.in
deleted file mode 100644
index df8888c..0000000
--- a/libnl.sym.in
+++ /dev/null
@@ -1,9 +0,0 @@
-libnl_@MAJ_VERSION@ {
-global:
-	*;
-local:
-	_nl_socket_generate_local_port_no_release;
-	_nl_socket_is_local_port_unspecified;
-	_nl_socket_used_ports_release_all;
-	_nl_socket_used_ports_set;
-};
diff --git a/m4/ax_pkg_swig.m4 b/m4/ax_pkg_swig.m4
deleted file mode 100644
index e112f3d..0000000
--- a/m4/ax_pkg_swig.m4
+++ /dev/null
@@ -1,135 +0,0 @@
-# ===========================================================================
-#        http://www.gnu.org/software/autoconf-archive/ax_pkg_swig.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_PKG_SWIG([major.minor.micro], [action-if-found], [action-if-not-found])
-#
-# DESCRIPTION
-#
-#   This macro searches for a SWIG installation on your system. If found,
-#   then SWIG is AC_SUBST'd; if not found, then $SWIG is empty.  If SWIG is
-#   found, then SWIG_LIB is set to the SWIG library path, and AC_SUBST'd.
-#
-#   You can use the optional first argument to check if the version of the
-#   available SWIG is greater than or equal to the value of the argument. It
-#   should have the format: N[.N[.N]] (N is a number between 0 and 999. Only
-#   the first N is mandatory.) If the version argument is given (e.g.
-#   1.3.17), AX_PKG_SWIG checks that the swig package is this version number
-#   or higher.
-#
-#   As usual, action-if-found is executed if SWIG is found, otherwise
-#   action-if-not-found is executed.
-#
-#   In configure.in, use as:
-#
-#     AX_PKG_SWIG(1.3.17, [], [ AC_MSG_ERROR([SWIG is required to build..]) ])
-#     AX_SWIG_ENABLE_CXX
-#     AX_SWIG_MULTI_MODULE_SUPPORT
-#     AX_SWIG_PYTHON
-#
-# LICENSE
-#
-#   Copyright (c) 2008 Sebastian Huber <sebastian-huber@web.de>
-#   Copyright (c) 2008 Alan W. Irwin <irwin@beluga.phys.uvic.ca>
-#   Copyright (c) 2008 Rafael Laboissiere <rafael@laboissiere.net>
-#   Copyright (c) 2008 Andrew Collier <colliera@ukzn.ac.za>
-#   Copyright (c) 2011 Murray Cumming <murrayc@openismus.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.
-#
-#   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/>.
-#
-#   As a special exception, the respective Autoconf Macro's copyright owner
-#   gives unlimited permission to copy, distribute and modify the configure
-#   scripts that are the output of Autoconf when processing the Macro. You
-#   need not follow the terms of the GNU General Public License when using
-#   or distributing such scripts, even though portions of the text of the
-#   Macro appear in them. The GNU General Public License (GPL) does govern
-#   all other use of the material that constitutes the Autoconf Macro.
-#
-#   This special exception to the GPL applies to versions of the Autoconf
-#   Macro released by the Autoconf Archive. When you make and distribute a
-#   modified version of the Autoconf Macro, you may extend this special
-#   exception to the GPL to apply to your modified version as well.
-
-#serial 8
-
-AC_DEFUN([AX_PKG_SWIG],[
-        # Ubuntu has swig 2.0 as /usr/bin/swig2.0
-        AC_PATH_PROGS([SWIG],[swig swig2.0])
-        if test -z "$SWIG" ; then
-                m4_ifval([$3],[$3],[:])
-        elif test -n "$1" ; then
-                AC_MSG_CHECKING([SWIG version])
-                [swig_version=`$SWIG -version 2>&1 | grep 'SWIG Version' | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`]
-                AC_MSG_RESULT([$swig_version])
-                if test -n "$swig_version" ; then
-                        # Calculate the required version number components
-                        [required=$1]
-                        [required_major=`echo $required | sed 's/[^0-9].*//'`]
-                        if test -z "$required_major" ; then
-                                [required_major=0]
-                        fi
-                        [required=`echo $required | sed 's/[0-9]*[^0-9]//'`]
-                        [required_minor=`echo $required | sed 's/[^0-9].*//'`]
-                        if test -z "$required_minor" ; then
-                                [required_minor=0]
-                        fi
-                        [required=`echo $required | sed 's/[0-9]*[^0-9]//'`]
-                        [required_patch=`echo $required | sed 's/[^0-9].*//'`]
-                        if test -z "$required_patch" ; then
-                                [required_patch=0]
-                        fi
-                        # Calculate the available version number components
-                        [available=$swig_version]
-                        [available_major=`echo $available | sed 's/[^0-9].*//'`]
-                        if test -z "$available_major" ; then
-                                [available_major=0]
-                        fi
-                        [available=`echo $available | sed 's/[0-9]*[^0-9]//'`]
-                        [available_minor=`echo $available | sed 's/[^0-9].*//'`]
-                        if test -z "$available_minor" ; then
-                                [available_minor=0]
-                        fi
-                        [available=`echo $available | sed 's/[0-9]*[^0-9]//'`]
-                        [available_patch=`echo $available | sed 's/[^0-9].*//'`]
-                        if test -z "$available_patch" ; then
-                                [available_patch=0]
-                        fi
-                        # Convert the version tuple into a single number for easier comparison.
-                        # Using base 100 should be safe since SWIG internally uses BCD values
-                        # to encode its version number.
-                        required_swig_vernum=`expr $required_major \* 10000 \
-                            \+ $required_minor \* 100 \+ $required_patch`
-                        available_swig_vernum=`expr $available_major \* 10000 \
-                            \+ $available_minor \* 100 \+ $available_patch`
-
-                        if test $available_swig_vernum -lt $required_swig_vernum; then
-                                AC_MSG_WARN([SWIG version >= $1 is required.  You have $swig_version.])
-                                SWIG=''
-                                m4_ifval([$3],[$3],[])
-                        else
-                                AC_MSG_CHECKING([for SWIG library])
-                                SWIG_LIB=`$SWIG -swiglib`
-                                AC_MSG_RESULT([$SWIG_LIB])
-                                m4_ifval([$2],[$2],[])
-                        fi
-                else
-                        AC_MSG_WARN([cannot determine SWIG version])
-                        SWIG=''
-                        m4_ifval([$3],[$3],[])
-                fi
-        fi
-        AC_SUBST([SWIG_LIB])
-])
diff --git a/m4/ax_python.m4 b/m4/ax_python.m4
deleted file mode 100644
index 1bc9d8a..0000000
--- a/m4/ax_python.m4
+++ /dev/null
@@ -1,97 +0,0 @@
-# ===========================================================================
-#         http://www.gnu.org/software/autoconf-archive/ax_python.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_PYTHON
-#
-# DESCRIPTION
-#
-#   This macro does a complete Python development environment check.
-#
-#   It recurses through several python versions (from 2.1 to 2.6 in this
-#   version), looking for an executable. When it finds an executable, it
-#   looks to find the header files and library.
-#
-#   It sets PYTHON_BIN to the name of the python executable,
-#   PYTHON_INCLUDE_DIR to the directory holding the header files, and
-#   PYTHON_LIB to the name of the Python library.
-#
-#   This macro calls AC_SUBST on PYTHON_BIN (via AC_CHECK_PROG),
-#   PYTHON_INCLUDE_DIR and PYTHON_LIB.
-#
-# LICENSE
-#
-#   Copyright (c) 2008 Michael Tindal
-#
-#   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/>.
-#
-#   As a special exception, the respective Autoconf Macro's copyright owner
-#   gives unlimited permission to copy, distribute and modify the configure
-#   scripts that are the output of Autoconf when processing the Macro. You
-#   need not follow the terms of the GNU General Public License when using
-#   or distributing such scripts, even though portions of the text of the
-#   Macro appear in them. The GNU General Public License (GPL) does govern
-#   all other use of the material that constitutes the Autoconf Macro.
-#
-#   This special exception to the GPL applies to versions of the Autoconf
-#   Macro released by the Autoconf Archive. When you make and distribute a
-#   modified version of the Autoconf Macro, you may extend this special
-#   exception to the GPL to apply to your modified version as well.
-
-#serial 9
-
-AC_DEFUN([AX_PYTHON],
-[AC_MSG_CHECKING(for python build information)
-AC_MSG_RESULT([])
-for python in python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python; do
-AC_CHECK_PROGS(PYTHON_BIN, [$python])
-ax_python_bin=$PYTHON_BIN
-if test x$ax_python_bin != x; then
-   AC_CHECK_LIB($ax_python_bin, main, ax_python_lib=$ax_python_bin, ax_python_lib=no)
-   AC_CHECK_HEADER([$ax_python_bin/Python.h],
-   [[ax_python_header=`locate $ax_python_bin/Python.h | sed -e s,/Python.h,,`]],
-   ax_python_header=no)
-   if test $ax_python_lib != no; then
-     if test $ax_python_header != no; then
-       break;
-     fi
-   fi
-fi
-done
-if test x$ax_python_bin = x; then
-   ax_python_bin=no
-fi
-if test x$ax_python_header = x; then
-   ax_python_header=no
-fi
-if test x$ax_python_lib = x; then
-   ax_python_lib=no
-fi
-
-AC_MSG_RESULT([  results of the Python check:])
-AC_MSG_RESULT([    Binary:      $ax_python_bin])
-AC_MSG_RESULT([    Library:     $ax_python_lib])
-AC_MSG_RESULT([    Include Dir: $ax_python_header])
-
-if test x$ax_python_header != xno; then
-  PYTHON_INCLUDE_DIR=$ax_python_header
-  AC_SUBST(PYTHON_INCLUDE_DIR)
-fi
-if test x$ax_python_lib != xno; then
-  PYTHON_LIB=$ax_python_lib
-  AC_SUBST(PYTHON_LIB)
-fi
-])dnl
diff --git a/m4/ax_python_devel.m4 b/m4/ax_python_devel.m4
deleted file mode 100644
index a62b860..0000000
--- a/m4/ax_python_devel.m4
+++ /dev/null
@@ -1,325 +0,0 @@
-# ===========================================================================
-#      http://www.gnu.org/software/autoconf-archive/ax_python_devel.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_PYTHON_DEVEL([version])
-#
-# DESCRIPTION
-#
-#   Note: Defines as a precious variable "PYTHON_VERSION". Don't override it
-#   in your configure.ac.
-#
-#   This macro checks for Python and tries to get the include path to
-#   'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS)
-#   output variables. It also exports $(PYTHON_EXTRA_LIBS) and
-#   $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code.
-#
-#   You can search for some particular version of Python by passing a
-#   parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please
-#   note that you *have* to pass also an operator along with the version to
-#   match, and pay special attention to the single quotes surrounding the
-#   version number. Don't use "PYTHON_VERSION" for this: that environment
-#   variable is declared as precious and thus reserved for the end-user.
-#
-#   This macro should work for all versions of Python >= 2.1.0. As an end
-#   user, you can disable the check for the python version by setting the
-#   PYTHON_NOVERSIONCHECK environment variable to something else than the
-#   empty string.
-#
-#   If you need to use this macro for an older Python version, please
-#   contact the authors. We're always open for feedback.
-#
-# LICENSE
-#
-#   Copyright (c) 2009 Sebastian Huber <sebastian-huber@web.de>
-#   Copyright (c) 2009 Alan W. Irwin <irwin@beluga.phys.uvic.ca>
-#   Copyright (c) 2009 Rafael Laboissiere <rafael@laboissiere.net>
-#   Copyright (c) 2009 Andrew Collier <colliera@ukzn.ac.za>
-#   Copyright (c) 2009 Matteo Settenvini <matteo@member.fsf.org>
-#   Copyright (c) 2009 Horst Knorr <hk_classes@knoda.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 3 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/>.
-#
-#   As a special exception, the respective Autoconf Macro's copyright owner
-#   gives unlimited permission to copy, distribute and modify the configure
-#   scripts that are the output of Autoconf when processing the Macro. You
-#   need not follow the terms of the GNU General Public License when using
-#   or distributing such scripts, even though portions of the text of the
-#   Macro appear in them. The GNU General Public License (GPL) does govern
-#   all other use of the material that constitutes the Autoconf Macro.
-#
-#   This special exception to the GPL applies to versions of the Autoconf
-#   Macro released by the Autoconf Archive. When you make and distribute a
-#   modified version of the Autoconf Macro, you may extend this special
-#   exception to the GPL to apply to your modified version as well.
-
-#serial 8
-
-AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL])
-AC_DEFUN([AX_PYTHON_DEVEL],[
-	#
-	# Allow the use of a (user set) custom python version
-	#
-	AC_ARG_VAR([PYTHON_VERSION],[The installed Python
-		version to use, for example '2.3'. This string
-		will be appended to the Python interpreter
-		canonical name.])
-
-	AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]])
-	if test -z "$PYTHON"; then
-	   AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path])
-	   PYTHON_VERSION=""
-	fi
-
-	#
-	# Check for a version of Python >= 2.1.0
-	#
-	AC_MSG_CHECKING([for a version of Python >= '2.1.0'])
-	ac_supports_python_ver=`$PYTHON -c "import sys; \
-		ver = sys.version.split ()[[0]]; \
-		print (ver >= '2.1.0')"`
-	if test "$ac_supports_python_ver" != "True"; then
-		if test -z "$PYTHON_NOVERSIONCHECK"; then
-			AC_MSG_RESULT([no])
-			AC_MSG_FAILURE([
-This version of the AC@&t@_PYTHON_DEVEL macro
-doesn't work properly with versions of Python before
-2.1.0. You may need to re-run configure, setting the
-variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG,
-PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand.
-Moreover, to disable this check, set PYTHON_NOVERSIONCHECK
-to something else than an empty string.
-])
-		else
-			AC_MSG_RESULT([skip at user request])
-		fi
-	else
-		AC_MSG_RESULT([yes])
-	fi
-
-	#
-	# if the macro parameter ``version'' is set, honour it
-	#
-	if test -n "$1"; then
-		AC_MSG_CHECKING([for a version of Python $1])
-		ac_supports_python_ver=`$PYTHON -c "import sys; \
-			ver = sys.version.split ()[[0]]; \
-			print (ver $1)"`
-		if test "$ac_supports_python_ver" = "True"; then
-		   AC_MSG_RESULT([yes])
-		else
-			AC_MSG_RESULT([no])
-			AC_MSG_ERROR([this package requires Python $1.
-If you have it installed, but it isn't the default Python
-interpreter in your system path, please pass the PYTHON_VERSION
-variable to configure. See ``configure --help'' for reference.
-])
-			PYTHON_VERSION=""
-		fi
-	fi
-
-	#
-	# Check if you have distutils, else fail
-	#
-	AC_MSG_CHECKING([for the distutils Python package])
-	ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
-	if test -z "$ac_distutils_result"; then
-		AC_MSG_RESULT([yes])
-	else
-		AC_MSG_RESULT([no])
-		AC_MSG_ERROR([cannot import Python module "distutils".
-Please check your Python installation. The error was:
-$ac_distutils_result])
-		PYTHON_VERSION=""
-	fi
-
-	#
-	# Check for Python include path
-	#
-	AC_MSG_CHECKING([for Python include path])
-	if test -z "$PYTHON_CPPFLAGS"; then
-		python_path=`$PYTHON -c "import distutils.sysconfig; \
-			print (distutils.sysconfig.get_python_inc ());"`
-		if test -n "${python_path}"; then
-			python_path="-I$python_path"
-		fi
-		PYTHON_CPPFLAGS=$python_path
-	fi
-	AC_MSG_RESULT([$PYTHON_CPPFLAGS])
-	AC_SUBST([PYTHON_CPPFLAGS])
-
-	#
-	# Check for Python library path
-	#
-	AC_MSG_CHECKING([for Python library path])
-	if test -z "$PYTHON_LDFLAGS"; then
-		# (makes two attempts to ensure we've got a version number
-		# from the interpreter)
-		ac_python_version=`cat<<EOD | $PYTHON -
-
-# join all versioning strings, on some systems
-# major/minor numbers could be in different list elements
-from distutils.sysconfig import *
-ret = ''
-for e in get_config_vars ('VERSION'):
-	if (e != None):
-		ret += e
-print (ret)
-EOD`
-
-		if test -z "$ac_python_version"; then
-			if test -n "$PYTHON_VERSION"; then
-				ac_python_version=$PYTHON_VERSION
-			else
-				ac_python_version=`$PYTHON -c "import sys; \
-					print (sys.version[[:3]])"`
-			fi
-		fi
-
-		# Make the versioning information available to the compiler
-		AC_DEFINE_UNQUOTED([HAVE_PYTHON], ["$ac_python_version"],
-                                   [If available, contains the Python version number currently in use.])
-
-		# First, the library directory:
-		ac_python_libdir=`cat<<EOD | $PYTHON -
-
-# There should be only one
-import distutils.sysconfig
-for e in distutils.sysconfig.get_config_vars ('LIBDIR'):
-	if e != None:
-		print (e)
-		break
-EOD`
-
-		# Before checking for libpythonX.Y, we need to know
-		# the extension the OS we're on uses for libraries
-		# (we take the first one, if there's more than one fix me!):
-		ac_python_soext=`$PYTHON -c \
-		  "import distutils.sysconfig; \
-		  print (distutils.sysconfig.get_config_vars('SO')[[0]])"`
-
-		# Now, for the library:
-		ac_python_soname=`$PYTHON -c \
-		  "import distutils.sysconfig; \
-		  print (distutils.sysconfig.get_config_vars('LDLIBRARY')[[0]])"`
-
-		# Strip away extension from the end to canonicalize its name:
-		ac_python_library=`echo "$ac_python_soname" | sed "s/${ac_python_soext}$//"`
-
-		# This small piece shamelessly adapted from PostgreSQL python macro;
-		# credits goes to momjian, I think. I'd like to put the right name
-		# in the credits, if someone can point me in the right direction... ?
-		#
-		if test -n "$ac_python_libdir" -a -n "$ac_python_library" \
-			-a x"$ac_python_library" != x"$ac_python_soname"
-		then
-			# use the official shared library
-			ac_python_library=`echo "$ac_python_library" | sed "s/^lib//"`
-			PYTHON_LDFLAGS="-L$ac_python_libdir -l$ac_python_library"
-		else
-			# old way: use libpython from python_configdir
-			ac_python_libdir=`$PYTHON -c \
-			  "from distutils.sysconfig import get_python_lib as f; \
-			  import os; \
-			  print (os.path.join(f(plat_specific=1, standard_lib=1), 'config'));"`
-			PYTHON_LDFLAGS="-L$ac_python_libdir -lpython$ac_python_version"
-		fi
-
-		if test -z "PYTHON_LDFLAGS"; then
-			AC_MSG_ERROR([
-  Cannot determine location of your Python DSO. Please check it was installed with
-  dynamic libraries enabled, or try setting PYTHON_LDFLAGS by hand.
-			])
-		fi
-	fi
-	AC_MSG_RESULT([$PYTHON_LDFLAGS])
-	AC_SUBST([PYTHON_LDFLAGS])
-
-	#
-	# Check for site packages
-	#
-	AC_MSG_CHECKING([for Python site-packages path])
-	if test -z "$PYTHON_SITE_PKG"; then
-		PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
-			print (distutils.sysconfig.get_python_lib(0,0));"`
-	fi
-	AC_MSG_RESULT([$PYTHON_SITE_PKG])
-	AC_SUBST([PYTHON_SITE_PKG])
-
-	#
-	# libraries which must be linked in when embedding
-	#
-	AC_MSG_CHECKING(python extra libraries)
-	if test -z "$PYTHON_EXTRA_LIBS"; then
-	   PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \
-                conf = distutils.sysconfig.get_config_var; \
-                print (conf('LOCALMODLIBS') + ' ' + conf('LIBS'))"`
-	fi
-	AC_MSG_RESULT([$PYTHON_EXTRA_LIBS])
-	AC_SUBST(PYTHON_EXTRA_LIBS)
-
-	#
-	# linking flags needed when embedding
-	#
-	AC_MSG_CHECKING(python extra linking flags)
-	if test -z "$PYTHON_EXTRA_LDFLAGS"; then
-		PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \
-			conf = distutils.sysconfig.get_config_var; \
-			print (conf('LINKFORSHARED'))"`
-	fi
-	AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS])
-	AC_SUBST(PYTHON_EXTRA_LDFLAGS)
-
-	#
-	# final check to see if everything compiles alright
-	#
-	AC_MSG_CHECKING([consistency of all components of python development environment])
-	# save current global flags
-	ac_save_LIBS="$LIBS"
-	ac_save_CPPFLAGS="$CPPFLAGS"
-	LIBS="$ac_save_LIBS $PYTHON_LDFLAGS $PYTHON_EXTRA_LDFLAGS $PYTHON_EXTRA_LIBS"
-	CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS"
-	AC_LANG_PUSH([C])
-	AC_LINK_IFELSE([
-		AC_LANG_PROGRAM([[#include <Python.h>]],
-				[[Py_Initialize();]])
-		],[pythonexists=yes],[pythonexists=no])
-	AC_LANG_POP([C])
-	# turn back to default flags
-	CPPFLAGS="$ac_save_CPPFLAGS"
-	LIBS="$ac_save_LIBS"
-
-	AC_MSG_RESULT([$pythonexists])
-
-        if test ! "x$pythonexists" = "xyes"; then
-	   AC_MSG_FAILURE([
-  Could not link test program to Python. Maybe the main Python library has been
-  installed in some non-standard library path. If so, pass it to configure,
-  via the LDFLAGS environment variable.
-  Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib"
-  ============================================================================
-   ERROR!
-   You probably have to install the development version of the Python package
-   for your distribution.  The exact name of this package varies among them.
-  ============================================================================
-	   ])
-	  PYTHON_VERSION=""
-	fi
-
-	#
-	# all done!
-	#
-])
diff --git a/m4/ax_swig_python.m4 b/m4/ax_swig_python.m4
deleted file mode 100644
index 8fd3df5..0000000
--- a/m4/ax_swig_python.m4
+++ /dev/null
@@ -1,64 +0,0 @@
-# ===========================================================================
-#      http://www.gnu.org/software/autoconf-archive/ax_swig_python.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_SWIG_PYTHON([use-shadow-classes = {no, yes}])
-#
-# DESCRIPTION
-#
-#   Checks for Python and provides the $(AX_SWIG_PYTHON_CPPFLAGS), and
-#   $(AX_SWIG_PYTHON_OPT) output variables.
-#
-#   $(AX_SWIG_PYTHON_OPT) contains all necessary SWIG options to generate
-#   code for Python. Shadow classes are enabled unless the value of the
-#   optional first argument is exactly 'no'. If you need multi module
-#   support (provided by the AX_SWIG_MULTI_MODULE_SUPPORT macro) use
-#   $(AX_SWIG_PYTHON_LIBS) to link against the appropriate library. It
-#   contains the SWIG Python runtime library that is needed by the type
-#   check system for example.
-#
-# LICENSE
-#
-#   Copyright (c) 2008 Sebastian Huber <sebastian-huber@web.de>
-#   Copyright (c) 2008 Alan W. Irwin <irwin@beluga.phys.uvic.ca>
-#   Copyright (c) 2008 Rafael Laboissiere <rafael@laboissiere.net>
-#   Copyright (c) 2008 Andrew Collier <colliera@ukzn.ac.za>
-#
-#   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/>.
-#
-#   As a special exception, the respective Autoconf Macro's copyright owner
-#   gives unlimited permission to copy, distribute and modify the configure
-#   scripts that are the output of Autoconf when processing the Macro. You
-#   need not follow the terms of the GNU General Public License when using
-#   or distributing such scripts, even though portions of the text of the
-#   Macro appear in them. The GNU General Public License (GPL) does govern
-#   all other use of the material that constitutes the Autoconf Macro.
-#
-#   This special exception to the GPL applies to versions of the Autoconf
-#   Macro released by the Autoconf Archive. When you make and distribute a
-#   modified version of the Autoconf Macro, you may extend this special
-#   exception to the GPL to apply to your modified version as well.
-
-#serial 7
-
-AU_ALIAS([SWIG_PYTHON], [AX_SWIG_PYTHON])
-AC_DEFUN([AX_SWIG_PYTHON],[
-        AC_REQUIRE([AX_PKG_SWIG])
-        AC_REQUIRE([AX_PYTHON_DEVEL])
-        test "x$1" != "xno" || swig_shadow=" -noproxy"
-        AC_SUBST([AX_SWIG_PYTHON_OPT],[-python$swig_shadow])
-        AC_SUBST([AX_SWIG_PYTHON_CPPFLAGS],[$PYTHON_CPPFLAGS])
-])
diff --git a/man/Makefile.am b/man/Makefile.am
deleted file mode 100644
index af1277b..0000000
--- a/man/Makefile.am
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- Makefile -*-
-
-dist_man8_MANS = \
-	nl-classid-lookup.8 \
-	nl-pktloc-lookup.8 \
-	nl-qdisc-add.8 nl-qdisc-delete.8 nl-qdisc-list.8 \
-	genl-ctrl-list.8
diff --git a/python/Makefile.am b/python/Makefile.am
deleted file mode 100644
index a1a3426..0000000
--- a/python/Makefile.am
+++ /dev/null
@@ -1,3 +0,0 @@
-# -*- Makefile -*-
-
-SUBDIRS = doc examples netlink tests
diff --git a/python/doc/Makefile.am b/python/doc/Makefile.am
deleted file mode 100644
index a2fe6f2..0000000
--- a/python/doc/Makefile.am
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Makefile -*-
-
-EXTRA_DIST = \
-	conf.py \
-	core.rst \
-	index.rst \
-	route_addr.rst \
-	route.rst
diff --git a/python/examples/Makefile.am b/python/examples/Makefile.am
deleted file mode 100644
index 1cea5e2..0000000
--- a/python/examples/Makefile.am
+++ /dev/null
@@ -1,6 +0,0 @@
-# -*- Makefile -*-
-
-EXTRA_DIST = \
-	iface.py \
-	nl80211.py \
-	wiphy.py
diff --git a/python/netlink/Makefile.am b/python/netlink/Makefile.am
deleted file mode 100644
index 1f6eaf8..0000000
--- a/python/netlink/Makefile.am
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- Makefile -*-
-
-SUBDIRS = route genl
-
-EXTRA_DIST = \
-	capi.i \
-	fixes.h \
-	__init__.py \
-	core.py \
-	util.py \
-	utils.h
diff --git a/python/netlink/capi.i b/python/netlink/capi.i
index e5d8a53..98f4e33 100644
--- a/python/netlink/capi.i
+++ b/python/netlink/capi.i
@@ -9,13 +9,15 @@
 #include <netlink/attr.h>
 #include <net/if.h>
 
-#define DEBUG
+/* enable define below to get swig api debug messages */
+/*#define DEBUG*/
 #include "utils.h"
 %}
 
 %include <stdint.i>
 %include <cstring.i>
 %include <cpointer.i>
+%include <exception.i>
 
 %inline %{
         struct nl_dump_params *alloc_dump_params(void)
@@ -186,6 +188,9 @@
 extern int nl_socket_set_buffer_size(struct nl_sock *, int, int);
 extern void nl_socket_set_cb(struct nl_sock *, struct nl_cb *);
 
+extern int nl_socket_add_membership(struct nl_sock *, int);
+extern int nl_socket_drop_membership(struct nl_sock *, int);
+
 extern int nl_send_auto_complete(struct nl_sock *, struct nl_msg *);
 extern int nl_recvmsgs(struct nl_sock *, struct nl_cb *);
 
@@ -808,6 +813,19 @@
 %typemap(out) void *;
 extern int		nla_type(const struct nlattr *);
 
+%typemap(in) (int, const void *) {
+	$1 = Py_SIZE($input);
+	if (PyByteArray_Check($input)) {
+		$2 = ($2_ltype)PyByteArray_AsString($input);
+	} else if (PyString_Check($input)) {
+		$2 = ($2_ltype)PyString_AsString($input);
+	} else
+		SWIG_exception(SWIG_TypeError,
+			       "pointer must be bytearray or string.");
+}
+extern int              nla_put(struct nl_msg *, int, int, const void *);
+%typemap(in) const void *;
+
 /* Integer attribute */
 extern uint8_t		nla_get_u8(struct nlattr *);
 extern int		nla_put_u8(struct nl_msg *, int, uint8_t);
diff --git a/python/netlink/genl/Makefile.am b/python/netlink/genl/Makefile.am
deleted file mode 100644
index 9e30904..0000000
--- a/python/netlink/genl/Makefile.am
+++ /dev/null
@@ -1,5 +0,0 @@
-# -*- Makefile -*-
-
-EXTRA_DIST = \
-	capi.i \
-	__init__.py
diff --git a/python/netlink/genl/capi.i b/python/netlink/genl/capi.i
index 069e617..fbf448f 100644
--- a/python/netlink/genl/capi.i
+++ b/python/netlink/genl/capi.i
@@ -36,8 +36,16 @@
 extern int genl_family_add_op(struct genl_family *, int, int);
 extern int genl_family_add_grp(struct genl_family *, uint32_t , const char *);
 
+/* #include <linux/genetlink.h> */
+struct genlmsghdr {
+	uint8_t cmd;
+	uint8_t version;
+	uint16_t reserved;
+};
+
 /* #include <netlink/genl/genl.h> */
 extern int genl_connect(struct nl_sock *);
+extern struct genlmsghdr *genlmsg_hdr(struct nlmsghdr *);
 
 extern void *genlmsg_put(struct nl_msg *, uint32_t, uint32_t,
 			 int, int, int, uint8_t, uint8_t);
diff --git a/python/netlink/route/Makefile.am b/python/netlink/route/Makefile.am
deleted file mode 100644
index ef714f4..0000000
--- a/python/netlink/route/Makefile.am
+++ /dev/null
@@ -1,14 +0,0 @@
-# -*- Makefile -*-
-
-EXTRA_DIST = \
-	capi.i \
-	__init__.py \
-	address.py \
-	link.py \
-	tc.py \
-	links/__init__.py \
-	links/dummy.py \
-	links/inet.py \
-	links/vlan.py \
-	qdisc/__init__.py \
-	qdisc/htb.py
diff --git a/python/netlink/utils.h b/python/netlink/utils.h
index 7836c30..3ec2fb5 100644
--- a/python/netlink/utils.h
+++ b/python/netlink/utils.h
@@ -30,7 +30,7 @@
 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
 
 #define container_of(ptr, type, member) ({			\
-	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
+	const __typeof__( ((type *)0)->member ) *__mptr = (ptr);\
 	(type *)( (char *)__mptr - offsetof(type,member) );})
 
 #ifdef DEBUG
diff --git a/python/setup.py.in b/python/setup.py.in
index 346c770..0cd35d0 100644
--- a/python/setup.py.in
+++ b/python/setup.py.in
@@ -35,6 +35,10 @@
       description = 'Python wrapper for netlink protocols',
       author = 'Thomas Graf',
       author_email = 'tgraf@suug.ch',
+      url = 'http://www.infradead.org/~tgr/libnl/',
+      license = 'LGPL 2',
+      platforms = 'linux2',
+      long_description = 'Experimental python bindings for libnl',
       ext_modules = [netlink_capi, route_capi, genl_capi],
       package_dir = {'': '@srcdir@'},
       packages = ['netlink', 'netlink.genl', 'netlink.route',
diff --git a/python/tests/Makefile.am b/python/tests/Makefile.am
deleted file mode 100644
index 15f77fa..0000000
--- a/python/tests/Makefile.am
+++ /dev/null
@@ -1,5 +0,0 @@
-
-# -*- Makefile -*-
-
-EXTRA_DIST = \
-	test-create-bridge.py
diff --git a/src/.gitignore b/src/.gitignore
index 5de9f29..e53eb3d 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,18 +1,30 @@
 genl-ctrl-list
-/nf-ct-add
+idiag-socket-details
+nf-ct-add
+nf-ct-events
 nf-ct-list
-nf-exp-list
 nf-exp-add
 nf-exp-delete
+nf-exp-list
 nf-log
 nf-monitor
+nf-queue
 nl-addr-add
 nl-addr-delete
 nl-addr-list
+nl-class-add
+nl-class-delete
+nl-class-list
+nl-classid-lookup
+nl-cls-add
+nl-cls-delete
+nl-cls-list
 nl-fib-lookup
-nl-link-list
+nl-link-enslave
 nl-link-ifindex2name
+nl-link-list
 nl-link-name2ifindex
+nl-link-release
 nl-link-set
 nl-link-stats
 nl-list-caches
@@ -22,25 +34,14 @@
 nl-neigh-delete
 nl-neigh-list
 nl-neightbl-list
+nl-pktloc-lookup
 nl-qdisc-add
 nl-qdisc-delete
 nl-qdisc-list
-nl-class-add
-nl-class-delete
-nl-class-list
-nl-cls-add
-nl-cls-delete
-nl-cls-list
 nl-route-add
 nl-route-delete
-nl-route-list
 nl-route-get
+nl-route-list
 nl-rule-list
 nl-tctree-list
 nl-util-addr
-nf-queue
-nl-classid-lookup
-nl-pktloc-lookup
-nl-link-enslave
-nl-link-release
-idiag-socket-details
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644
index f8ac4ca..0000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,106 +0,0 @@
-# -*- Makefile -*-
-
-SUBDIRS = lib
-
-AM_CPPFLAGS  = -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DSYSCONFDIR=\"$(sysconfdir)/libnl\"
-AM_CFLAGS = -Wall
-
-LDADD = \
-	${top_builddir}/src/lib/libnl-cli-3.la \
-	${top_builddir}/lib/libnl-3.la \
-	${top_builddir}/lib/libnl-nf-3.la \
-	${top_builddir}/lib/libnl-genl-3.la \
-	${top_builddir}/lib/libnl-route-3.la \
-	${top_builddir}/lib/libnl-idiag-3.la
-
-sbin_PROGRAMS = \
-	genl-ctrl-list \
-	nl-qdisc-add nl-qdisc-list nl-qdisc-delete \
-	nl-class-add nl-class-list nl-class-delete \
-	nl-cls-add nl-cls-list nl-cls-delete \
-	nl-classid-lookup \
-	nl-pktloc-lookup \
-	nl-link-list
-
-noinst_PROGRAMS = \
-	nf-ct-list nf-ct-add nf-log nf-queue nf-monitor \
-	nf-exp-list nf-exp-add nf-exp-delete \
-	nl-addr-add nl-addr-delete nl-addr-list \
-	nl-link-set nl-link-stats \
-	nl-link-ifindex2name nl-link-name2ifindex \
-	nl-neigh-add nl-neigh-delete nl-neigh-list \
-	nl-rule-list \
-	nl-neightbl-list \
-	nl-monitor \
-	nl-tctree-list \
-	nl-route-add nl-route-delete nl-route-get nl-route-list \
-	nl-fib-lookup \
-	nl-list-caches nl-list-sockets \
-	nl-util-addr \
-	nl-link-enslave \
-	nl-link-release \
-	idiag-socket-details
-
-genl_ctrl_list_SOURCES = genl-ctrl-list.c 
-
-nf_ct_list_SOURCES = nf-ct-list.c 
-nf_ct_add_SOURCES = nf-ct-add.c
-nf_log_SOURCES = nf-log.c
-nf_queue_SOURCES = nf-queue.c 
-nf_monitor_SOURCES = nf-monitor.c
-
-nf_exp_list_SOURCES = nf-exp-list.c 
-nf_exp_add_SOURCES = nf-exp-add.c 
-nf_exp_delete_SOURCES = nf-exp-delete.c 
-
-nl_addr_add_SOURCES = nl-addr-add.c
-nl_addr_delete_SOURCES = nl-addr-delete.c
-nl_addr_list_SOURCES = nl-addr-list.c
-
-nl_link_list_SOURCES = nl-link-list.c
-nl_link_set_SOURCES = nl-link-set.c
-nl_link_stats_SOURCES = nl-link-stats.c
-nl_link_ifindex2name_SOURCES = nl-link-ifindex2name.c
-nl_link_name2ifindex_SOURCES = nl-link-name2ifindex.c
-
-nl_monitor_SOURCES = nl-monitor.c
-
-nl_neigh_add_SOURCES = nl-neigh-add.c
-nl_neigh_delete_SOURCES = nl-neigh-delete.c
-nl_neigh_list_SOURCES = nl-neigh-list.c
-
-nl_neightbl_list_SOURCES = nl-neightbl-list.c
-
-nl_qdisc_add_SOURCES = nl-qdisc-add.c
-nl_qdisc_delete_SOURCES = nl-qdisc-delete.c
-nl_qdisc_list_SOURCES = nl-qdisc-list.c
-
-nl_class_add_SOURCES = nl-class-add.c
-nl_class_delete_SOURCES = nl-class-delete.c
-nl_class_list_SOURCES = nl-class-list.c
-
-nl_cls_add_SOURCES = nl-cls-add.c
-nl_cls_list_SOURCES = nl-cls-list.c
-nl_cls_delete_SOURCES = nl-cls-delete.c
-
-nl_route_add_SOURCES = nl-route-add.c
-nl_route_delete_SOURCES = nl-route-delete.c
-nl_route_get_SOURCES = nl-route-get.c
-nl_route_list_SOURCES = nl-route-list.c
-
-nl_rule_list_SOURCES = nl-rule-list.c
-
-nl_tctree_list_SOURCES = nl-tctree-list.c
-
-nl_fib_lookup_SOURCES = nl-fib-lookup.c
-
-nl_list_caches_SOURCES = nl-list-caches.c
-nl_list_sockets_SOURCES = nl-list-sockets.c
-
-nl_util_addr_SOURCES = nl-util-addr.c
-
-nl_pktloc_lookup_SOURCES = nl-pktloc-lookup.c
-
-nl_classid_lookup_SOURCES = nl-classid-lookup.c
-
-idiag_socket_details_SOURCES = idiag-socket-details.c
diff --git a/src/genl-ctrl-list.c b/src/genl-ctrl-list.c
index 0895bcc..d3279a8 100644
--- a/src/genl-ctrl-list.c
+++ b/src/genl-ctrl-list.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/genl-ctrl-list.c	List Generic Netlink Families
  *
@@ -11,10 +12,12 @@
 
 #include <netlink/cli/utils.h>
 
+#include <linux/genetlink.h>
+
 static struct nl_cache *alloc_genl_family_cache(struct nl_sock *sk)
 {
 	return nl_cli_alloc_cache(sk, "generic netlink family",
-			   genl_ctrl_alloc_cache);
+				  genl_ctrl_alloc_cache);
 }
 
 static void print_usage(void)
@@ -38,11 +41,11 @@
 		.dp_type = NL_DUMP_LINE,
 		.dp_fd = stdout,
 	};
- 
+
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_GENERIC);
 	family_cache = alloc_genl_family_cache(sock);
- 
+
 	for (;;) {
 		int c, optidx = 0;
 		static struct option long_opts[] = {
@@ -52,7 +55,7 @@
 			{ "version", 0, 0, 'v' },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "df:hv", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -63,7 +66,7 @@
 		case 'h': print_usage(); break;
 		case 'v': nl_cli_print_version(); break;
 		}
- 	}
+	}
 
 	nl_cache_dump(family_cache, &params);
 
diff --git a/src/idiag-socket-details.c b/src/idiag-socket-details.c
index 9568676..2d7dd4b 100644
--- a/src/idiag-socket-details.c
+++ b/src/idiag-socket-details.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/idiag-socket-details.c     List socket details
  *
@@ -72,7 +73,7 @@
 		}
 	}
 
-	if ((err = idiagnl_msg_alloc_cache(sock, AF_INET, IDIAG_SS_ALL,
+	if ((err = idiagnl_msg_alloc_cache(sock, AF_INET, IDIAGNL_SS_ALL,
 					&idiag_cache))) {
 		nl_cli_fatal(err, "Unable to allocate idiag msg cache: %s",
 				nl_geterror(err));
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
deleted file mode 100644
index 5161115..0000000
--- a/src/lib/Makefile.am
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- Makefile -*-
-
-AM_CPPFLAGS  = -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DPKGLIBDIR=\"$(pkglibdir)\" -DSYSCONFDIR=\"$(sysconfdir)\" -rdynamic
-AM_CFLAGS = -Wall
-AM_LDFLAGS = \
-	-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
-NL_LIBADD = \
-	-L${top_builddir}/lib \
-	-ldl
-
-#nobase_pkglib_LTLIBRARIES = cls/basic.la cls/ematch/cmp.la
-#cls_basic_la_LDFLAGS = -module -version-info 2:0:0
-#cls_ematch_cmp_la_LDFLAGS = -module -version-info 2:0:0
-
-#cls/ematch_grammar.c: cls/ematch_grammar.l
-#	$(LEX) --header-file=cls/ematch_grammar.h $(LFLAGS) -o $@ $^
-
-#cls/ematch_syntax.c: cls/ematch_syntax.y
-#	$(YACC) -d $(YFLAGS) -o $@ $^
-
-#cls/pktloc_grammar.c: cls/pktloc_grammar.l
-#	$(LEX) --header-file=cls/pktloc_grammar.h $(LFLAGS) -o $@ $^
-
-#cls/pktloc_syntax.c: cls/pktloc_syntax.y
-#	$(YACC) -d $(YFLAGS) -o $@ $^
-
-#CLEANFILES = \
-#	cls/ematch_grammar.c cls/ematch_grammar.h \
-#	cls/ematch_syntax.c cls/ematch_syntax.h \
-#	cls/pktloc_grammar.c cls/pktloc_grammar.h \
-#	cls/pktloc_syntax.c cls/pktloc_syntax.h
-
-lib_LTLIBRARIES = \
-	libnl-cli-3.la
-
-libnl_cli_3_la_LIBADD  = ${top_builddir}/lib/libnl-3.la \
-		       ${top_builddir}/lib/libnl-route-3.la \
-		       ${top_builddir}/lib/libnl-nf-3.la \
-		       ${top_builddir}/lib/libnl-genl-3.la ${NL_LIBADD}
-
-libnl_cli_3_la_SOURCES = \
-	utils.c addr.c ct.c link.c neigh.c rule.c route.c \
-	tc.c qdisc.c class.c cls.c exp.c
-#	cls/ematch_syntax.c cls/ematch_grammar.c cls/ematch.c
-#	cls/pktloc_syntax.c cls/pktloc_grammar.c cls/utils.c
diff --git a/src/lib/addr.c b/src/lib/addr.c
index a9c137b..5d39f7c 100644
--- a/src/lib/addr.c
+++ b/src/lib/addr.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/lib/addr.c     Address Helpers
  *
diff --git a/src/lib/class.c b/src/lib/class.c
index 96f60cd..162e542 100644
--- a/src/lib/class.c
+++ b/src/lib/class.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/lib/class.c     CLI Class Helpers
  *
diff --git a/src/lib/cls.c b/src/lib/cls.c
index 86d775d..a5ac925 100644
--- a/src/lib/cls.c
+++ b/src/lib/cls.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/lib/cls.c     	CLI Classifier Helpers
  *
diff --git a/src/lib/ct.c b/src/lib/ct.c
index c903878..e6732ae 100644
--- a/src/lib/ct.c
+++ b/src/lib/ct.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/lib/ct.c		CLI Conntrack Helpers
  *
diff --git a/src/lib/exp.c b/src/lib/exp.c
index a7a74f5..732843f 100644
--- a/src/lib/exp.c
+++ b/src/lib/exp.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/lib/exp.c		CLI Expectation Helpers
  *
diff --git a/src/lib/link.c b/src/lib/link.c
index 5bce824..ae367e4 100644
--- a/src/lib/link.c
+++ b/src/lib/link.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/lib/link.c     CLI Link Helpers
  *
@@ -31,12 +32,14 @@
 	return link;
 }
 
-struct nl_cache *nl_cli_link_alloc_cache_family(struct nl_sock *sock, int family)
+struct nl_cache *nl_cli_link_alloc_cache_family_flags(struct nl_sock *sock,
+						      int family,
+						      unsigned int flags)
 {
 	struct nl_cache *cache;
 	int err;
 
-	if ((err = rtnl_link_alloc_cache(sock, family, &cache)) < 0)
+	if ((err = rtnl_link_alloc_cache_flags(sock, family, &cache, flags)) < 0)
 		nl_cli_fatal(err, "Unable to allocate link cache: %s",
 			     nl_geterror(err));
 
@@ -45,11 +48,22 @@
 	return cache;
 }
 
+struct nl_cache *nl_cli_link_alloc_cache_family(struct nl_sock *sock, int family)
+{
+	return nl_cli_link_alloc_cache_family_flags(sock, family, 0);
+}
+
 struct nl_cache *nl_cli_link_alloc_cache(struct nl_sock *sock)
 {
 	return nl_cli_link_alloc_cache_family(sock, AF_UNSPEC);
 }
 
+struct nl_cache *nl_cli_link_alloc_cache_flags(struct nl_sock *sock,
+						unsigned int flags)
+{
+	return nl_cli_link_alloc_cache_family_flags(sock, AF_UNSPEC, flags);
+}
+
 void nl_cli_link_parse_family(struct rtnl_link *link, char *arg)
 {
 	int family;
diff --git a/src/lib/neigh.c b/src/lib/neigh.c
index 4518e46..75862c7 100644
--- a/src/lib/neigh.c
+++ b/src/lib/neigh.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/lib/neigh.c     CLI Neighbour Helpers
  *
diff --git a/src/lib/qdisc.c b/src/lib/qdisc.c
index ccf7d26..ea047c2 100644
--- a/src/lib/qdisc.c
+++ b/src/lib/qdisc.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/lib/qdisc.c     CLI QDisc Helpers
  *
diff --git a/src/lib/route.c b/src/lib/route.c
index cd3e897..9d0fbe8 100644
--- a/src/lib/route.c
+++ b/src/lib/route.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/lib/route.c     CLI Route Helpers
  *
@@ -141,11 +142,13 @@
 		NH_DEV,
 		NH_VIA,
 		NH_WEIGHT,
+		NH_AS,
 	};
 	static char *const tokens[] = {
 		"dev",
 		"via",
 		"weight",
+		"as",
 		NULL,
 	};
 	struct rtnl_nexthop *nh;
@@ -175,8 +178,20 @@
 			break;
 
 		case NH_VIA:
-			addr = nl_cli_addr_parse(arg,rtnl_route_get_family(route));
-			rtnl_route_nh_set_gateway(nh, addr);
+			if (rtnl_route_get_family(route) == AF_MPLS) {
+				addr = nl_cli_addr_parse(arg, 0);
+				rtnl_route_nh_set_via(nh, addr);
+			} else {
+				addr = nl_cli_addr_parse(arg,rtnl_route_get_family(route));
+				rtnl_route_nh_set_gateway(nh, addr);
+			}
+			nl_addr_put(addr);
+			break;
+
+		case NH_AS:
+			addr = nl_cli_addr_parse(arg,
+						 rtnl_route_get_family(route));
+			rtnl_route_nh_set_newdst(nh, addr);
 			nl_addr_put(addr);
 			break;
 
diff --git a/src/lib/rule.c b/src/lib/rule.c
index 96f1d4c..213eca2 100644
--- a/src/lib/rule.c
+++ b/src/lib/rule.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/lib/rule.c     CLI Routing Rule Helpers
  *
diff --git a/src/lib/tc.c b/src/lib/tc.c
index dde729f..5d3a203 100644
--- a/src/lib/tc.c
+++ b/src/lib/tc.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/lib/tc.c     CLI Traffic Control Helpers
  *
diff --git a/src/lib/utils.c b/src/lib/utils.c
index e5eacde..3aa2a90 100644
--- a/src/lib/utils.c
+++ b/src/lib/utils.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/utils.c		Utilities
  *
@@ -22,6 +23,13 @@
  */
 
 #include <netlink/cli/utils.h>
+#include <locale.h>
+
+#include "lib/defs.h"
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
 
 /**
  * Parse a text based 32 bit unsigned integer argument
@@ -70,7 +78,6 @@
 void nl_cli_fatal(int err, const char *fmt, ...)
 {
 	va_list ap;
-	char buf[256];
 
 	fprintf(stderr, "Error: ");
 
@@ -79,8 +86,28 @@
 		vfprintf(stderr, fmt, ap);
 		va_end(ap);
 		fprintf(stderr, "\n");
-	} else
-		fprintf(stderr, "%s\n", strerror_r(err, buf, sizeof(buf)));
+	} else {
+		char *buf;
+#ifdef HAVE_STRERROR_L
+		locale_t loc = newlocale(LC_MESSAGES_MASK, "", (locale_t)0);
+		if (loc == (locale_t)0) {
+			if (errno == ENOENT)
+				loc = newlocale(LC_MESSAGES_MASK,
+						"POSIX", (locale_t)0);
+			if (loc == (locale_t)0)
+				buf = "newlocale() failed";
+		}
+		if (loc != (locale_t)0)
+			buf = strerror_l(err, loc);
+#else
+		buf = strerror(err);
+#endif
+		fprintf(stderr, "%s\n", buf);
+#ifdef HAVE_STRERROR_L
+		if (loc != (locale_t)0)
+			freelocale(loc);
+#endif
+	}
 
 	exit(abs(err));
 }
@@ -153,6 +180,7 @@
 		switch ((answer = tolower(buf[0]))) {
 		case '\n':
 			answer = default_yes ? 'y' : 'n';
+			/* fall through */
 		case 'y':
 		case 'n':
 			return answer == 'y';
@@ -180,17 +208,43 @@
 	return cache;
 }
 
+struct nl_cache *nl_cli_alloc_cache_flags(struct nl_sock *sock,
+			    const char *name, unsigned int flags,
+			    int (*ac)(struct nl_sock *, struct nl_cache **,
+				      unsigned int))
+{
+	struct nl_cache *cache;
+	int err;
+
+	if ((err = ac(sock, &cache, flags)) < 0)
+		nl_cli_fatal(err, "Unable to allocate %s cache: %s",
+			     name, nl_geterror(err));
+
+	nl_cache_mngt_provide(cache);
+
+	return cache;
+}
+
 void nl_cli_load_module(const char *prefix, const char *name)
 {
 	char path[FILENAME_MAX+1];
-	void *handle;
 
 	snprintf(path, sizeof(path), "%s/%s/%s.so",
 		 PKGLIBDIR, prefix, name);
 
-	if (!(handle = dlopen(path, RTLD_NOW)))
-		nl_cli_fatal(ENOENT, "Unable to load module \"%s\": %s\n",
-			path, dlerror());
+#ifdef HAVE_DLFCN_H
+	{
+		void *handle;
+
+		if (!(handle = dlopen(path, RTLD_NOW))) {
+			nl_cli_fatal(ENOENT, "Unable to load module \"%s\": %s\n",
+			             path, dlerror());
+		}
+	}
+#else
+	nl_cli_fatal(ENOTSUP, "Unable to load module \"%s\": built without dynamic libraries support\n",
+	             path);
+#endif
 }
 
 /** @} */
diff --git a/src/nf-ct-add.c b/src/nf-ct-add.c
index 8ad4c53..eec9b86 100644
--- a/src/nf-ct-add.c
+++ b/src/nf-ct-add.c
@@ -1,5 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
- * src/nf-ct-list.c     List Conntrack Entries
+ * src/nf-ct-add.c     Add Conntrack Entry
  *
  *	This library is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU Lesser General Public
@@ -14,6 +15,8 @@
 #include <netlink/cli/utils.h>
 #include <netlink/cli/ct.h>
 
+#include <linux/rtnetlink.h>
+
 static int quiet = 0;
 
 static void print_usage(void)
diff --git a/src/nf-ct-events.c b/src/nf-ct-events.c
new file mode 100644
index 0000000..87f2da9
--- /dev/null
+++ b/src/nf-ct-events.c
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/*
+ * src/nf-ct-events.c	  Listen on Conntrack Events
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2018 Avast software
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/ct.h>
+
+#include <linux/netlink.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_conntrack.h>
+
+struct private_nl_object
+{
+	int			ce_refcnt;
+	struct nl_object_ops *	ce_ops;
+	struct nl_cache *	ce_cache;
+	struct nl_list_head	ce_list;
+	int			ce_msgtype;
+	int			ce_flags;
+	uint64_t		ce_mask;
+};
+
+static void nf_conntrack_parse_callback(struct nl_object *obj, void *opaque)
+{
+	struct nl_dump_params params = {
+		.dp_fd = stdout,
+		.dp_type = NL_DUMP_DETAILS,
+	};
+
+	nl_object_dump(obj, &params);
+}
+
+static int nf_conntrack_event_callback(struct nl_msg *msg, void *opaque)
+{
+	int err;
+	struct nlmsghdr *hdr = nlmsg_hdr(msg);
+
+	enum cntl_msg_types type = (enum cntl_msg_types) NFNL_MSG_TYPE(hdr->nlmsg_type);
+
+	int flags = hdr->nlmsg_flags;
+
+	if (type == IPCTNL_MSG_CT_DELETE) {
+		printf("DELETE ");
+	} else if (type == IPCTNL_MSG_CT_NEW) {
+		if (flags & (NLM_F_CREATE|NLM_F_EXCL)) {
+			printf("NEW ");
+		} else {
+			printf("UPDATE ");
+		}
+	} else {
+		printf("UNKNOWN ");
+	}
+
+	if ((err = nl_msg_parse(msg, &nf_conntrack_parse_callback, opaque)) < 0) {
+		nl_cli_fatal(err, "nl_msg_parse: %s", nl_geterror(err));
+	}
+	/* Continue with next event */
+	return NL_OK;
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *socket;
+	int err;
+
+	socket = nl_cli_alloc_socket();
+	if (socket == NULL) {
+		nl_cli_fatal(ENOBUFS, "Unable to allocate netlink socket");
+	}
+
+	/*
+	 * Disable sequence number checking.
+	 * This is required to allow messages to be processed which were not requested by
+	 * a preceding request message, e.g. netlink events.
+	 */
+	nl_socket_disable_seq_check(socket);
+
+	/* subscribe conntrack events */
+	nl_join_groups(socket, NF_NETLINK_CONNTRACK_NEW |
+												 NF_NETLINK_CONNTRACK_UPDATE |
+												 NF_NETLINK_CONNTRACK_DESTROY |
+												 NF_NETLINK_CONNTRACK_EXP_NEW |
+												 NF_NETLINK_CONNTRACK_EXP_UPDATE |
+												 NF_NETLINK_CONNTRACK_EXP_DESTROY);
+
+	nl_cli_connect(socket, NETLINK_NETFILTER);
+
+	nl_socket_modify_cb(socket, NL_CB_VALID, NL_CB_CUSTOM, &nf_conntrack_event_callback, 0);
+
+	while (1) {
+
+		errno = 0;
+		if ((err = nl_recvmsgs_default(socket)) < 0) {
+			switch (errno) {
+				case 	ENOBUFS:
+					// just print warning
+					fprintf(stderr, "Lost events because of ENOBUFS\n");
+					break;
+				case EAGAIN:
+				case EINTR:
+					// continue reading
+					break;
+				default:
+					nl_cli_fatal(err, "Failed to receive: %s", nl_geterror(err));
+			}
+		}
+	}
+}
diff --git a/src/nf-ct-list.c b/src/nf-ct-list.c
index 5f72998..c512027 100644
--- a/src/nf-ct-list.c
+++ b/src/nf-ct-list.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nf-ct-list.c     List Conntrack Entries
  *
@@ -14,6 +15,8 @@
 #include <netlink/cli/utils.h>
 #include <netlink/cli/ct.h>
 
+#include <linux/netlink.h>
+
 static void print_usage(void)
 {
 	printf(
@@ -54,9 +57,9 @@
 		.dp_type = NL_DUMP_LINE,
 		.dp_fd = stdout,
 	};
- 
- 	ct = nl_cli_ct_alloc();
- 
+
+	ct = nl_cli_ct_alloc();
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -95,7 +98,7 @@
 			{ "refcnt", 1, 0, ARG_REFCNT },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -124,7 +127,7 @@
 		case ARG_REFCNT: nl_cli_ct_parse_use(ct, optarg); break;
 		case ARG_FLAGS: nl_cli_ct_parse_status(ct, optarg); break;
 		}
- 	}
+	}
 
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_NETFILTER);
diff --git a/src/nf-exp-add.c b/src/nf-exp-add.c
index 4b7f9d9..1f71cd5 100644
--- a/src/nf-exp-add.c
+++ b/src/nf-exp-add.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nf-exp-add.c     Create an expectation
  *
@@ -16,6 +17,8 @@
 #include <netlink/cli/utils.h>
 #include <netlink/cli/exp.h>
 
+#include <linux/netlink.h>
+
 static int quiet = 0;
 
 static void print_usage(void)
@@ -64,8 +67,8 @@
 	};
 	int err, nlflags = NLM_F_CREATE;
 
- 	exp = nl_cli_exp_alloc();
- 
+	exp = nl_cli_exp_alloc();
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -86,12 +89,12 @@
 			ARG_MASK_SPORT,
 			ARG_MASK_DST,
 			ARG_MASK_DPORT,
-            ARG_NAT_PROTO,
-            ARG_NAT_SRC,
-            ARG_NAT_SPORT,
-            ARG_NAT_DST,
-            ARG_NAT_DPORT,
-            ARG_NAT_DIR,
+			ARG_NAT_PROTO,
+			ARG_NAT_SRC,
+			ARG_NAT_SPORT,
+			ARG_NAT_DST,
+			ARG_NAT_DPORT,
+			ARG_NAT_DIR,
 			ARG_TIMEOUT,
 			ARG_HELPER_NAME,
 			ARG_REPLACE,
@@ -118,19 +121,19 @@
 			{ "mask-sport", 1, 0, ARG_MASK_SPORT },
 			{ "mask-dst", 1, 0, ARG_MASK_DST },
 			{ "mask-dport", 1, 0, ARG_MASK_DPORT },
-            { "nat-proto", 1, 0, ARG_NAT_PROTO },
-            { "nat-src", 1, 0, ARG_NAT_SRC },
-            { "nat-sport", 1, 0, ARG_NAT_SPORT },
-            { "nat-dst", 1, 0, ARG_NAT_DST },
-            { "nat-dport", 1, 0, ARG_NAT_DPORT },
-            { "nat-dir", 1, 0, ARG_NAT_DIR },
+			{ "nat-proto", 1, 0, ARG_NAT_PROTO },
+			{ "nat-src", 1, 0, ARG_NAT_SRC },
+			{ "nat-sport", 1, 0, ARG_NAT_SPORT },
+			{ "nat-dst", 1, 0, ARG_NAT_DST },
+			{ "nat-dport", 1, 0, ARG_NAT_DPORT },
+			{ "nat-dir", 1, 0, ARG_NAT_DIR },
 			{ "family", 1, 0, 'F' },
 			{ "timeout", 1, 0, ARG_TIMEOUT },
 			{ "helper", 1, 0, ARG_HELPER_NAME },
 			{ "flags", 1, 0, ARG_FLAGS},
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -159,18 +162,18 @@
 		case ARG_MASK_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
 		case ARG_MASK_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
 		case ARG_MASK_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
-        case ARG_NAT_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
-        case ARG_NAT_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
-        case ARG_NAT_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
-        case ARG_NAT_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
-        case ARG_NAT_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
-        case ARG_NAT_DIR: nl_cli_exp_parse_nat_dir(exp, optarg); break;
+		case ARG_NAT_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
+		case ARG_NAT_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
+		case ARG_NAT_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
+		case ARG_NAT_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
+		case ARG_NAT_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
+		case ARG_NAT_DIR: nl_cli_exp_parse_nat_dir(exp, optarg); break;
 		case 'F': nl_cli_exp_parse_family(exp, optarg); break;
 		case ARG_TIMEOUT: nl_cli_exp_parse_timeout(exp, optarg); break;
 		case ARG_HELPER_NAME: nl_cli_exp_parse_helper_name(exp, optarg); break;
 		case ARG_FLAGS: nl_cli_exp_parse_flags(exp, optarg); break;
 		}
- 	}
+	}
 
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_NETFILTER);
diff --git a/src/nf-exp-delete.c b/src/nf-exp-delete.c
index 2ec45ae..c6e478c 100644
--- a/src/nf-exp-delete.c
+++ b/src/nf-exp-delete.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nf-exp-delete.c     Delete an expectation
  *
@@ -15,6 +16,8 @@
 #include <netlink/cli/utils.h>
 #include <netlink/cli/exp.h>
 
+#include <linux/rtnetlink.h>
+
 static int quiet = 0;
 
 static void print_usage(void)
@@ -63,8 +66,8 @@
 	};
 	int err, nlflags = 0;
 
- 	exp = nl_cli_exp_alloc();
- 
+	exp = nl_cli_exp_alloc();
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -80,17 +83,17 @@
 			ARG_MASTER_SPORT,
 			ARG_MASTER_DST,
 			ARG_MASTER_DPORT,
- 			ARG_MASK_PROTO,
- 			ARG_MASK_SRC,
+			ARG_MASK_PROTO,
+			ARG_MASK_SRC,
 			ARG_MASK_SPORT,
 			ARG_MASK_DST,
 			ARG_MASK_DPORT,
 			ARG_TIMEOUT,
- 			ARG_HELPER_NAME,
+			ARG_HELPER_NAME,
 			ARG_FLAGS,
 		};
 		static struct option long_opts[] = {
- 			{ "quiet", 0, 0, 'q' },
+			{ "quiet", 0, 0, 'q' },
 			{ "help", 0, 0, 'h' },
 			{ "version", 0, 0, 'v' },
 			{ "id", 1, 0, 'i' },
@@ -105,17 +108,17 @@
 			{ "master-dst", 1, 0, ARG_MASTER_DST },
 			{ "master-dport", 1, 0, ARG_MASTER_DPORT },
 			{ "mask-proto", 1, 0, ARG_MASK_PROTO },
- 			{ "mask-src", 1, 0, ARG_MASK_SRC },
- 			{ "mask-sport", 1, 0, ARG_MASK_SPORT },
- 			{ "mask-dst", 1, 0, ARG_MASK_DST },
- 			{ "mask-dport", 1, 0, ARG_MASK_DPORT },
+			{ "mask-src", 1, 0, ARG_MASK_SRC },
+			{ "mask-sport", 1, 0, ARG_MASK_SPORT },
+			{ "mask-dst", 1, 0, ARG_MASK_DST },
+			{ "mask-dport", 1, 0, ARG_MASK_DPORT },
 			{ "family", 1, 0, 'F' },
 			{ "timeout", 1, 0, ARG_TIMEOUT },
 			{ "helper", 1, 0, ARG_HELPER_NAME },
- 			{ "flags", 1, 0, ARG_FLAGS},
+			{ "flags", 1, 0, ARG_FLAGS},
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -142,13 +145,13 @@
 		case ARG_MASK_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
 		case ARG_MASK_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
 		case ARG_MASK_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
- 		case ARG_MASK_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
+		case ARG_MASK_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
 		case 'F': nl_cli_exp_parse_family(exp, optarg); break;
 		case ARG_TIMEOUT: nl_cli_exp_parse_timeout(exp, optarg); break;
 		case ARG_HELPER_NAME: nl_cli_exp_parse_helper_name(exp, optarg); break;
 		case ARG_FLAGS: nl_cli_exp_parse_flags(exp, optarg); break;
 		}
- 	}
+	}
 
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_NETFILTER);
diff --git a/src/nf-exp-list.c b/src/nf-exp-list.c
index 1c6ec69..0993a98 100644
--- a/src/nf-exp-list.c
+++ b/src/nf-exp-list.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nf-exp-list.c     List Expectation Entries
  *
@@ -15,6 +16,8 @@
 #include <netlink/cli/utils.h>
 #include <netlink/cli/exp.h>
 
+#include <linux/netlink.h>
+
 static void print_usage(void)
 {
 	printf(
@@ -54,9 +57,9 @@
 		.dp_type = NL_DUMP_LINE,
 		.dp_fd = stdout,
 	};
- 
- 	exp = nl_cli_exp_alloc();
- 
+
+	exp = nl_cli_exp_alloc();
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -73,7 +76,7 @@
 			ARG_MASTER_DST,
 			ARG_MASTER_DPORT,
 			ARG_TIMEOUT,
- 			ARG_HELPER_NAME,
+			ARG_HELPER_NAME,
 			ARG_FLAGS,
 		};
 		static struct option long_opts[] = {
@@ -97,7 +100,7 @@
 			{ "flags", 1, 0, ARG_FLAGS},
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -125,7 +128,7 @@
 		case ARG_HELPER_NAME: nl_cli_exp_parse_helper_name(exp, optarg); break;
 		case ARG_FLAGS: nl_cli_exp_parse_flags(exp, optarg); break;
 		}
- 	}
+	}
 
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_NETFILTER);
diff --git a/src/nf-log.c b/src/nf-log.c
index 913ba16..c8a40bf 100644
--- a/src/nf-log.c
+++ b/src/nf-log.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nf-log.c     Monitor netfilter log events
  *
@@ -13,10 +14,12 @@
 
 #include <netlink/cli/utils.h>
 #include <netlink/cli/link.h>
-#include <linux/netfilter/nfnetlink_log.h>
 #include <netlink/netfilter/nfnl.h>
 #include <netlink/netfilter/log.h>
 
+#include <linux/netfilter/nfnetlink_log.h>
+#include <linux/netlink.h>
+
 static struct nfnl_log *alloc_log(void)
 {
 	struct nfnl_log *log;
@@ -52,9 +55,8 @@
 {
 	struct nl_sock *nf_sock;
 	struct nl_sock *rt_sock;
-        struct nl_cache *link_cache;
 	struct nfnl_log *log;
-	enum nfnl_log_copy_mode copy_mode;
+	int copy_mode;
 	uint32_t copy_range;
 	int err;
 	int family;
@@ -116,7 +118,7 @@
 
 	rt_sock = nl_cli_alloc_socket();
 	nl_cli_connect(rt_sock, NETLINK_ROUTE);
-	link_cache = nl_cli_link_alloc_cache(rt_sock);
+	nl_cli_link_alloc_cache(rt_sock);
 
 	while (1) {
 		fd_set rfds;
diff --git a/src/nf-monitor.c b/src/nf-monitor.c
index fe99af4..4afbdb2 100644
--- a/src/nf-monitor.c
+++ b/src/nf-monitor.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nf-monitor.c     Monitor netfilter events
  *
@@ -14,6 +15,9 @@
 #include <netlink/cli/utils.h>
 #include <netlink/netfilter/nfnl.h>
 
+#include <linux/netlink.h>
+#include <linux/netfilter/nfnetlink.h>
+
 static void obj_input(struct nl_object *obj, void *arg)
 {
 	struct nl_dump_params dp = {
diff --git a/src/nf-queue.c b/src/nf-queue.c
index 922d9c8..f46abc2 100644
--- a/src/nf-queue.c
+++ b/src/nf-queue.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nf-queue.c     Monitor netfilter queue events
  *
@@ -20,6 +21,8 @@
 #include <netlink/netfilter/queue.h>
 #include <netlink/netfilter/queue_msg.h>
 
+#include <linux/netlink.h>
+
 static struct nl_sock *nf_sock;
 
 static struct nfnl_queue *alloc_queue(void)
@@ -60,9 +63,8 @@
 int main(int argc, char *argv[])
 {
 	struct nl_sock *rt_sock;
-	struct nl_cache *link_cache;
 	struct nfnl_queue *queue;
-	enum nfnl_queue_copy_mode copy_mode;
+	int copy_mode;
 	uint32_t copy_range;
 	int err = 1;
 	int family;
@@ -116,7 +118,7 @@
 
 	rt_sock = nl_cli_alloc_socket();
 	nl_cli_connect(rt_sock, NETLINK_ROUTE);
-	link_cache = nl_cli_link_alloc_cache(rt_sock);
+	nl_cli_link_alloc_cache(rt_sock);
 
 	nl_socket_set_buffer_size(nf_sock, 1024*127, 1024*127);
 
diff --git a/src/nl-addr-add.c b/src/nl-addr-add.c
index 52995ec..e6ebefe 100644
--- a/src/nl-addr-add.c
+++ b/src/nl-addr-add.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-addr-add.c     Add addresses
  *
@@ -12,6 +13,8 @@
 #include <netlink/cli/addr.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static int quiet = 0;
 
 static void print_usage(void)
@@ -50,12 +53,12 @@
 		.dp_fd = stdout,
 	};
 	int err, nlflags = NLM_F_CREATE;
- 
+
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
 	link_cache = nl_cli_link_alloc_cache(sock);
- 	addr = nl_cli_addr_alloc();
- 
+	addr = nl_cli_addr_alloc();
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -84,7 +87,7 @@
 			{ "valid", 1, 0, ARG_VALID },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "qhva:d:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -105,7 +108,7 @@
 		case ARG_PREFERRED: nl_cli_addr_parse_preferred(addr, optarg); break;
 		case ARG_VALID: nl_cli_addr_parse_valid(addr, optarg); break;
 		}
- 	}
+	}
 
 	if ((err = rtnl_addr_add(sock, addr, nlflags)) < 0)
 		nl_cli_fatal(err, "Unable to add address: %s",
@@ -114,7 +117,7 @@
 	if (!quiet) {
 		printf("Added ");
 		nl_object_dump(OBJ_CAST(addr), &dp);
- 	}
+	}
 
 	return 0;
 }
diff --git a/src/nl-addr-delete.c b/src/nl-addr-delete.c
index 2849c01..9d017f4 100644
--- a/src/nl-addr-delete.c
+++ b/src/nl-addr-delete.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-addr-delete.c     Delete addresses
  *
@@ -12,6 +13,8 @@
 #include <netlink/cli/addr.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static struct nl_sock *sock;
 static int interactive = 0, default_yes = 0, quiet = 0;
 static int deleted = 0;
@@ -108,7 +111,7 @@
 			{ "valid", 1, 0, ARG_VALID },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "iqhva:d:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -129,7 +132,7 @@
 		case ARG_PREFERRED: nl_cli_addr_parse_preferred(addr, optarg); break;
 		case ARG_VALID: nl_cli_addr_parse_valid(addr, optarg); break;
 		}
- 	}
+	}
 
 	nl_cache_foreach_filter(addr_cache, OBJ_CAST(addr), delete_cb, NULL);
 
diff --git a/src/nl-addr-list.c b/src/nl-addr-list.c
index 20995a8..c5258bd 100644
--- a/src/nl-addr-list.c
+++ b/src/nl-addr-list.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-addr-list.c     List addresses
  *
@@ -12,6 +13,8 @@
 #include <netlink/cli/addr.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static void print_usage(void)
 {
 	printf(
diff --git a/src/nl-class-add.c b/src/nl-class-add.c
index b9a17dc..a1ccf4e 100644
--- a/src/nl-class-add.c
+++ b/src/nl-class-add.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-class-add.c     Add/Update/Replace Traffic Class
  *
@@ -17,6 +18,8 @@
 
 #include <netlink-private/route/tc-api.h>
 
+#include <linux/netlink.h>
+
 static int quiet = 0;
 
 static void print_usage(void)
@@ -62,15 +65,15 @@
 	struct rtnl_tc_ops *ops;
 	int err, flags = NLM_F_CREATE | NLM_F_EXCL;
 	char *kind, *id = NULL;
- 
+
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
 
 	link_cache = nl_cli_link_alloc_cache(sock);
 
- 	class = nl_cli_class_alloc();
+	class = nl_cli_class_alloc();
 	tc = (struct rtnl_tc *) class;
- 
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -96,7 +99,7 @@
 			{ "linktype", 1, 0, ARG_LINKTYPE },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "+qhvd:p:i:",
 				long_opts, &optidx);
 		if (c == -1)
@@ -116,7 +119,7 @@
 		case ARG_OVERHEAD: nl_cli_tc_parse_overhead(tc, optarg); break;
 		case ARG_LINKTYPE: nl_cli_tc_parse_linktype(tc, optarg); break;
 		}
- 	}
+	}
 
 	if (optind >= argc)
 		print_usage();
@@ -146,7 +149,7 @@
 	if (!quiet) {
 		printf("Adding ");
 		nl_object_dump(OBJ_CAST(class), &dp);
- 	}
+	}
 
 	if ((err = rtnl_class_add(sock, class, flags)) < 0)
 		nl_cli_fatal(EINVAL, "Unable to add class: %s", nl_geterror(err));
diff --git a/src/nl-class-delete.c b/src/nl-class-delete.c
index 37657a4..5627821 100644
--- a/src/nl-class-delete.c
+++ b/src/nl-class-delete.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-class-delete.c     Delete Traffic Classes
  *
@@ -13,6 +14,8 @@
 #include <netlink/cli/class.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0;
 static struct nl_sock *sock;
 
@@ -70,13 +73,13 @@
 	struct rtnl_class *class;
 	struct rtnl_tc *tc;
 	struct nl_cache *link_cache, *class_cache;
- 
+
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
 	link_cache = nl_cli_link_alloc_cache(sock);
- 	class = nl_cli_class_alloc();
+	class = nl_cli_class_alloc();
 	tc = (struct rtnl_tc *) class;
- 
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -95,7 +98,7 @@
 			{ "kind", 1, 0, 'k' },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "qhvd:p:i:k:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -112,7 +115,7 @@
 		case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break;
 		case 'k': nl_cli_tc_parse_kind(tc, optarg); break;
 		}
- 	}
+	}
 
 	if (!rtnl_tc_get_ifindex(tc))
 		nl_cli_fatal(EINVAL, "You must specify a network device (--dev=XXX)");
diff --git a/src/nl-class-list.c b/src/nl-class-list.c
index c2423fb..0ce4ab2 100644
--- a/src/nl-class-list.c
+++ b/src/nl-class-list.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-class-list.c     List Traffic Classes
  *
@@ -14,6 +15,8 @@
 #include <netlink/cli/class.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static struct nl_sock *sock;
 
 static struct nl_dump_params params = {
@@ -65,15 +68,15 @@
 	struct rtnl_tc *tc;
 	struct nl_cache *link_cache;
 	int ifindex;
- 
+
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
 	link_cache = nl_cli_link_alloc_cache(sock);
- 	class = nl_cli_class_alloc();
+	class = nl_cli_class_alloc();
 	tc = (struct rtnl_tc *) class;
 
 	params.dp_fd = stdout;
- 
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -91,7 +94,7 @@
 			{ "kind", 1, 0, 'k' },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "hvd:p:i:k:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -106,11 +109,11 @@
 		case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break;
 		case 'k': nl_cli_tc_parse_kind(tc, optarg); break;
 		}
- 	}
+	}
 
 	if ((ifindex = rtnl_tc_get_ifindex(tc)))
 		__dump_class(ifindex, class);
-	 else
+	else
 		nl_cache_foreach(link_cache, dump_class, class);
 
 	return 0;
diff --git a/src/nl-classid-lookup.c b/src/nl-classid-lookup.c
index 1d45d0b..4ddc842 100644
--- a/src/nl-classid-lookup.c
+++ b/src/nl-classid-lookup.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-classid-lookup.c     Lookup classid
  *
@@ -10,6 +11,7 @@
  */
 
 #include <netlink/cli/utils.h>
+#include <linux/pkt_sched.h>
 
 static void print_usage(void)
 {
@@ -48,7 +50,7 @@
 			{ "raw", 0, 0, ARG_RAW },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "hvr", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -59,7 +61,7 @@
 		case 'r': reverse = 1; break;
 		case ARG_RAW: raw = 1; break;
 		}
- 	}
+	}
 
 	if (optind >= argc)
 		print_usage();
diff --git a/src/nl-cls-add.c b/src/nl-cls-add.c
index 6acb320..c2ad717 100644
--- a/src/nl-cls-add.c
+++ b/src/nl-cls-add.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-cls-add.c     Add classifier
  *
@@ -15,6 +16,8 @@
 
 #include <netlink-private/route/tc-api.h>
 
+#include <linux/netlink.h>
+
 static int quiet = 0;
 
 static void print_usage(void)
@@ -31,7 +34,7 @@
 " -d, --dev=DEV             Network device the classifier should be attached to.\n"
 " -i, --id=ID               ID of new classifier (default: auto-generated)\n"
 " -p, --parent=ID           ID of parent { root | ingress | class-ID }\n"
-"     --protocol=PROTO      Protocol to match (default: all)\n"
+"     --proto=PROTO         Protocol to match (default: all)\n"
 "     --prio=PRIO           Priority (default: 0)\n"
 "     --mtu=SIZE            Overwrite MTU (default: MTU of network device)\n"
 "     --mpu=SIZE            Minimum packet size on the link (default: 0).\n"
@@ -62,15 +65,15 @@
 	struct rtnl_tc_ops *ops;
 	int err, flags = NLM_F_CREATE | NLM_F_EXCL;
 	char *kind, *id = NULL;
- 
+
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
 
 	link_cache = nl_cli_link_alloc_cache(sock);
 
- 	cls = nl_cli_cls_alloc();
+	cls = nl_cli_cls_alloc();
 	tc = (struct rtnl_tc *) cls;
- 
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -100,7 +103,7 @@
 			{ "linktype", 1, 0, ARG_LINKTYPE },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "+qhvd:p:i:",
 				long_opts, &optidx);
 		if (c == -1)
@@ -124,7 +127,7 @@
 			rtnl_cls_set_prio(cls, nl_cli_parse_u32(optarg));
 			break;
 		}
- 	}
+	}
 
 	if (optind >= argc)
 		print_usage();
@@ -154,7 +157,7 @@
 	if (!quiet) {
 		printf("Adding ");
 		nl_object_dump(OBJ_CAST(cls), &dp);
- 	}
+	}
 
 	if ((err = rtnl_cls_add(sock, cls, flags)) < 0)
 		nl_cli_fatal(EINVAL, "Unable to add classifier: %s", nl_geterror(err));
diff --git a/src/nl-cls-delete.c b/src/nl-cls-delete.c
index 2b3db1f..a2a93a7 100644
--- a/src/nl-cls-delete.c
+++ b/src/nl-cls-delete.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-cls-delete.c     Delete Classifier
  *
@@ -13,6 +14,8 @@
 #include <netlink/cli/cls.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0;
 static struct nl_sock *sock;
 
@@ -32,7 +35,7 @@
 " -p, --parent=ID           Identifier of parent qdisc/class.\n"
 " -i, --id=ID               Identifier\n"
 " -k, --kind=NAME           Kind of classifier (e.g. basic, u32, fw)\n"
-"     --protocol=PROTO      Protocol to match (default: all)\n"
+"     --proto=PROTO         Protocol to match (default: all)\n"
 "     --prio=PRIO           Priority (default: 0)\n"
 "\n"
 "EXAMPLE\n"
@@ -91,13 +94,13 @@
 	struct rtnl_tc *tc;
 	struct nl_cache *link_cache;
 	int ifindex;
- 
+
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
 	link_cache = nl_cli_link_alloc_cache(sock);
- 	cls = nl_cli_cls_alloc();
+	cls = nl_cli_cls_alloc();
 	tc = (struct rtnl_tc *) cls;
- 
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -120,7 +123,7 @@
 			{ "prio", 1, 0, ARG_PRIO },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "qhvd:p:i:k:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -141,11 +144,11 @@
 			rtnl_cls_set_prio(cls, nl_cli_parse_u32(optarg));
 			break;
 		}
- 	}
+	}
 
 	if ((ifindex = rtnl_tc_get_ifindex(tc)))
 		__delete_link(ifindex, cls);
-	 else
+	else
 		nl_cache_foreach(link_cache, delete_link, cls);
 
 	if (!quiet)
diff --git a/src/nl-cls-list.c b/src/nl-cls-list.c
index 5072585..5d4faa0 100644
--- a/src/nl-cls-list.c
+++ b/src/nl-cls-list.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-cls-list.c     	List classifiers
  *
@@ -14,6 +15,8 @@
 #include <netlink/cli/cls.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static struct nl_sock *sock;
 
 static struct nl_dump_params params = {
@@ -35,7 +38,7 @@
 " -p, --parent=ID           Identifier of parent class.\n"
 " -i, --id=ID               Identifier.\n"
 " -k, --kind=NAME           Kind of classifier (e.g. basic, u32, fw)\n"
-"     --protocol=PROTO      Protocol to match (default: all)\n"
+"     --proto=PROTO         Protocol to match (default: all)\n"
 "     --prio=PRIO           Priority (default: 0)\n"
 "\n"
 "EXAMPLE\n"
@@ -69,15 +72,15 @@
 	struct rtnl_tc *tc;
 	struct nl_cache *link_cache;
 	int ifindex;
- 
+
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
 	link_cache = nl_cli_link_alloc_cache(sock);
- 	cls = nl_cli_cls_alloc();
+	cls = nl_cli_cls_alloc();
 	tc = (struct rtnl_tc *) cls;
 
 	params.dp_fd = stdout;
- 
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -99,7 +102,7 @@
 			{ "prio", 1, 0, ARG_PRIO },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "hvd:p:i:k:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -118,11 +121,11 @@
 			rtnl_cls_set_prio(cls, nl_cli_parse_u32(optarg));
 			break;
 		}
- 	}
+	}
 
 	if ((ifindex = rtnl_tc_get_ifindex(tc)))
 		__dump_link(ifindex, cls);
-	 else
+	else
 		nl_cache_foreach(link_cache, dump_link, cls);
 
 	return 0;
diff --git a/src/nl-fib-lookup.c b/src/nl-fib-lookup.c
index 705cf32..a649687 100644
--- a/src/nl-fib-lookup.c
+++ b/src/nl-fib-lookup.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-fib-lookup.c		FIB Route Lookup
  *
@@ -11,6 +12,8 @@
 
 #include <netlink/cli/utils.h>
 
+#include <linux/rtnetlink.h>
+
 static void print_usage(void)
 {
 	printf(
diff --git a/src/nl-link-enslave.c b/src/nl-link-enslave.c
index 2b5d47d..4e368c3 100644
--- a/src/nl-link-enslave.c
+++ b/src/nl-link-enslave.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-link-enslave.c     Enslave a link
  *
@@ -13,6 +14,8 @@
 #include <netlink/cli/link.h>
 #include <netlink/route/link/bonding.h>
 
+#include <linux/netlink.h>
+
 int main(int argc, char *argv[])
 {
 	struct nl_sock *sock;
diff --git a/src/nl-link-ifindex2name.c b/src/nl-link-ifindex2name.c
index 68e5158..0cb3cbe 100644
--- a/src/nl-link-ifindex2name.c
+++ b/src/nl-link-ifindex2name.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-link-ifindex2name.c     Transform a interface index to its name
  *
@@ -12,6 +13,8 @@
 #include <netlink/cli/utils.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static void print_usage(void)
 {
 	printf("Usage: nl-link-ifindex2name <ifindex>\n");
diff --git a/src/nl-link-list.c b/src/nl-link-list.c
index b5c98b4..d382076 100644
--- a/src/nl-link-list.c
+++ b/src/nl-link-list.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-link-dump.c	Dump link attributes
  *
@@ -12,6 +13,8 @@
 #include <netlink/cli/utils.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static void print_usage(void)
 {
 	printf(
diff --git a/src/nl-link-name2ifindex.c b/src/nl-link-name2ifindex.c
index b8ae4bc..d3e8399 100644
--- a/src/nl-link-name2ifindex.c
+++ b/src/nl-link-name2ifindex.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-link-name2ifindex.c     Transform a interface name to its index
  *
@@ -12,6 +13,8 @@
 #include <netlink/cli/utils.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static void print_usage(void)
 {
 	printf("Usage: nl-link-name2ifindex <name>\n");
diff --git a/src/nl-link-release.c b/src/nl-link-release.c
index 4c9f15a..abe8cdb 100644
--- a/src/nl-link-release.c
+++ b/src/nl-link-release.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-link-release.c     release a link
  *
@@ -13,6 +14,8 @@
 #include <netlink/cli/link.h>
 #include <netlink/route/link/bonding.h>
 
+#include <linux/netlink.h>
+
 int main(int argc, char *argv[])
 {
 	struct nl_sock *sock;
diff --git a/src/nl-link-set.c b/src/nl-link-set.c
index bbb60f9..fc0f5a7 100644
--- a/src/nl-link-set.c
+++ b/src/nl-link-set.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-link-set.c     Set link attributes
  *
@@ -12,6 +13,9 @@
 #include <netlink/cli/utils.h>
 #include <netlink/cli/link.h>
 
+#include <linux/if.h>
+#include <linux/netlink.h>
+
 static struct nl_sock *sock;
 static int quiet = 0;
 
diff --git a/src/nl-link-stats.c b/src/nl-link-stats.c
index 4dfca86..85719c1 100644
--- a/src/nl-link-stats.c
+++ b/src/nl-link-stats.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-link-stats.c     Retrieve link statistics
  *
@@ -12,6 +13,8 @@
 #include <netlink/cli/utils.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static void print_usage(void)
 {
 	printf(
@@ -34,7 +37,7 @@
 	char buf[64];
 	int i;
 
-	for (i = 0; i < RTNL_LINK_STATS_MAX; i++)
+	for (i = 0; i <= RTNL_LINK_STATS_MAX; i++)
 		printf("%s\n", rtnl_link_stat2str(i, buf, sizeof(buf)));
 
 	exit(0);
@@ -59,7 +62,7 @@
 	if (optind >= gargc) {
 		int i;
 
-		for (i = 0; i < RTNL_LINK_STATS_MAX; i++)
+		for (i = 0; i <= RTNL_LINK_STATS_MAX; i++)
 			dump_stat(link, i);
 	} else {
 		while (optind < gargc) {
diff --git a/src/nl-list-caches.c b/src/nl-list-caches.c
index 853d8a4..c59f95b 100644
--- a/src/nl-list-caches.c
+++ b/src/nl-list-caches.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * nl-list-caches.c	List registered cache types
  *
diff --git a/src/nl-list-sockets.c b/src/nl-list-sockets.c
index c2fa1cd..e7d4703 100644
--- a/src/nl-list-sockets.c
+++ b/src/nl-list-sockets.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * nl-list-sockets.c	Pretty-print /proc/net/netlink
  *
@@ -18,7 +19,7 @@
 	FILE *fd;
 	char buf[2048], p[64];
 
-	fd = fopen(PROC_NETLINK, "r");
+	fd = fopen(PROC_NETLINK, "re");
 	if (fd == NULL) {
 		perror("fopen");
 		return -1;
diff --git a/src/nl-monitor.c b/src/nl-monitor.c
index fdf6497..a6f21b4 100644
--- a/src/nl-monitor.c
+++ b/src/nl-monitor.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-monitor.c     Monitor events
  *
@@ -12,71 +13,105 @@
 #include <netlink/cli/utils.h>
 #include <netlink/cli/link.h>
 
+#include <linux/rtnetlink.h>
+
+static const struct {
+	enum rtnetlink_groups gr_id;
+	const char* gr_name;
+} known_groups[] = {
+	{ RTNLGRP_LINK, "link" },
+	{ RTNLGRP_NOTIFY, "notify" },
+	{ RTNLGRP_NEIGH, "neigh" },
+	{ RTNLGRP_TC, "tc" },
+	{ RTNLGRP_IPV4_IFADDR, "ipv4-ifaddr" },
+	{ RTNLGRP_IPV4_MROUTE, "ipv4-mroute" },
+	{ RTNLGRP_IPV4_ROUTE, "ipv4-route" },
+	{ RTNLGRP_IPV6_IFADDR, "ipv6-ifaddr" },
+	{ RTNLGRP_IPV6_MROUTE, "ipv6-mroute" },
+	{ RTNLGRP_IPV6_ROUTE, "ipv6-route" },
+	{ RTNLGRP_IPV6_IFINFO, "ipv6-ifinfo" },
+	{ RTNLGRP_DECnet_IFADDR, "decnet-ifaddr" },
+	{ RTNLGRP_DECnet_ROUTE, "decnet-route" },
+	{ RTNLGRP_IPV6_PREFIX, "ipv6-prefix" },
+	{ RTNLGRP_IPV4_NETCONF, "ipv4-netconf" },
+	{ RTNLGRP_IPV6_NETCONF, "ipv6-netconf" },
+	{ RTNLGRP_MPLS_NETCONF, "mpls-netconf" },
+	{ RTNLGRP_NONE, NULL }
+};
+
 static void obj_input(struct nl_object *obj, void *arg)
 {
+	nl_object_dump(obj, arg);
+}
+
+static int event_input(struct nl_msg *msg, void *arg)
+{
+	if (nl_msg_parse(msg, &obj_input, arg) < 0)
+		fprintf(stderr, "<<EVENT>> Unknown message type\n");
+
+	/* Exit nl_recvmsgs_def() and return to the main select() */
+	return NL_STOP;
+}
+
+static void print_usage(void)
+{
+	int i;
+
+        printf(
+	"Usage: nl-monitor [OPTION] [<groups>]\n"
+	"\n"
+	"Options\n"
+	" -f, --format=TYPE     Output format { brief | details | stats }\n"
+	" -h, --help            Show this help.\n"
+	"\n"
+        );
+	printf("Known groups:");
+	for (i = 0; known_groups[i].gr_id != RTNLGRP_NONE; i++)
+		printf(" %s", known_groups[i].gr_name);
+	printf("\n");
+        exit(0);
+}
+
+int main(int argc, char *argv[])
+{
 	struct nl_dump_params dp = {
 		.dp_type = NL_DUMP_STATS,
 		.dp_fd = stdout,
 		.dp_dump_msgtype = 1,
 	};
 
-	nl_object_dump(obj, &dp);
-}
-
-static int event_input(struct nl_msg *msg, void *arg)
-{
-	if (nl_msg_parse(msg, &obj_input, NULL) < 0)
-		fprintf(stderr, "<<EVENT>> Unknown message type\n");
-
-	/* Exit nl_recvmsgs_def() and return to the main select() */
-	return NL_STOP;
-}
-
-int main(int argc, char *argv[])
-{
 	struct nl_sock *sock;
-	struct nl_cache *link_cache;
 	int err = 1;
 	int i, idx;
 
-	static const struct {
-		enum rtnetlink_groups gr_id;
-		const char* gr_name;
-	} known_groups[] = {
-		{ RTNLGRP_LINK, "link" },
-		{ RTNLGRP_NOTIFY, "notify" },
-		{ RTNLGRP_NEIGH, "neigh" },
-		{ RTNLGRP_TC, "tc" },
-		{ RTNLGRP_IPV4_IFADDR, "ipv4-ifaddr" },
-		{ RTNLGRP_IPV4_MROUTE, "ipv4-mroute" },
-		{ RTNLGRP_IPV4_ROUTE, "ipv4-route" },
-		{ RTNLGRP_IPV6_IFADDR, "ipv6-ifaddr" },
-		{ RTNLGRP_IPV6_MROUTE, "ipv6-mroute" },
-		{ RTNLGRP_IPV6_ROUTE, "ipv6-route" },
-		{ RTNLGRP_IPV6_IFINFO, "ipv6-ifinfo" },
-		{ RTNLGRP_DECnet_IFADDR, "decnet-ifaddr" },
-		{ RTNLGRP_DECnet_ROUTE, "decnet-route" },
-		{ RTNLGRP_IPV6_PREFIX, "ipv6-prefix" },
-		{ RTNLGRP_NONE, NULL }
-	};
-
 	sock = nl_cli_alloc_socket();
 	nl_socket_disable_seq_check(sock);
-	nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL);
+	nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, &dp);
 
-	if (argc > 1 && !strcasecmp(argv[1], "-h")) {
-		printf("Usage: nl-monitor [<groups>]\n");
+	for (;;) {
+		int c, optidx = 0;
+		static struct option long_opts[] = {
+			{ "format", 1, 0, 'f' },
+			{ 0, 0, 0, 0 }
+		};
 
-		printf("Known groups:");
-		for (i = 0; known_groups[i].gr_id != RTNLGRP_NONE; i++)
-			printf(" %s", known_groups[i].gr_name);
-		printf("\n");
-		return 2;
+		c = getopt_long(argc, argv, "f:h", long_opts, &optidx);
+		if (c == -1)
+                        break;
+
+                switch (c) {
+                case 'f':
+			dp.dp_type = nl_cli_parse_dumptype(optarg);
+			break;
+		default:
+			print_usage();
+			break;
+		}
 	}
 
 	nl_cli_connect(sock, NETLINK_ROUTE);
 
-	for (idx = 1; argc > idx; idx++) {
+	for (idx = optind; argc > idx; idx++) {
 		for (i = 0; known_groups[i].gr_id != RTNLGRP_NONE; i++) {
 			if (!strcmp(argv[idx], known_groups[i].gr_name)) {
 
@@ -92,7 +127,7 @@
 			fprintf(stderr, "Warning: Unknown group: %s\n", argv[idx]);
 	}
 
-	link_cache = nl_cli_link_alloc_cache(sock);
+	nl_cli_link_alloc_cache(sock);
 
 	while (1) {
 		fd_set rfds;
diff --git a/src/nl-neigh-add.c b/src/nl-neigh-add.c
index 4cddabe..585639a 100644
--- a/src/nl-neigh-add.c
+++ b/src/nl-neigh-add.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/ nl-neigh-add.c     Add a neighbour
  *
@@ -13,6 +14,8 @@
 #include <netlink/cli/neigh.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static int quiet = 0;
 
 static void print_usage(void)
@@ -51,12 +54,12 @@
 		.dp_fd = stdout,
 	};
 	int err, ok = 0, nlflags = NLM_F_REPLACE | NLM_F_CREATE;
- 
+
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
 	link_cache = nl_cli_link_alloc_cache(sock);
- 	neigh = nl_cli_neigh_alloc();
- 
+	neigh = nl_cli_neigh_alloc();
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -76,7 +79,7 @@
 			{ "state", 1, 0, ARG_STATE },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "qhva:l:d:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -92,7 +95,7 @@
 		case 'd': nl_cli_neigh_parse_dev(neigh, link_cache, optarg); break;
 		case ARG_STATE: nl_cli_neigh_parse_state(neigh, optarg); break;
 		}
- 	}
+	}
 
 	if (!ok)
 		print_usage();
@@ -104,7 +107,7 @@
 	if (!quiet) {
 		printf("Added ");
 		nl_object_dump(OBJ_CAST(neigh), &dp);
- 	}
+	}
 
 	return 0;
 }
diff --git a/src/nl-neigh-delete.c b/src/nl-neigh-delete.c
index c418608..826c1c5 100644
--- a/src/nl-neigh-delete.c
+++ b/src/nl-neigh-delete.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-neigh-delete.c     Delete a neighbour
  *
@@ -13,6 +14,8 @@
 #include <netlink/cli/neigh.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0;
 static struct nl_sock *sock;
 
@@ -67,13 +70,13 @@
 {
 	struct rtnl_neigh *neigh;
 	struct nl_cache *link_cache, *neigh_cache;
- 
+
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
 	link_cache = nl_cli_link_alloc_cache(sock);
 	neigh_cache = nl_cli_neigh_alloc_cache(sock);
- 	neigh = nl_cli_neigh_alloc();
- 
+	neigh = nl_cli_neigh_alloc();
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -94,7 +97,7 @@
 			{ "state", 1, 0, ARG_STATE },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "qhva:l:d:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -111,7 +114,7 @@
 		case ARG_FAMILY: nl_cli_neigh_parse_family(neigh, optarg); break;
 		case ARG_STATE: nl_cli_neigh_parse_state(neigh, optarg); break;
 		}
- 	}
+	}
 
 	nl_cache_foreach_filter(neigh_cache, OBJ_CAST(neigh), delete_cb, NULL);
 
diff --git a/src/nl-neigh-list.c b/src/nl-neigh-list.c
index ebf5486..a926208 100644
--- a/src/nl-neigh-list.c
+++ b/src/nl-neigh-list.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-neigh-list.c      List Neighbours
  *
@@ -13,6 +14,8 @@
 #include <netlink/cli/neigh.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static void print_usage(void)
 {
 	printf(
@@ -42,13 +45,13 @@
 		.dp_type = NL_DUMP_LINE,
 		.dp_fd = stdout,
 	};
- 
+
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
-	link_cache = nl_cli_link_alloc_cache(sock);
+	link_cache = nl_cli_link_alloc_cache_flags(sock, NL_CACHE_AF_ITER);
 	neigh_cache = nl_cli_neigh_alloc_cache(sock);
- 	neigh = nl_cli_neigh_alloc();
- 
+	neigh = nl_cli_neigh_alloc();
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -66,7 +69,7 @@
 			{ "state", 1, 0, ARG_STATE },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "f:hva:l:d:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -81,9 +84,14 @@
 		case ARG_FAMILY: nl_cli_neigh_parse_family(neigh, optarg); break;
 		case ARG_STATE: nl_cli_neigh_parse_state(neigh, optarg); break;
 		}
- 	}
+	}
 
 	nl_cache_dump_filter(neigh_cache, &params, OBJ_CAST(neigh));
 
+	rtnl_neigh_put(neigh);
+	nl_cache_put(neigh_cache);
+	nl_cache_put(link_cache);
+	nl_socket_free(sock);
+
 	return 0;
 }
diff --git a/src/nl-neightbl-list.c b/src/nl-neightbl-list.c
index 5010b92..10d7ed4 100644
--- a/src/nl-neightbl-list.c
+++ b/src/nl-neightbl-list.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-neightbl-list.c     Dump neighbour tables
  *
@@ -12,6 +13,8 @@
 #include <netlink/cli/utils.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static void print_usage(void)
 {
 	printf(
@@ -28,18 +31,18 @@
 int main(int argc, char *argv[])
 {
 	struct nl_sock *sock;
-	struct nl_cache *link_cache, *neightbl_cache;
+	struct nl_cache *neightbl_cache;
 	struct nl_dump_params params = {
 		.dp_type = NL_DUMP_LINE,
 		.dp_fd = stdout,
 	};
- 
+
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
-	link_cache = nl_cli_link_alloc_cache(sock);
+	nl_cli_link_alloc_cache(sock);
 	neightbl_cache = nl_cli_alloc_cache(sock, "neighbour table",
 					    rtnl_neightbl_alloc_cache);
- 
+
 	for (;;) {
 		int c, optidx = 0;
 		static struct option long_opts[] = {
@@ -48,7 +51,7 @@
 			{ "version", 0, 0, 'v' },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "f:hv", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -58,7 +61,7 @@
 		case 'h': print_usage(); break;
 		case 'v': nl_cli_print_version(); break;
 		}
- 	}
+	}
 
 	nl_cache_dump(neightbl_cache, &params);
 
diff --git a/src/nl-pktloc-lookup.c b/src/nl-pktloc-lookup.c
index 17c867b..8b1272c 100644
--- a/src/nl-pktloc-lookup.c
+++ b/src/nl-pktloc-lookup.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-pktloc-lookup.c     Lookup packet location alias
  *
@@ -11,6 +12,7 @@
 
 #include <netlink/cli/utils.h>
 #include <netlink/route/pktloc.h>
+#include <linux/tc_ematch/tc_em_cmp.h>
 
 static void print_usage(void)
 {
@@ -122,7 +124,7 @@
 			{ "u32", 1, 0, ARG_U32 },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "hvl", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -136,7 +138,7 @@
 			uvalue = nl_cli_parse_u32(optarg);
 			break;
 		}
- 	}
+	}
 
 	if (optind >= argc)
 		print_usage();
diff --git a/src/nl-qdisc-add.c b/src/nl-qdisc-add.c
index c2a7c9f..38903f3 100644
--- a/src/nl-qdisc-add.c
+++ b/src/nl-qdisc-add.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-qdisc-add.c     Add Queueing Discipline
  *
@@ -16,6 +17,8 @@
 
 #include <netlink-private/route/tc-api.h>
 
+#include <linux/netlink.h>
+
 static int quiet = 0;
 
 static void print_usage(void)
@@ -59,15 +62,15 @@
 	struct rtnl_tc_ops *ops;
 	int err, flags = NLM_F_CREATE | NLM_F_EXCL;
 	char *kind, *id = NULL;
- 
+
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
 
 	link_cache = nl_cli_link_alloc_cache(sock);
 
- 	qdisc = nl_cli_qdisc_alloc();
+	qdisc = nl_cli_qdisc_alloc();
 	tc = (struct rtnl_tc *) qdisc;
- 
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -89,7 +92,7 @@
 			{ "update-only", 0, 0, ARG_UPDATE_ONLY },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "+qhvd:p:i:",
 				long_opts, &optidx);
 		if (c == -1)
@@ -107,7 +110,7 @@
 		case ARG_UPDATE_ONLY: flags = 0; break;
 		case ARG_REPLACE_ONLY: flags = NLM_F_REPLACE; break;
 		}
- 	}
+	}
 
 	if (optind >= argc)
 		print_usage();
@@ -137,7 +140,7 @@
 	if (!quiet) {
 		printf("Adding ");
 		nl_object_dump(OBJ_CAST(qdisc), &dp);
- 	}
+	}
 
 	if ((err = rtnl_qdisc_add(sock, qdisc, flags)) < 0)
 		nl_cli_fatal(EINVAL, "Unable to add qdisc: %s", nl_geterror(err));
diff --git a/src/nl-qdisc-delete.c b/src/nl-qdisc-delete.c
index 2f945bb..7c5926b 100644
--- a/src/nl-qdisc-delete.c
+++ b/src/nl-qdisc-delete.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-qdisc-delete.c     Delete Queuing Disciplines
  *
@@ -14,6 +15,8 @@
 #include <netlink/cli/qdisc.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0;
 static struct nl_sock *sock;
 
@@ -71,14 +74,14 @@
 	struct rtnl_tc *tc;
 	struct nl_cache *link_cache, *qdisc_cache;
 	int nfilter = 0;
- 
+
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
 	link_cache = nl_cli_link_alloc_cache(sock);
 	qdisc_cache = nl_cli_qdisc_alloc_cache(sock);
- 	qdisc = nl_cli_qdisc_alloc();
+	qdisc = nl_cli_qdisc_alloc();
 	tc = (struct rtnl_tc *) qdisc;
- 
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -97,7 +100,7 @@
 			{ "kind", 1, 0, 'k' },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "qhvd:p:i:k:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -126,7 +129,7 @@
 			nl_cli_tc_parse_kind(tc, optarg);
 			break;
 		}
- 	}
+	}
 
 	if (nfilter == 0 && !interactive && !default_yes) {
 		nl_cli_fatal(EINVAL,
diff --git a/src/nl-qdisc-list.c b/src/nl-qdisc-list.c
index 5b0a3f0..6796ca5 100644
--- a/src/nl-qdisc-list.c
+++ b/src/nl-qdisc-list.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-qdisc-list.c     List Queueing Disciplines
  *
@@ -16,6 +17,9 @@
 #include <netlink/cli/cls.h>
 #include <netlink/cli/link.h>
 
+#include <linux/pkt_sched.h>
+#include <linux/netlink.h>
+
 #define NUM_INDENT 4
 
 static struct nl_sock *sock;
@@ -129,15 +133,15 @@
 	struct rtnl_qdisc *qdisc;
 	struct rtnl_tc *tc;
 	struct nl_cache *link_cache, *qdisc_cache;
- 
+
 	params.dp_fd = stdout;
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
 	link_cache = nl_cli_link_alloc_cache(sock);
 	qdisc_cache = nl_cli_qdisc_alloc_cache(sock);
- 	qdisc = nl_cli_qdisc_alloc();
+	qdisc = nl_cli_qdisc_alloc();
 	tc = (struct rtnl_tc *) qdisc;
- 
+
 	for (;;) {
 		int c, optidx = 0;
 		enum {
@@ -156,7 +160,7 @@
 			{ "kind", 1, 0, 'k' },
 			{ 0, 0, 0, 0 }
 		};
-	
+
 		c = getopt_long(argc, argv, "rhvd:p:i:k:", long_opts, &optidx);
 		if (c == -1)
 			break;
@@ -172,7 +176,7 @@
 		case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break;
 		case 'k': nl_cli_tc_parse_kind(tc, optarg); break;
 		}
- 	}
+	}
 
 	if (recursive)
 		nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(qdisc), list_qdisc, NULL);
diff --git a/src/nl-route-add.c b/src/nl-route-add.c
index d4aa767..ed2c4e2 100644
--- a/src/nl-route-add.c
+++ b/src/nl-route-add.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-route-add.c     Route addition utility
  *
@@ -13,6 +14,8 @@
 #include <netlink/cli/route.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static int quiet = 0;
 static struct nl_cache *link_cache, *route_cache;
 
diff --git a/src/nl-route-delete.c b/src/nl-route-delete.c
index 884fd7f..750b57f 100644
--- a/src/nl-route-delete.c
+++ b/src/nl-route-delete.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-route-delete.c     Delete Routes
  *
@@ -13,6 +14,8 @@
 #include <netlink/cli/route.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static int interactive = 0, default_yes = 0, quiet = 0;
 static int deleted = 0;
 static struct nl_sock *sock;
diff --git a/src/nl-route-get.c b/src/nl-route-get.c
index 9d9a448..564fc16 100644
--- a/src/nl-route-get.c
+++ b/src/nl-route-get.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-route-get.c     Get Route Attributes
  *
@@ -13,6 +14,8 @@
 #include <netlink/cli/route.h>
 #include <netlink/cli/link.h>
 
+#include <linux/rtnetlink.h>
+
 static void print_usage(void)
 {
 	printf("Usage: nl-route-get <addr>\n");
@@ -43,7 +46,6 @@
 int main(int argc, char *argv[])
 {
 	struct nl_sock *sock;
-	struct nl_cache *link_cache, *route_cache;
 	struct nl_addr *dst;
 	int err = 1;
 
@@ -52,8 +54,8 @@
 
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
-	link_cache = nl_cli_link_alloc_cache(sock);
-	route_cache = nl_cli_route_alloc_cache(sock, 0);
+	nl_cli_link_alloc_cache(sock);
+	nl_cli_route_alloc_cache(sock, 0);
 
 	dst = nl_cli_addr_parse(argv[1], AF_INET);
 
@@ -83,7 +85,5 @@
 			nl_cli_fatal(err, "%s", nl_geterror(err));
 	}
 
-	//nl_cache_dump(route_cache, &params);
-
 	return 0;
 }
diff --git a/src/nl-route-list.c b/src/nl-route-list.c
index e0e57be..b6c4270 100644
--- a/src/nl-route-list.c
+++ b/src/nl-route-list.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-route-list.c     List route attributes
  *
@@ -13,6 +14,8 @@
 #include <netlink/cli/route.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static void print_usage(void)
 {
 	printf(
diff --git a/src/nl-rule-list.c b/src/nl-rule-list.c
index 8b474fa..b923184 100644
--- a/src/nl-rule-list.c
+++ b/src/nl-rule-list.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-rule-dump.c     Dump rule attributes
  *
@@ -13,6 +14,8 @@
 #include <netlink/cli/rule.h>
 #include <netlink/cli/link.h>
 
+#include <linux/netlink.h>
+
 static void print_usage(void)
 {
 	printf(
@@ -34,7 +37,7 @@
 {
 	struct nl_sock *sock;
 	struct rtnl_rule *rule;
-	struct nl_cache *link_cache, *rule_cache;
+	struct nl_cache *rule_cache;
 	struct nl_dump_params params = {
 		.dp_fd = stdout,
 		.dp_type = NL_DUMP_LINE,
@@ -42,7 +45,7 @@
 
 	sock = nl_cli_alloc_socket();
 	nl_cli_connect(sock, NETLINK_ROUTE);
-	link_cache = nl_cli_link_alloc_cache(sock);
+	nl_cli_link_alloc_cache(sock);
 	rule_cache = nl_cli_rule_alloc_cache(sock);
 	rule = nl_cli_rule_alloc();
 
diff --git a/src/nl-tctree-list.c b/src/nl-tctree-list.c
index d90cb28..9e03038 100644
--- a/src/nl-tctree-list.c
+++ b/src/nl-tctree-list.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-tctree-list.c		List Traffic Control Tree
  *
@@ -13,6 +14,8 @@
 #include <netlink/cli/link.h>
 #include <netlink/cli/qdisc.h>
 #include <netlink/cli/class.h>
+
+#include <linux/netlink.h>
 #include <linux/pkt_sched.h>
 
 static struct nl_sock *sock;
@@ -50,9 +53,9 @@
 
 	leaf = rtnl_class_leaf_qdisc(class, qdisc_cache);
 	if (leaf)
-		print_qdisc((struct nl_object *) leaf, arg + 2);
+		print_qdisc((struct nl_object *) leaf, (char *) arg + 2);
 
-	print_tc_childs(TC_CAST(class), arg + 2);
+	print_tc_childs(TC_CAST(class), (char *) arg + 2);
 
 	if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0)
 		return;
@@ -85,7 +88,7 @@
 	params.dp_prefix = (int)(long) arg;
 	nl_object_dump(obj, &params);
 
-	print_tc_childs(TC_CAST(qdisc), arg + 2);
+	print_tc_childs(TC_CAST(qdisc), (char *) arg + 2);
 
 	if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0)
 		return;
diff --git a/src/nl-util-addr.c b/src/nl-util-addr.c
index 5f0738d..6a81166 100644
--- a/src/nl-util-addr.c
+++ b/src/nl-util-addr.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
 /*
  * src/nl-util-addr.c     Address Helper
  *
diff --git a/tests/.gitignore b/tests/.gitignore
index 6b77cac..90af67a 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -7,16 +7,26 @@
 /test-complex-HTB-with-hash-filters
 /test-create-bond
 /test-create-bridge
+/test-create-geneve
+/test-create-ifb
 /test-create-ip6tnl
 /test-create-ipgre
+/test-create-ipgretap
 /test-create-ipip
+/test-create-ipvlan
 /test-create-ipvti
+/test-create-macsec
+/test-create-macvlan
+/test-create-macvtap
 /test-create-sit
 /test-create-veth
 /test-create-vlan
+/test-create-vrf
 /test-create-vxlan
+/test-create-xfrmi
 /test-delete-link
 /test-genl
+/test-loopback-up-down
 /test-nf-cache-mngr
 /test-socket-creation
 /test-suite.log
diff --git a/tests/Makefile.am b/tests/Makefile.am
deleted file mode 100644
index 255033d..0000000
--- a/tests/Makefile.am
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- Makefile -*-
-
-EXTRA_DIST = \
-	util.h
-
-if ENABLE_UNIT_TESTS
-
-AM_CPPFLAGS  = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DSYSCONFDIR=\"$(sysconfdir)/libnl\"
-
-LDADD = \
-	${top_builddir}/lib/libnl-3.la \
-	${top_builddir}/lib/libnl-nf-3.la \
-	${top_builddir}/lib/libnl-genl-3.la \
-	${top_builddir}/lib/libnl-route-3.la \
-	@CHECK_LIBS@
-
-AM_CFLAGS = @CHECK_CFLAGS@
-
-UNIT_TESTS = check-all
-
-check_PROGRAMS = \
-	test-create-bond \
-	test-create-vlan \
-	test-create-vxlan \
-	test-create-veth \
-	test-create-bridge \
-	test-create-ip6tnl \
-	test-create-ipgre \
-	test-create-ipip \
-	test-create-ipvti \
-	test-create-sit \
-	test-delete-link \
-	test-socket-creation \
-	test-complex-HTB-with-hash-filters \
-	test-u32-filter-with-actions \
-	${UNIT_TESTS}
-
-TESTS = \
-	${UNIT_TESTS}
-
-if ENABLE_CLI
-LDADD += ${top_builddir}/src/lib/libnl-cli-3.la
-check_PROGRAMS += \
-	test-cache-mngr \
-	test-genl \
-	test-nf-cache-mngr
-endif
-
-test_cache_mngr_SOURCES = test-cache-mngr.c
-test_create_bond_SOURCES = test-create-bond.c
-test_create_vlan_SOURCES = test-create-vlan.c
-test_create_vxlan_SOURCES = test-create-vxlan.c
-test_create_veth_SOURCES = test-create-veth.c
-test_create_bridge_SOURCES = test-create-bridge.c
-test_delete_link_SOURCES = test-delete-link.c
-test_genl_SOURCES = test-genl.c
-test_nf_cache_mngr_SOURCES = test-nf-cache-mngr.c
-test_socket_creation_SOURCES = test-socket-creation.c
-test_complex_HTB_with_hash_filters_SOURCES = test-complex-HTB-with-hash-filters.c
-test_u32_filter_with_actions_SOURCES = test-u32-filter-with-actions.c
-
-# Unit tests
-check_all_SOURCES = \
-	check-all.c \
-	check-addr.c \
-	check-attr.c
-endif
diff --git a/tests/check-addr.c b/tests/check-addr.c
index 39f3ede..48a2d93 100644
--- a/tests/check-addr.c
+++ b/tests/check-addr.c
@@ -12,6 +12,8 @@
 #include <check.h>
 #include <netlink/addr.h>
 
+#include "util.h"
+
 START_TEST(addr_alloc)
 {
 	struct nl_addr *addr;
diff --git a/tests/check-all.c b/tests/check-all.c
index e431802..7b738da 100644
--- a/tests/check-all.c
+++ b/tests/check-all.c
@@ -11,8 +11,7 @@
 
 #include <check.h>
 
-extern Suite *make_nl_addr_suite(void);
-extern Suite *make_nl_attr_suite(void);
+#include "util.h"
 
 static Suite *main_suite(void)
 {
@@ -32,6 +31,7 @@
 
 	srunner_add_suite(runner, make_nl_addr_suite());
 	srunner_add_suite(runner, make_nl_attr_suite());
+	srunner_add_suite(runner, make_nl_ematch_tree_clone_suite());
 
 	/* Do not add testsuites below this line */
 
diff --git a/tests/check-attr.c b/tests/check-attr.c
index d862230..0390997 100644
--- a/tests/check-attr.c
+++ b/tests/check-attr.c
@@ -13,6 +13,8 @@
 #include <netlink/attr.h>
 #include <netlink/msg.h>
 
+#include <linux/netlink.h>
+
 START_TEST(attr_size)
 {
 	fail_if(nla_attr_size(0) != NLA_HDRLEN,
diff --git a/tests/check-ematch-tree-clone.c b/tests/check-ematch-tree-clone.c
new file mode 100644
index 0000000..9c35c52
--- /dev/null
+++ b/tests/check-ematch-tree-clone.c
@@ -0,0 +1,143 @@
+#include <netlink-private/types.h>
+#include <netlink/route/cls/ematch.h>
+
+#include <linux/netlink.h>
+
+#include <stdio.h>
+#include <time.h>
+
+#include <check.h>
+#include "util.h"
+
+#define MAX_DEPTH		6
+#define MAX_CHILDREN		5
+
+static int current_depth = 0;
+static int id = 1;
+static long long array_size = 0;
+
+static int *src_result = NULL, *dst_result = NULL;
+
+static long long my_pow(long long x, long long y)
+{
+	int ret = x;
+
+	if (y == 0)
+		return 1;
+
+	if (y < 0 || x == 0)
+		return 0;
+
+	while(--y) {
+		ret *= x;
+	}
+
+	return ret;
+}
+
+static long int generate_random(long int max)
+{
+	srandom(time(NULL) + id);
+	return (random() % max);
+}
+
+static int build_children(struct nl_list_head *parent)
+{
+	int i, num = 0;
+	struct rtnl_ematch *child = NULL;
+
+	if (!parent)
+		return 0;
+
+	if (++current_depth > MAX_DEPTH) {
+		--current_depth;
+		return 0;
+	}
+
+	num = generate_random(MAX_CHILDREN + 1);
+	for (i = 0; i < num; ++i) {
+		child = rtnl_ematch_alloc();
+		if (!child) {
+			printf("Mem alloc error\n");
+			exit(1);
+		}
+		build_children(&child->e_childs);
+		child->e_id = id++;
+		nl_list_add_tail(&child->e_list, parent);
+	}
+
+	--current_depth;
+	return 0;
+}
+
+static void build_src_cgroup(struct rtnl_ematch_tree *tree)
+{
+	build_children(&tree->et_list);
+}
+
+static void dump_ematch_list(struct nl_list_head *head, int *result, int *index)
+{
+	struct rtnl_ematch *pos = NULL;
+
+	nl_list_for_each_entry(pos, head, e_list) {
+		if (!nl_list_empty(&pos->e_childs))
+			dump_ematch_list(&pos->e_childs, result, index);
+		result[*index] = pos->e_id;
+		(*index)++;
+	}
+}
+
+static void dump_ematch_tree(struct rtnl_ematch_tree *tree, int *result, int *index)
+{
+	if (!tree)
+		return;
+
+	dump_ematch_list(&tree->et_list, result, index);
+}
+
+static int compare(int *r1, int *r2, int len)
+{
+	int i = 0;
+	for (i = 0; i < len; ++i) {
+		if (r1[i] != r2[i])
+			return -1;
+	}
+	return 0;
+}
+
+START_TEST(ematch_tree_clone)
+{
+	struct rtnl_ematch_tree *src = NULL, *dst = NULL;
+	int i = 0, j = 0;
+
+	array_size = (MAX_DEPTH * my_pow(MAX_CHILDREN, MAX_DEPTH)) / 2;
+	src_result = calloc(4, array_size);
+	dst_result = calloc(4, array_size);
+
+	src = rtnl_ematch_tree_alloc(2);
+
+	build_src_cgroup(src);
+	dump_ematch_tree(src, src_result, &i);
+
+	dst = rtnl_ematch_tree_clone(src);
+	dump_ematch_tree(dst, dst_result, &j);
+
+	fail_if(!dst);
+	fail_if(i != j);
+	fail_if(compare(src_result, dst_result, i));
+
+	free(src_result);
+	free(dst_result);
+}
+END_TEST
+
+Suite *make_nl_ematch_tree_clone_suite(void)
+{
+	Suite *suite = suite_create("Clone ematch tree");
+
+	TCase *ematch_tree = tcase_create("Core");
+	tcase_add_test(ematch_tree, ematch_tree_clone);
+	suite_add_tcase(suite, ematch_tree);
+
+	return suite;
+}
diff --git a/tests/test-cache-mngr.c b/tests/test-cache-mngr.c
index 8999e58..9761452 100644
--- a/tests/test-cache-mngr.c
+++ b/tests/test-cache-mngr.c
@@ -5,6 +5,8 @@
 
 #include <netlink-private/cache-api.h>
 
+#include <linux/netlink.h>
+
 static int quit = 0;
 
 static struct nl_dump_params dp = {
diff --git a/tests/test-complex-HTB-with-hash-filters.c b/tests/test-complex-HTB-with-hash-filters.c
index 48cf5e3..b1bf36e 100644
--- a/tests/test-complex-HTB-with-hash-filters.c
+++ b/tests/test-complex-HTB-with-hash-filters.c
@@ -18,6 +18,7 @@
 #include <netlink/route/classifier.h>
 #include <netlink/route/class.h>
 #include <linux/if_ether.h>
+#include <linux/pkt_cls.h>
 
 #include <netlink/attr.h>
 //#include "include/rtnl_u32.h"
@@ -26,10 +27,12 @@
 #include <string.h>
 //#include "include/rtnl_u32_addon.h"
 
+#include <linux/netlink.h>
+
 #define 	TC_HANDLE(maj, min)   (TC_H_MAJ((maj) << 16) | TC_H_MIN(min))
 
 /* some functions are copied from iproute-tc tool */
-int get_u32(__u32 *val, const char *arg, int base)
+static int get_u32(__u32 *val, const char *arg, int base)
 {
 	unsigned long res;
 	char *ptr;
@@ -43,7 +46,7 @@
 	return 0;
 }
 
-int get_u32_handle(__u32 *handle, const char *str)
+static int get_u32_handle(__u32 *handle, const char *str)
 {
 	__u32 htid=0, hash=0, nodeid=0;
 	char *tmp = strchr(str, ':');
@@ -78,7 +81,7 @@
 	return 0;
 }
 
-uint32_t get_u32_parse_handle(const char *cHandle)
+static uint32_t get_u32_parse_handle(const char *cHandle)
 {
 	uint32_t handle=0;
 
@@ -94,7 +97,7 @@
 	return handle;
 }
 
-int get_tc_classid(__u32 *h, const char *str)
+static int get_tc_classid(__u32 *h, const char *str)
 {
 	__u32 maj, min;
 	char *p;
@@ -134,7 +137,7 @@
  * Function that adds a new filter and attach it to a hash table
  *
  */
-int u32_add_filter_on_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, 
+static int u32_add_filter_on_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, 
 		uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask,
 		uint32_t htid, uint32_t classid
 )
@@ -186,7 +189,7 @@
  * and set next hash table link with hash mask
  *
  */
-int u32_add_filter_on_ht_with_hashmask(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, 
+static int u32_add_filter_on_ht_with_hashmask(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, 
 	    uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask,
 	    uint32_t htid, uint32_t htlink, uint32_t hmask, uint32_t hoffset
 )
@@ -237,7 +240,7 @@
 /* 
  * function that creates a new hash table 
  */
-int u32_add_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, uint32_t htid, uint32_t divisor)
+static int u32_add_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, uint32_t htid, uint32_t divisor)
 {
 
     int err;
@@ -276,7 +279,7 @@
 /*
  * function that adds a new HTB qdisc and set the default class for unclassified traffic
  */
-int qdisc_add_HTB(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t defaultClass)
+static int qdisc_add_HTB(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t defaultClass)
 {
     
     struct rtnl_qdisc *qdisc;
@@ -326,7 +329,7 @@
 /*
  * function that adds a new HTB class and set its parameters
  */
-int class_add_HTB(struct nl_sock *sock, struct rtnl_link *rtnlLink, 
+static int class_add_HTB(struct nl_sock *sock, struct rtnl_link *rtnlLink, 
 		    uint32_t parentMaj, uint32_t parentMin,
 		    uint32_t childMaj,  uint32_t childMin, 
 		    uint64_t rate, uint64_t ceil,
@@ -388,7 +391,7 @@
 /*
  * function that adds a HTB root class and set its parameters
  */
-int class_add_HTB_root(struct nl_sock *sock, struct rtnl_link *rtnlLink, 
+static int class_add_HTB_root(struct nl_sock *sock, struct rtnl_link *rtnlLink, 
 			uint64_t rate, uint64_t ceil,
 			uint32_t burst, uint32_t cburst
 )
@@ -443,7 +446,7 @@
 /*
  * function that adds a new SFQ qdisc as a leaf for a HTB class
  */
-int qdisc_add_SFQ_leaf(struct nl_sock *sock, struct rtnl_link *rtnlLink,
+static int qdisc_add_SFQ_leaf(struct nl_sock *sock, struct rtnl_link *rtnlLink,
 			uint32_t parentMaj, uint32_t parentMin, 
 			int quantum, int limit, int perturb
 )
@@ -514,6 +517,8 @@
 
     struct nl_cache *link_cache;
     
+    uint32_t i;
+
     if (!(sock = nl_socket_alloc())) {
         printf("Unable to allocate netlink socket\n");
         exit(1);
@@ -576,7 +581,6 @@
      * each entry in hash table match a byte from IP address specified later by a hash key
      */
 
-    uint32_t i;
     for (i = 1; i <= 0xf; i++) 
 	u32_add_ht(sock, link, 1, i, 256);
 
diff --git a/tests/test-create-bond.c b/tests/test-create-bond.c
index 11bc5b0..326e0ef 100644
--- a/tests/test-create-bond.c
+++ b/tests/test-create-bond.c
@@ -2,6 +2,8 @@
 #include <netlink/route/link.h>
 #include <netlink/route/link/bonding.h>
 
+#include <linux/netlink.h>
+
 int main(int argc, char *argv[])
 {
 	struct rtnl_link *link;
diff --git a/tests/test-create-bridge.c b/tests/test-create-bridge.c
index 7202cd7..a9d9f2e 100644
--- a/tests/test-create-bridge.c
+++ b/tests/test-create-bridge.c
@@ -2,10 +2,12 @@
 #include <netlink/route/link.h>
 #include <netlink/route/link/bridge.h>
 
+#include <linux/netlink.h>
+
 #define TEST_BRIDGE_NAME "testbridge"
 #define TEST_INTERFACE_NAME "testtap1"
 
-int create_bridge(struct nl_sock *sk, struct nl_cache *link_cache, const char *name) {
+static int create_bridge(struct nl_sock *sk, struct nl_cache *link_cache, const char *name) {
 	struct rtnl_link *link;
 	int err;
 
@@ -29,6 +31,7 @@
 	struct rtnl_link *link;
 	struct nl_cache *link_cache;
 	struct nl_sock *sk;
+	struct rtnl_link *ltap;
 	int err;
 
 	sk = nl_socket_alloc();
@@ -50,7 +53,7 @@
 	nl_cache_refill(sk, link_cache);
 
 	link = rtnl_link_get_by_name(link_cache, TEST_BRIDGE_NAME);
-	struct rtnl_link *ltap = rtnl_link_get_by_name(link_cache, TEST_INTERFACE_NAME);
+	ltap = rtnl_link_get_by_name(link_cache, TEST_INTERFACE_NAME);
 	if (!ltap) {
 		fprintf(stderr, "You should create a tap interface before lunch this test (# tunctl -t %s)\n", TEST_INTERFACE_NAME);
 		return -1;
@@ -65,6 +68,11 @@
 		fprintf(stderr, "Link is not a bridge\n");
 		return -2;
 	}
+
+	rtnl_link_put(ltap);
+	nl_cache_refill(sk, link_cache);
+	ltap = rtnl_link_get_by_name(link_cache, TEST_INTERFACE_NAME);
+
 	if(rtnl_link_get_master(ltap) <= 0) {
 		fprintf(stderr, "Interface is not attached to a bridge\n");
 		return -3;
diff --git a/tests/test-create-geneve.c b/tests/test-create-geneve.c
new file mode 100644
index 0000000..37b220a
--- /dev/null
+++ b/tests/test-create-geneve.c
@@ -0,0 +1,87 @@
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/geneve.h>
+
+#include <linux/netlink.h>
+
+#define IPv6 1
+
+int main(int argc, char *argv[])
+{
+	struct rtnl_link *link;
+	struct nl_addr *addr;
+	struct nl_sock *sk;
+	int err;
+
+	sk = nl_socket_alloc();
+	if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+		nl_perror(err, "Unable to connect socket");
+		return err;
+	}
+
+	link = rtnl_link_geneve_alloc();
+
+	rtnl_link_set_name(link, "gnv123");
+
+	if ((err = rtnl_link_geneve_set_id(link, 123)) < 0) {
+		nl_perror(err, "Unable to set GENEVE ID");
+		return err;
+	}
+
+#if IPv6
+	if ((err = nl_addr_parse("2001:0db8:0:f101::1/64", AF_INET6, &addr)) < 0) {
+		nl_perror(err,  "Unable to parse IPv6 address");
+		return err;
+	}
+	if ((err = rtnl_link_geneve_set_label(link, 123)) < 0) {
+		nl_perror(err, "Unable to set label");
+		return err;
+	}
+
+	if ((err = rtnl_link_geneve_set_udp_zero_csum6_tx(link, 1)) < 0) {
+		nl_perror(err, "Unable to set skip transmitted UDP checksum");
+		return err;
+	}
+
+	if ((err = rtnl_link_geneve_set_udp_zero_csum6_rx(link, 1)) < 0) {
+		nl_perror(err, "Unable to set skip received UDP checksum");
+		return err;
+	}
+#else
+	if ((err = nl_addr_parse("10.4.4.4", AF_INET, &addr)) < 0) {
+		nl_perror(err, "Unable to parse IP address");
+		return err;
+	}
+#endif
+
+	if ((err = rtnl_link_geneve_set_remote(link, addr)) < 0) {
+		nl_perror(err, "Unable to set remote address");
+		return err;
+	}
+	nl_addr_put(addr);
+
+	if ((err = rtnl_link_geneve_set_ttl(link, 1)) < 0) {
+		nl_perror(err, "Unable to set TTL");
+		return err;
+	}
+
+	if ((err = rtnl_link_geneve_set_tos(link, 0)) < 0) {
+		nl_perror(err, "Unable to set ToS");
+		return err;
+	}
+
+	if ((err = rtnl_link_geneve_set_port(link, 5060)) < 0) {
+		nl_perror(err, "Unable to set port");
+		return err;
+	}
+
+	if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) {
+		nl_perror(err, "Unable to add link");
+		return err;
+	}
+
+	rtnl_link_put(link);
+	nl_close(sk);
+
+	return 0;
+}
diff --git a/tests/test-create-ifb.c b/tests/test-create-ifb.c
new file mode 100644
index 0000000..d154ffd
--- /dev/null
+++ b/tests/test-create-ifb.c
@@ -0,0 +1,31 @@
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#include <linux/netlink.h>
+
+int main(int argc, char *argv[])
+{
+	struct rtnl_link *link;
+	struct nl_sock *sk;
+	int err;
+
+	sk = nl_socket_alloc();
+	if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+		nl_perror(err, "Unable to connect socket");
+		return err;
+	}
+
+	link = rtnl_link_alloc();
+	rtnl_link_set_type(link, "ifb");
+	rtnl_link_set_name(link, "ifb1");
+
+	if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) {
+		nl_perror(err, "Unable to add link");
+		return err;
+	}
+
+	rtnl_link_put(link);
+	nl_close(sk);
+
+	return 0;
+}
diff --git a/tests/test-create-ipgretap.c b/tests/test-create-ipgretap.c
new file mode 100644
index 0000000..1fe8231
--- /dev/null
+++ b/tests/test-create-ipgretap.c
@@ -0,0 +1,56 @@
+#include <netlink/route/link/ipgre.h>
+#include <netlink-private/netlink.h>
+
+int main(int argc, char *argv[])
+{
+	struct nl_cache *link_cache;
+	struct rtnl_link *link;
+	struct in_addr addr;
+	struct nl_sock *sk;
+	int err, if_index;
+
+	sk = nl_socket_alloc();
+	if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+		nl_perror(err, "Unable to connect socket");
+		return err;
+	}
+
+	err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache);
+	if ( err < 0) {
+		nl_perror(err, "Unable to allocate cache");
+		return err;
+	}
+
+	if_index = rtnl_link_name2i(link_cache, "enp0s5");
+	if (!if_index) {
+		fprintf(stderr, "Unable to lookup enp0s5");
+		return -1;
+	}
+
+	link = rtnl_link_ipgretap_alloc();
+	if(!link) {
+		nl_perror(err, "Unable to allocate link");
+		return -1;
+
+	}
+	rtnl_link_set_name(link, "ipgre-tap");
+	rtnl_link_ipgre_set_link(link, if_index);
+
+	inet_pton(AF_INET, "10.211.55.10", &addr.s_addr);
+	rtnl_link_ipgre_set_local(link, addr.s_addr);
+
+	inet_pton(AF_INET, "10.133.6.33", &addr.s_addr);
+	rtnl_link_ipgre_set_remote(link, addr.s_addr);
+
+	rtnl_link_ipgre_set_ttl(link, 64);
+	err = rtnl_link_add(sk, link, NLM_F_CREATE);
+	if (err < 0) {
+		nl_perror(err, "Unable to add link");
+		return err;
+	}
+
+	rtnl_link_put(link);
+	nl_close(sk);
+
+	return 0;
+}
diff --git a/tests/test-create-ipvlan.c b/tests/test-create-ipvlan.c
new file mode 100644
index 0000000..50bac54
--- /dev/null
+++ b/tests/test-create-ipvlan.c
@@ -0,0 +1,47 @@
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/ipvlan.h>
+
+#include <linux/netlink.h>
+
+int main(int argc, char *argv[])
+{
+	struct rtnl_link *link;
+	struct nl_cache *link_cache;
+	struct nl_sock *sk;
+	int err, master_index;
+
+	sk = nl_socket_alloc();
+	if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+		nl_perror(err, "Unable to connect socket");
+		return err;
+	}
+
+	if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) {
+		nl_perror(err, "Unable to allocate cache");
+		return err;
+	}
+
+	if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) {
+		fprintf(stderr, "Unable to lookup eth0");
+		return -1;
+	}
+
+	if (!(link = rtnl_link_ipvlan_alloc())) {
+		fprintf(stderr, "Unable to allocate link");
+		return -1;
+	}
+
+	rtnl_link_set_link(link, master_index);
+	rtnl_link_ipvlan_set_mode(link, rtnl_link_ipvlan_str2mode("l2"));
+
+	if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) {
+		nl_perror(err, "Unable to add link");
+		return err;
+	}
+
+	rtnl_link_put(link);
+	nl_close(sk);
+
+	return 0;
+}
diff --git a/tests/test-create-macsec.c b/tests/test-create-macsec.c
new file mode 100644
index 0000000..efadf6b
--- /dev/null
+++ b/tests/test-create-macsec.c
@@ -0,0 +1,51 @@
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#include <linux/netlink.h>
+#include <linux/if_link.h>
+
+#include <netlink/route/link/macsec.h>
+
+int main(int argc, char *argv[])
+{
+	struct rtnl_link *link;
+	struct nl_cache *link_cache;
+	struct nl_sock *sk;
+	int err, master_index;
+
+	sk = nl_socket_alloc();
+	if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+		nl_perror(err, "Unable to connect socket");
+		return err;
+	}
+
+	if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) {
+		nl_perror(err, "Unable to allocate cache");
+		return err;
+	}
+
+	if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) {
+		fprintf(stderr, "Unable to lookup eth0");
+		return -1;
+	}
+
+
+	link = rtnl_link_macsec_alloc();
+
+	rtnl_link_set_link(link, master_index);
+
+	rtnl_link_macsec_set_port(link, 10);
+	rtnl_link_macsec_set_encrypt(link, 1);
+	rtnl_link_macsec_set_replay_protect(link, 1);
+	rtnl_link_macsec_set_window(link, 200);
+
+	if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) {
+		nl_perror(err, "Unable to add link");
+		return err;
+	}
+
+	rtnl_link_put(link);
+	nl_close(sk);
+
+	return 0;
+}
diff --git a/tests/test-create-macvlan.c b/tests/test-create-macvlan.c
index 6477923..f520b67 100644
--- a/tests/test-create-macvlan.c
+++ b/tests/test-create-macvlan.c
@@ -1,7 +1,11 @@
+#include <netinet/ether.h>
+
 #include <netlink/netlink.h>
 #include <netlink/route/link.h>
 #include <netlink/route/link/macvlan.h>
 
+#include <linux/netlink.h>
+
 int main(int argc, char *argv[])
 {
 	struct rtnl_link *link;
diff --git a/tests/test-create-macvtap.c b/tests/test-create-macvtap.c
new file mode 100644
index 0000000..27d1969
--- /dev/null
+++ b/tests/test-create-macvtap.c
@@ -0,0 +1,52 @@
+#include <netinet/ether.h>
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/macvtap.h>
+
+#include <linux/netlink.h>
+
+int main(int argc, char *argv[])
+{
+	struct rtnl_link *link;
+	struct nl_cache *link_cache;
+	struct nl_sock *sk;
+        struct nl_addr* addr;
+	int err, master_index;
+
+	sk = nl_socket_alloc();
+	if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+		nl_perror(err, "Unable to connect socket");
+		return err;
+	}
+
+	if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) {
+		nl_perror(err, "Unable to allocate cache");
+		return err;
+	}
+
+	if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) {
+		fprintf(stderr, "Unable to lookup eth0");
+		return -1;
+	}
+
+	link = rtnl_link_macvtap_alloc();
+
+	rtnl_link_set_link(link, master_index);
+
+	addr = nl_addr_build(AF_LLC, ether_aton("00:11:22:33:44:55"), ETH_ALEN);
+	rtnl_link_set_addr(link, addr);
+	nl_addr_put(addr);
+
+	rtnl_link_macvtap_set_mode(link, rtnl_link_macvtap_str2mode("bridge"));
+
+	if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) {
+		nl_perror(err, "Unable to add link");
+		return err;
+	}
+
+	rtnl_link_put(link);
+	nl_close(sk);
+
+	return 0;
+}
diff --git a/tests/test-create-veth.c b/tests/test-create-veth.c
index db5ab8b..9600f8d 100644
--- a/tests/test-create-veth.c
+++ b/tests/test-create-veth.c
@@ -2,6 +2,8 @@
 #include <netlink/route/link.h>
 #include <netlink/route/link/veth.h>
 
+#include <linux/netlink.h>
+
 int main(int argc, char *argv[])
 {
 	struct rtnl_link *link;
diff --git a/tests/test-create-vlan.c b/tests/test-create-vlan.c
index 64e478f..04756d4 100644
--- a/tests/test-create-vlan.c
+++ b/tests/test-create-vlan.c
@@ -2,6 +2,8 @@
 #include <netlink/route/link.h>
 #include <netlink/route/link/vlan.h>
 
+#include <linux/netlink.h>
+
 int main(int argc, char *argv[])
 {
 	struct rtnl_link *link;
diff --git a/tests/test-create-vrf.c b/tests/test-create-vrf.c
new file mode 100644
index 0000000..c3d23e7
--- /dev/null
+++ b/tests/test-create-vrf.c
@@ -0,0 +1,61 @@
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/vrf.h>
+
+#include <linux/netlink.h>
+
+int main(int argc, char *argv[])
+{
+	struct nl_cache *link_cache;
+	struct rtnl_link *link, *link2;
+	struct nl_sock *sk;
+	uint32_t tb_id;
+	int err;
+
+	sk = nl_socket_alloc();
+	if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+		nl_perror(err, "Unable to connect socket");
+		return err;
+	}
+
+	if (!(link = rtnl_link_vrf_alloc())) {
+		fprintf(stderr, "Unable to allocate link");
+		return -1;
+	}
+
+	rtnl_link_set_name(link, "vrf-red");
+
+	if ((err = rtnl_link_vrf_set_tableid(link, 10)) < 0) {
+		nl_perror(err, "Unable to set VRF table id");
+		return err;
+	}
+
+	if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) {
+		nl_perror(err, "Unable to add link");
+		return err;
+	}
+
+	if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) {
+		nl_perror(err, "Unable to allocate cache");
+		return err;
+	}
+
+	if (!(link2 = rtnl_link_get_by_name(link_cache, "vrf-red"))) {
+		fprintf(stderr, "Unable to lookup vrf-red");
+		return -1;
+	}
+
+	if ((err = rtnl_link_vrf_get_tableid(link2, &tb_id)) < 0) {
+		nl_perror(err, "Unable to get VRF table id");
+		return err;
+	}
+
+	if (tb_id != 10) {
+		fprintf(stderr, "Mismatch with VRF table id\n");
+	}
+
+	rtnl_link_put(link);
+	nl_close(sk);
+
+	return 0;
+}
diff --git a/tests/test-create-vxlan.c b/tests/test-create-vxlan.c
index 98a5103..855fdb5 100644
--- a/tests/test-create-vxlan.c
+++ b/tests/test-create-vxlan.c
@@ -2,6 +2,8 @@
 #include <netlink/route/link.h>
 #include <netlink/route/link/vxlan.h>
 
+#include <linux/netlink.h>
+
 int main(int argc, char *argv[])
 {
 	struct rtnl_link *link;
diff --git a/tests/test-create-xfrmi.c b/tests/test-create-xfrmi.c
new file mode 100644
index 0000000..3a01a4f
--- /dev/null
+++ b/tests/test-create-xfrmi.c
@@ -0,0 +1,49 @@
+#include <netlink/route/link/xfrmi.h>
+#include <netlink-private/netlink.h>
+
+int main(int argc, char *argv[])
+{
+	struct nl_cache *link_cache;
+	struct rtnl_link *link;
+	struct nl_sock *sk;
+	int err, if_index;
+
+	sk = nl_socket_alloc();
+	if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+		nl_perror(err, "Unable to connect socket");
+		return err;
+	}
+
+	err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache);
+	if (err < 0) {
+		nl_perror(err, "Unable to allocate cache");
+		return err;
+	}
+
+	if_index = rtnl_link_name2i(link_cache, "eth0");
+	if (!if_index) {
+		fprintf(stderr, "Unable to lookup eth0");
+		return -1;
+	}
+
+	link = rtnl_link_xfrmi_alloc();
+	if (!link) {
+		nl_perror(err, "Unable to allocate link");
+		return -1;
+
+	}
+
+	rtnl_link_set_name(link, "ipsec0");
+	rtnl_link_xfrmi_set_link(link, if_index);
+	rtnl_link_xfrmi_set_if_id(link, 16);
+
+	err = rtnl_link_add(sk, link, NLM_F_CREATE);
+	if (err < 0) {
+		nl_perror(err, "Unable to add link");
+		return err;
+	}
+
+	rtnl_link_put(link);
+	nl_close(sk);
+	return 0;
+}
diff --git a/tests/test-delete-link.c b/tests/test-delete-link.c
index 9cf1034..8633123 100644
--- a/tests/test-delete-link.c
+++ b/tests/test-delete-link.c
@@ -1,6 +1,8 @@
 #include <netlink/netlink.h>
 #include <netlink/route/link.h>
 
+#include <linux/netlink.h>
+
 int main(int argc, char *argv[])
 {
 	struct rtnl_link *link;
diff --git a/tests/test-genl.c b/tests/test-genl.c
index 74aea10..42db501 100644
--- a/tests/test-genl.c
+++ b/tests/test-genl.c
@@ -1,5 +1,7 @@
 #include <netlink/cli/utils.h>
+
 #include <linux/taskstats.h>
+#include <linux/genetlink.h>
 
 static struct nla_policy attr_policy[TASKSTATS_TYPE_MAX+1] = {
 	[TASKSTATS_TYPE_PID]	= { .type = NLA_U32 },
@@ -99,7 +101,7 @@
 	if ((err = nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, 1)) < 0)
 		nl_cli_fatal(err, "Unable to add attribute: %s", nl_geterror(err));
 
-	if ((err = nl_send_auto_complete(sock, msg)) < 0)
+	if ((err = nl_send_auto(sock, msg)) < 0)
 		nl_cli_fatal(err, "Unable to send message: %s", nl_geterror(err));
 
 	nlmsg_free(msg);
diff --git a/tests/test-loopback-up-down.c b/tests/test-loopback-up-down.c
new file mode 100644
index 0000000..5a7bcb1
--- /dev/null
+++ b/tests/test-loopback-up-down.c
@@ -0,0 +1,54 @@
+#include <net/if.h>
+#include <netlink/route/link.h>
+
+int main(void)
+{
+	struct nl_sock *sk;
+	struct rtnl_link *link, *change;
+	struct nl_cache *cache;
+	int err = 0;
+
+	sk = nl_socket_alloc();
+	if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+		nl_perror(err, "Unable to connect socket");
+		return err;
+	}
+
+	if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &cache)) < 0) {
+		nl_perror(err, "Unable to allocate cache");
+		goto out;
+	}
+
+	if (!(link = rtnl_link_get_by_name(cache, "lo"))) {
+		fprintf(stderr, "Interface not found\n");
+		err = 1;
+		goto out;
+	}
+
+	/* exit if the loopback interface is already deactivated */
+	err = rtnl_link_get_flags(link);
+	if (!(err & IFF_UP)) {
+		err = 0;
+		goto out;
+	}
+
+	change = rtnl_link_alloc();
+	rtnl_link_unset_flags(change, IFF_UP);
+
+	if ((err = rtnl_link_change(sk, link, change, 0)) < 0) {
+		nl_perror(err, "Unable to deactivate lo");
+		goto out;
+	}
+
+	rtnl_link_set_flags(change, IFF_UP);
+	if ((err = rtnl_link_change(sk, link, change, 0)) < 0) {
+		nl_perror(err, "Unable to activate lo");
+		goto out;
+	}
+
+	err = 0;
+
+out:
+	nl_socket_free(sk);
+	return err;
+}
diff --git a/tests/test-nf-cache-mngr.c b/tests/test-nf-cache-mngr.c
index b4f3022..7625e3f 100644
--- a/tests/test-nf-cache-mngr.c
+++ b/tests/test-nf-cache-mngr.c
@@ -1,5 +1,7 @@
 #include <netlink/cli/utils.h>
 
+#include <linux/netlink.h>
+
 static void change_cb(struct nl_cache *cache, struct nl_object *obj,
 		      int action, void *data)
 {
diff --git a/tests/test-u32-filter-with-actions.c b/tests/test-u32-filter-with-actions.c
index 55f913a..e9910e3 100644
--- a/tests/test-u32-filter-with-actions.c
+++ b/tests/test-u32-filter-with-actions.c
@@ -18,13 +18,17 @@
 #include <netlink/route/classifier.h>
 #include <netlink/route/action.h>
 #include <netlink/route/act/mirred.h>
+#include <netlink/route/act/skbedit.h>
 #include <netlink/route/class.h>
-#include <linux/if_ether.h>
-
 #include <netlink/attr.h>
+
 #include <stdio.h>
 #include <string.h>
 
+#include <linux/if_ether.h>
+#include <linux/tc_act/tc_mirred.h>
+#include <linux/netlink.h>
+
 #define 	TC_HANDLE(maj, min)   (TC_H_MAJ((maj) << 16) | TC_H_MIN(min))
 
 /* some functions are copied from iproute-tc tool */
@@ -101,7 +105,7 @@
 static
 int u32_add_filter_on_ht_with_hashmask(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, 
 	    uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask,
-	    uint32_t htid, uint32_t htlink, uint32_t hmask, uint32_t hoffset, struct rtnl_act *act)
+	    uint32_t htid, uint32_t htlink, uint32_t hmask, uint32_t hoffset, struct rtnl_act *act, struct rtnl_act *act2)
 {
     struct rtnl_cls *cls;
     int err;
@@ -136,6 +140,8 @@
 
     rtnl_u32_add_action(cls, act);
 
+    rtnl_u32_add_action(cls, act2);
+
 
     if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) {
         printf("Can not add classifier: %s\n", nl_geterror(err));
@@ -236,7 +242,8 @@
     char chashlink[16]="";
     int err;
     struct nl_cache *link_cache;
-    struct rtnl_act *act;
+    struct rtnl_act *act, *act2;
+    uint32_t i;
 
     if (!(sock = nl_socket_alloc())) {
         printf("Unable to allocate netlink socket\n");
@@ -284,7 +291,6 @@
      * each entry in hash table match a byte from IP address specified later by a hash key
      */
 
-    uint32_t i;
     for (i = 1; i <= 0xf; i++) 
 	u32_add_ht(sock, link, 1, i, 256);
 
@@ -354,7 +360,7 @@
 
     u32_add_filter_on_ht_with_hashmask(sock, link, 1, 
 	    0x0, 0x0, direction, 0,
-	    0, htlink, 0xff000000, direction, NULL);
+	    0, htlink, 0xff000000, direction, NULL, NULL);
 
     /*
      * For each first byte that we need to match we will create a new hash table
@@ -373,16 +379,24 @@
      * previous steps.
      *
      */
-    
     act = rtnl_act_alloc();
     if (!act) {
             printf("rtnl_act_alloc() returns %p\n", act);
             return -1;
-   }
-    rtnl_tc_set_kind(TC_CAST(act), "mirred");
-    rtnl_mirred_set_action(act, TCA_EGRESS_REDIR);
-    rtnl_mirred_set_policy(act, TC_ACT_STOLEN);
-    rtnl_mirred_set_ifindex(act, rtnl_link_name2i(link_cache, "eth1"));
+    }
+    rtnl_tc_set_kind(TC_CAST(act), "skbedit");
+    rtnl_skbedit_set_queue_mapping(act, 4);
+    rtnl_skbedit_set_action(act, TC_ACT_PIPE);
+
+    act2 = rtnl_act_alloc();
+    if (!act2) {
+            printf("rtnl_act_alloc() returns %p\n", act2);
+            return -1;
+    }
+    rtnl_tc_set_kind(TC_CAST(act2), "mirred");
+    rtnl_mirred_set_action(act2, TCA_EGRESS_REDIR);
+    rtnl_mirred_set_policy(act2, TC_ACT_STOLEN);
+    rtnl_mirred_set_ifindex(act2, rtnl_link_name2i(link_cache, "eth1"));
     // /8 check
 
     // 10.0.0.0/8
@@ -392,7 +406,7 @@
 
     u32_add_filter_on_ht_with_hashmask(sock, link, 1, 
 	    0x0a000000, 0xff000000, direction, 0,
-	    htid, htlink, 0x00ff0000, direction, act);
+	    htid, htlink, 0x00ff0000, direction, act, act2);
 
     rtnl_act_put(act);
     nl_socket_free(sock);
diff --git a/tests/util.h b/tests/util.h
index c675383..8c9acf1 100644
--- a/tests/util.h
+++ b/tests/util.h
@@ -3,3 +3,8 @@
 #define nl_fail_if(condition, error, message) \
 	fail_if((condition), "nlerr=%d (%s): %s", \
 		(error), nl_geterror(error), (message))
+
+Suite *make_nl_attr_suite(void);
+Suite *make_nl_addr_suite(void);
+Suite *make_nl_ematch_tree_clone_suite(void);
+
diff --git a/tools/build_release.sh b/tools/build_release.sh
new file mode 100755
index 0000000..ca63be8
--- /dev/null
+++ b/tools/build_release.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+
+# script to create libnl release.
+# Steps:
+# - create new commit, bumping version number
+# - run this script
+# - check all is good
+# - tag the commit (signed)
+#     git tag -m 'libnl-3.2.26-rc1' -s libnl3_2_26rc1 HEAD
+# - publish the tarballs
+# - push the commit to github
+# - publish the tag on github
+# - publish the tarballs on github
+# - send ANN email
+
+
+die() {
+    printf '%s\n' "$@"
+    exit 1
+}
+
+set -x
+set -e
+
+cd "$(dirname "$0")/.."
+git_dir="$(readlink -f "$(git rev-parse --show-toplevel)")"
+test -f "$git_dir/tools/build_release.sh"
+
+Build() {
+    test "$(git status --porcelain)" = "" || die "there are uncommited changes"
+    git clean -fdx
+    ./autogen.sh
+    ./configure
+    pushd ./doc/
+        ./autogen.sh
+        ./configure --enable-doc
+    popd
+    make -j 5
+    make -C doc
+    make -C doc gendoc
+    make -j 5 distcheck
+    make -C doc dist
+    echo "Build: success"
+}
+
+Copy() {
+    local V="$(ls -1 ./libnl-*.tar.gz | sed -n 's/^\.\/libnl-\(3\.[0-9]\+\.[0-9]\+\(-rc[0-9]\)\?\).tar.gz$/\1/p')"
+    test -n "$V"
+    local REL="libnl-$V"
+    rm -rf "./$REL"
+    mkdir "./$REL"
+    ln "./libnl-$V.tar.gz" "./$REL/"
+    ln "./doc/libnl-doc-$V.tar.gz" "./$REL/"
+    (
+        cd "./$REL/"
+        for F in "libnl-$V.tar.gz" "libnl-doc-$V.tar.gz"; do
+            md5sum "./$F" > "./$F.md5sum"
+            sha256sum "./$F" > "./$F.sha256sum"
+            gpg ${GPG_USER--u thaller@redhat.com} --armor --verbose -o "./$F.sig" --detach-sign "./$F"
+        done
+    )
+    tar -cvf "./$REL.tar" "./$REL/"
+    echo "Copy: success"
+}
+
+BuildAll() {
+     Build || return
+     Copy || return
+     echo "BuildAll: success"
+}
+
+case "$1" in
+    Build)
+        Build
+        ;;
+    Copy)
+        Copy
+        ;;
+    BuildAll)
+        BuildAll
+        ;;
+    *)
+        echo "SYNOPSIS: $0 Build|Copy|BuildAll"
+        echo "WARNING: does a git-clean first!!"
+        ;;
+esac