Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2
diff --git a/Makefile b/Makefile
index 46a5ad9..d4eed71 100644
--- a/Makefile
+++ b/Makefile
@@ -30,7 +30,9 @@
 HOSTCC = gcc
 DEFINES += -D_GNU_SOURCE
 CCOPTS = -O2
-WFLAGS = -Wall -Wstrict-prototypes
+WFLAGS := -Wall -Wstrict-prototypes -Werror -Wmissing-prototypes
+WFLAGS += -Wmissing-declarations -Wold-style-definition
+
 CFLAGS = $(WFLAGS) $(CCOPTS) -I../include $(DEFINES)
 YACCFLAGS = -d -t -v
 
diff --git a/README b/README
index 99d1aeb..c7a5118 100644
--- a/README
+++ b/README
@@ -4,7 +4,7 @@
     http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2
 
 Download:
-    http://devresources.linuxfoundation.org/dev/iproute2/download
+    http://www.kernel.org/pub/linux/utils/net/iproute2/
 
 Repository:
     git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git
@@ -37,7 +37,7 @@
    kernel include files.
 
 Stephen Hemminger
-shemminger@linux-foundation.org
+stephen@networkplumber.org
 
 Alexey Kuznetsov
 kuznet@ms2.inr.ac.ru
diff --git a/bridge/Makefile b/bridge/Makefile
index 9a6743e..67aceb4 100644
--- a/bridge/Makefile
+++ b/bridge/Makefile
@@ -1,4 +1,4 @@
-BROBJ = bridge.o fdb.o monitor.o link.o
+BROBJ = bridge.o fdb.o monitor.o link.o mdb.o
 
 include ../Config
 
diff --git a/bridge/br_common.h b/bridge/br_common.h
index 718ecb9..10f6ce9 100644
--- a/bridge/br_common.h
+++ b/bridge/br_common.h
@@ -3,12 +3,15 @@
 			  void *arg);
 extern int print_fdb(const struct sockaddr_nl *who,
 		     struct nlmsghdr *n, void *arg);
+extern int print_mdb(const struct sockaddr_nl *who,
+		     struct nlmsghdr *n, void *arg);
 
 extern int do_fdb(int argc, char **argv);
+extern int do_mdb(int argc, char **argv);
 extern int do_monitor(int argc, char **argv);
 
 extern int preferred_family;
 extern int show_stats;
-extern int show_detail;
+extern int show_details;
 extern int timestamp;
 extern struct rtnl_handle rth;
diff --git a/bridge/bridge.c b/bridge/bridge.c
index e2c33b0..1d59a1e 100644
--- a/bridge/bridge.c
+++ b/bridge/bridge.c
@@ -27,7 +27,7 @@
 {
 	fprintf(stderr,
 "Usage: bridge [ OPTIONS ] OBJECT { COMMAND | help }\n"
-"where  OBJECT := { fdb |  monitor }\n"
+"where  OBJECT := { fdb |  mdb | monitor }\n"
 "       OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails]\n" );
 	exit(-1);
 }
@@ -43,6 +43,7 @@
 	int (*func)(int argc, char **argv);
 } cmds[] = {
 	{ "fdb", 	do_fdb },
+	{ "mdb", 	do_mdb },
 	{ "monitor",	do_monitor },
 	{ "help",	do_help },
 	{ 0 }
diff --git a/bridge/mdb.c b/bridge/mdb.c
new file mode 100644
index 0000000..81d479b
--- /dev/null
+++ b/bridge/mdb.c
@@ -0,0 +1,254 @@
+/*
+ * Get mdb table with netlink
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <linux/if_bridge.h>
+#include <linux/if_ether.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "libnetlink.h"
+#include "br_common.h"
+#include "rt_names.h"
+#include "utils.h"
+
+#ifndef MDBA_RTA
+#define MDBA_RTA(r) \
+	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg))))
+#endif
+
+int filter_index;
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: bridge mdb { add | del } dev DEV port PORT grp GROUP [permanent | temp]\n");
+	fprintf(stderr, "       bridge mdb {show} [ dev DEV ]\n");
+	exit(-1);
+}
+
+static void br_print_router_ports(FILE *f, struct rtattr *attr)
+{
+	uint32_t *port_ifindex;
+	struct rtattr *i;
+	int rem;
+
+	rem = RTA_PAYLOAD(attr);
+	for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+		port_ifindex = RTA_DATA(i);
+		fprintf(f, "%s ", ll_index_to_name(*port_ifindex));
+	}
+
+	fprintf(f, "\n");
+}
+
+static void print_mdb_entry(FILE *f, int ifindex, struct br_mdb_entry *e)
+{
+	SPRINT_BUF(abuf);
+
+	if (e->addr.proto == htons(ETH_P_IP))
+		fprintf(f, "dev %s port %s grp %s %s\n", ll_index_to_name(ifindex),
+			ll_index_to_name(e->ifindex),
+			inet_ntop(AF_INET, &e->addr.u.ip4, abuf, sizeof(abuf)),
+			(e->state & MDB_PERMANENT) ? "permanent" : "temp");
+	else
+		fprintf(f, "dev %s port %s grp %s %s\n", ll_index_to_name(ifindex),
+			ll_index_to_name(e->ifindex),
+			inet_ntop(AF_INET6, &e->addr.u.ip6, abuf, sizeof(abuf)),
+			(e->state & MDB_PERMANENT) ? "permanent" : "temp");
+}
+
+static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr)
+{
+	struct rtattr *i;
+	int rem;
+	struct br_mdb_entry *e;
+
+	rem = RTA_PAYLOAD(attr);
+	for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+		e = RTA_DATA(i);
+		print_mdb_entry(f, ifindex, e);
+	}
+}
+
+int print_mdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = arg;
+	struct br_port_msg *r = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr * tb[MDBA_MAX+1];
+
+	if (n->nlmsg_type != RTM_GETMDB && n->nlmsg_type != RTM_NEWMDB && n->nlmsg_type != RTM_DELMDB) {
+		fprintf(stderr, "Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+
+		return 0;
+	}
+
+	len -= NLMSG_LENGTH(sizeof(*r));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	if (filter_index && filter_index != r->ifindex)
+		return 0;
+
+	parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
+
+	if (tb[MDBA_MDB]) {
+		struct rtattr *i;
+		int rem = RTA_PAYLOAD(tb[MDBA_MDB]);
+
+		for (i = RTA_DATA(tb[MDBA_MDB]); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
+			br_print_mdb_entry(fp, r->ifindex, i);
+	}
+
+	if (tb[MDBA_ROUTER]) {
+		if (show_details) {
+			fprintf(fp, "router ports on %s: ", ll_index_to_name(r->ifindex));
+			br_print_router_ports(fp, tb[MDBA_ROUTER]);
+		}
+	}
+
+	return 0;
+}
+
+static int mdb_show(int argc, char **argv)
+{
+	char *filter_dev = NULL;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			if (filter_dev)
+				duparg("dev", *argv);
+			filter_dev = *argv;
+		}
+		argc--; argv++;
+	}
+
+	if (filter_dev) {
+		filter_index = if_nametoindex(filter_dev);
+		if (filter_index == 0) {
+			fprintf(stderr, "Cannot find device \"%s\"\n",
+				filter_dev);
+			return -1;
+		}
+	}
+
+	if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETMDB) < 0) {
+		perror("Cannot send dump request");
+		exit(1);
+	}
+
+	if (rtnl_dump_filter(&rth, print_mdb, stdout) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		exit(1);
+	}
+
+	return 0;
+}
+
+static int mdb_modify(int cmd, int flags, int argc, char **argv)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct br_port_msg	bpm;
+		char   			buf[1024];
+	} req;
+	struct br_mdb_entry entry;
+	char *d = NULL, *p = NULL, *grp = NULL;
+
+	memset(&req, 0, sizeof(req));
+	memset(&entry, 0, sizeof(entry));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg));
+	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+	req.n.nlmsg_type = cmd;
+	req.bpm.family = PF_BRIDGE;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			d = *argv;
+		} else if (strcmp(*argv, "grp") == 0) {
+			NEXT_ARG();
+			grp = *argv;
+		} else if (strcmp(*argv, "port") == 0) {
+			NEXT_ARG();
+			p = *argv;
+		} else if (strcmp(*argv, "permanent") == 0) {
+			if (cmd == RTM_NEWMDB)
+				entry.state |= MDB_PERMANENT;
+		} else if (strcmp(*argv, "temp") == 0) {
+			;/* nothing */
+		} else {
+			if (matches(*argv, "help") == 0)
+				usage();
+		}
+		argc--; argv++;
+	}
+
+	if (d == NULL || grp == NULL || p == NULL) {
+		fprintf(stderr, "Device, group address and port name are required arguments.\n");
+		exit(-1);
+	}
+
+	req.bpm.ifindex = ll_name_to_index(d);
+	if (req.bpm.ifindex == 0) {
+		fprintf(stderr, "Cannot find device \"%s\"\n", d);
+		return -1;
+	}
+
+	entry.ifindex = ll_name_to_index(p);
+	if (entry.ifindex == 0) {
+		fprintf(stderr, "Cannot find device \"%s\"\n", p);
+		return -1;
+	}
+
+	if (!inet_pton(AF_INET, grp, &entry.addr.u.ip4)) {
+		if (!inet_pton(AF_INET6, grp, &entry.addr.u.ip6)) {
+			fprintf(stderr, "Invalid address \"%s\"\n", grp);
+			return -1;
+		} else
+			entry.addr.proto = htons(ETH_P_IPV6);
+	} else
+		entry.addr.proto = htons(ETH_P_IP);
+
+	addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+		exit(2);
+
+	return 0;
+}
+
+int do_mdb(int argc, char **argv)
+{
+	ll_init_map(&rth);
+
+	if (argc > 0) {
+		if (matches(*argv, "add") == 0)
+			return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
+		if (matches(*argv, "delete") == 0)
+			return mdb_modify(RTM_DELMDB, 0, argc-1, argv+1);
+
+		if (matches(*argv, "show") == 0 ||
+		    matches(*argv, "lst") == 0 ||
+		    matches(*argv, "list") == 0)
+			return mdb_show(argc-1, argv+1);
+		if (matches(*argv, "help") == 0)
+			usage();
+	} else
+		return mdb_show(0, NULL);
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"bridge mdb help\".\n", *argv);
+	exit(-1);
+}
diff --git a/bridge/monitor.c b/bridge/monitor.c
index 2f60655..e96fcaf 100644
--- a/bridge/monitor.c
+++ b/bridge/monitor.c
@@ -31,7 +31,7 @@
 
 static void usage(void)
 {
-	fprintf(stderr, "Usage: bridge monitor\n");
+	fprintf(stderr, "Usage: bridge monitor [file | link | fdb | mdb | all]\n");
 	exit(-1);
 }
 
@@ -46,8 +46,8 @@
 	return 0;
 }
 
-int accept_msg(const struct sockaddr_nl *who,
-	       struct nlmsghdr *n, void *arg)
+static int accept_msg(const struct sockaddr_nl *who,
+		      struct nlmsghdr *n, void *arg)
 {
 	FILE *fp = arg;
 
@@ -68,6 +68,12 @@
 			fprintf(fp, "[NEIGH]");
 		return print_fdb(who, n, arg);
 
+	case RTM_NEWMDB:
+	case RTM_DELMDB:
+		if (prefix_banner)
+			fprintf(fp, "[MDB]");
+		return print_mdb(who, n, arg);
+
 	case 15:
 		return show_mark(fp, n);
 
@@ -84,6 +90,7 @@
 	unsigned groups = ~RTMGRP_TC;
 	int llink=0;
 	int lneigh=0;
+	int lmdb=0;
 
 	rtnl_close(&rth);
 
@@ -97,6 +104,9 @@
 		} else if (matches(*argv, "fdb") == 0) {
 			lneigh = 1;
 			groups = 0;
+		} else if (matches(*argv, "mdb") == 0) {
+			lmdb = 1;
+			groups = 0;
 		} else if (strcmp(*argv, "all") == 0) {
 			groups = ~RTMGRP_TC;
 			prefix_banner=1;
@@ -116,6 +126,10 @@
 		groups |= nl_mgrp(RTNLGRP_NEIGH);
 	}
 
+	if (lmdb) {
+		groups |= nl_mgrp(RTNLGRP_MDB);
+	}
+
 	if (file) {
 		FILE *fp;
 		fp = fopen(file, "r");
diff --git a/configure b/configure
index 9912114..da01c19 100755
--- a/configure
+++ b/configure
@@ -1,18 +1,25 @@
 #! /bin/bash
-# This is not an autconf generated configure
+# This is not an autoconf generated configure
 #
 INCLUDE=${1:-"$PWD/include"}
-: ${PKG_CONFIG:=pkg-config}
-: ${CC=gcc}
-echo "PKG_CONFIG:=${PKG_CONFIG}" >>Config
 
 # Make a temp directory in build tree.
 TMPDIR=$(mktemp -d config.XXXXXX)
 trap 'status=$?; rm -rf $TMPDIR; exit $status' EXIT HUP INT QUIT TERM
 
+check_toolchain()
+{
+    : ${PKG_CONFIG:=pkg-config}
+    : ${AR=ar}
+    : ${CC=gcc}
+    echo "PKG_CONFIG:=${PKG_CONFIG}" >>Config
+    echo "AR:=${AR}" >>Config
+    echo "CC:=${CC}" >>Config
+}
+
 check_atm()
 {
-cat >$TMPDIR/atmtest.c <<EOF
+    cat >$TMPDIR/atmtest.c <<EOF
 #include <atm.h>
 int main(int argc, char **argv) {
 	struct atm_qos qos;
@@ -20,21 +27,22 @@
 	return 0;
 }
 EOF
-$CC -I$INCLUDE -o $TMPDIR/atmtest $TMPDIR/atmtest.c -latm >/dev/null 2>&1
-if [ $? -eq 0 ]
-then
-    echo "TC_CONFIG_ATM:=y" >>Config
-    echo yes
-else
-    echo no
-fi
-rm -f $TMPDIR/atmtest.c $TMPDIR/atmtest
+
+    $CC -I$INCLUDE -o $TMPDIR/atmtest $TMPDIR/atmtest.c -latm >/dev/null 2>&1
+    if [ $? -eq 0 ]
+    then
+	echo "TC_CONFIG_ATM:=y" >>Config
+	echo yes
+    else
+	echo no
+    fi
+    rm -f $TMPDIR/atmtest.c $TMPDIR/atmtest
 }
 
 check_xt()
 {
-#check if we have xtables from iptables >= 1.4.5.
-cat >$TMPDIR/ipttest.c <<EOF
+    #check if we have xtables from iptables >= 1.4.5.
+    cat >$TMPDIR/ipttest.c <<EOF
 #include <xtables.h>
 #include <linux/netfilter.h>
 static struct xtables_globals test_globals = {
@@ -51,27 +59,27 @@
 	xtables_init_all(&test_globals, NFPROTO_IPV4);
 	return 0;
 }
-
 EOF
 
-if $CC -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL $(${PKG_CONFIG} xtables --cflags --libs) -ldl >/dev/null 2>&1
-then
+    if $CC -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL \
+	$(${PKG_CONFIG} xtables --cflags --libs) -ldl >/dev/null 2>&1
+    then
 	echo "TC_CONFIG_XT:=y" >>Config
 	echo "using xtables"
-fi
-rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest
+    fi
+    rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest
 }
 
 check_xt_old()
 {
-# bail if previous XT checks has already succeded.
-if grep TC_CONFIG_XT Config > /dev/null
-then
+    # bail if previous XT checks has already succeded.
+    if grep -q TC_CONFIG_XT Config
+    then
 	return
-fi
+    fi
 
-#check if we dont need our internal header ..
-cat >$TMPDIR/ipttest.c <<EOF
+    #check if we dont need our internal header ..
+    cat >$TMPDIR/ipttest.c <<EOF
 #include <xtables.h>
 char *lib_dir;
 unsigned int global_option_offset = 0;
@@ -91,26 +99,26 @@
 }
 
 EOF
-$CC -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL -ldl >/dev/null 2>&1
 
-if [ $? -eq 0 ]
-then
+    $CC -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL -ldl >/dev/null 2>&1
+    if [ $? -eq 0 ]
+    then
 	echo "TC_CONFIG_XT_OLD:=y" >>Config
 	echo "using old xtables (no need for xt-internal.h)"
-fi
-rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest
+    fi
+    rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest
 }
 
 check_xt_old_internal_h()
 {
-# bail if previous XT checks has already succeded.
-if grep TC_CONFIG_XT Config > /dev/null
-then
+    # bail if previous XT checks has already succeded.
+    if grep -q TC_CONFIG_XT Config
+    then
 	return
-fi
+    fi
 
-#check if we need our own internal.h
-cat >$TMPDIR/ipttest.c <<EOF
+    #check if we need our own internal.h
+    cat >$TMPDIR/ipttest.c <<EOF
 #include <xtables.h>
 #include "xt-internal.h"
 char *lib_dir;
@@ -131,14 +139,14 @@
 }
 
 EOF
-$CC -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL -ldl >/dev/null 2>&1
+	$CC -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL -ldl >/dev/null 2>&1
 
-if [ $? -eq 0 ]
-then
-	echo "using old xtables with xt-internal.h"
-	echo "TC_CONFIG_XT_OLD_H:=y" >>Config
-fi
-rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest
+	if [ $? -eq 0 ]
+	then
+	    echo "using old xtables with xt-internal.h"
+	    echo "TC_CONFIG_XT_OLD_H:=y" >>Config
+	fi
+	rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest
 }
 
 check_ipt()
@@ -173,7 +181,7 @@
 
 check_setns()
 {
-cat >$TMPDIR/setnstest.c <<EOF
+    cat >$TMPDIR/setnstest.c <<EOF
 #include <sched.h>
 int main(int argc, char **argv) 
 {
@@ -181,20 +189,20 @@
 	return 0;
 }
 EOF
-$CC -I$INCLUDE -o $TMPDIR/setnstest $TMPDIR/setnstest.c >/dev/null 2>&1
-if [ $? -eq 0 ]
-then
+    $CC -I$INCLUDE -o $TMPDIR/setnstest $TMPDIR/setnstest.c >/dev/null 2>&1
+    if [ $? -eq 0 ]
+    then
 	echo "IP_CONFIG_SETNS:=y" >>Config
 	echo "yes"
-else
+    else
 	echo "no"
-fi
-rm -f $TMPDIR/setnstest.c $TMPDIR/setnstest
+    fi
+    rm -f $TMPDIR/setnstest.c $TMPDIR/setnstest
 }
 
 check_ipset()
 {
-cat >$TMPDIR/ipsettest.c <<EOF
+    cat >$TMPDIR/ipsettest.c <<EOF
 #include <linux/netfilter/ipset/ip_set.h>
 #ifndef IP_SET_INVALID
 #define IPSET_DIM_MAX 3
@@ -213,17 +221,18 @@
 #endif
 EOF
 
-if $CC -I$INCLUDE -o $TMPDIR/ipsettest $TMPDIR/ipsettest.c >/dev/null 2>&1
-then
+    if $CC -I$INCLUDE -o $TMPDIR/ipsettest $TMPDIR/ipsettest.c >/dev/null 2>&1
+    then
 	echo "TC_CONFIG_IPSET:=y" >>Config
 	echo "yes"
-else
+    else
 	echo "no"
-fi
-rm -f $TMPDIR/ipsettest.c $TMPDIR/ipsettest
+    fi
+    rm -f $TMPDIR/ipsettest.c $TMPDIR/ipsettest
 }
 
 echo "# Generated config based on" $INCLUDE >Config
+check_toolchain
 
 echo "TC schedulers"
 
diff --git a/genl/ctrl.c b/genl/ctrl.c
index 6d97c26..7c42578 100644
--- a/genl/ctrl.c
+++ b/genl/ctrl.c
@@ -112,7 +112,7 @@
 	return ret;
 }
 
-void print_ctrl_cmd_flags(FILE *fp, __u32 fl)
+static void print_ctrl_cmd_flags(FILE *fp, __u32 fl)
 {
 	fprintf(fp, "\n\t\tCapabilities (0x%x):\n ", fl);
 	if (!fl) {
diff --git a/include/SNAPSHOT.h b/include/SNAPSHOT.h
index d4fc137..b373873 100644
--- a/include/SNAPSHOT.h
+++ b/include/SNAPSHOT.h
@@ -1 +1 @@
-static const char SNAPSHOT[] = "121001";
+static const char SNAPSHOT[] = "121211";
diff --git a/include/libnetlink.h b/include/libnetlink.h
index 81649af..41e6ed1 100644
--- a/include/libnetlink.h
+++ b/include/libnetlink.h
@@ -1,6 +1,7 @@
 #ifndef __LIBNETLINK_H__
 #define __LIBNETLINK_H__ 1
 
+#include <stdio.h>
 #include <string.h>
 #include <asm/types.h>
 #include <linux/netlink.h>
@@ -8,6 +9,7 @@
 #include <linux/if_link.h>
 #include <linux/if_addr.h>
 #include <linux/neighbour.h>
+#include <linux/netconf.h>
 
 struct rtnl_handle
 {
diff --git a/include/linux/atm.h b/include/linux/atm.h
index 2fafdfb..08e27be 100644
--- a/include/linux/atm.h
+++ b/include/linux/atm.h
@@ -8,8 +8,8 @@
  *          Instead, #include <atm.h>
  */
 
-#ifndef _UAPI_LINUX_ATM_H
-#define _UAPI_LINUX_ATM_H
+#ifndef _LINUX_ATM_H
+#define _LINUX_ATM_H
 
 /*
  * BEGIN_xx and END_xx markers are used for automatic generation of
@@ -238,4 +238,4 @@
 
 
 typedef unsigned short atm_backend_t;
-#endif /* _UAPI_LINUX_ATM_H */
+#endif /* _LINUX_ATM_H */
diff --git a/include/linux/genetlink.h b/include/linux/genetlink.h
index c880a41..1f85a27 100644
--- a/include/linux/genetlink.h
+++ b/include/linux/genetlink.h
@@ -1,5 +1,5 @@
-#ifndef _UAPI__LINUX_GENERIC_NETLINK_H
-#define _UAPI__LINUX_GENERIC_NETLINK_H
+#ifndef __LINUX_GENERIC_NETLINK_H
+#define __LINUX_GENERIC_NETLINK_H
 
 #include <linux/types.h>
 #include <linux/netlink.h>
@@ -81,4 +81,4 @@
 #define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1)
 
 
-#endif /* _UAPI__LINUX_GENERIC_NETLINK_H */
+#endif /* __LINUX_GENERIC_NETLINK_H */
diff --git a/include/linux/hdlc/ioctl.h b/include/linux/hdlc/ioctl.h
index 5839723..04bc027 100644
--- a/include/linux/hdlc/ioctl.h
+++ b/include/linux/hdlc/ioctl.h
@@ -34,13 +34,15 @@
 #define LMI_CCITT		3 /* ITU-T Annex A */
 #define LMI_CISCO		4 /* The "original" LMI, aka Gang of Four */
 
-typedef struct { 
+#ifndef __ASSEMBLY__
+
+typedef struct {
 	unsigned int clock_rate; /* bits per second */
 	unsigned int clock_type; /* internal, external, TX-internal etc. */
 	unsigned short loopback;
 } sync_serial_settings;          /* V.35, V.24, X.21 */
 
-typedef struct { 
+typedef struct {
 	unsigned int clock_rate; /* bits per second */
 	unsigned int clock_type; /* internal, external, TX-internal etc. */
 	unsigned short loopback;
@@ -78,4 +80,5 @@
 
 /* PPP doesn't need any info now - supply length = 0 to ioctl */
 
+#endif /* __ASSEMBLY__ */
 #endif /* __HDLC_IOCTL_H__ */
diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h
index 82c7d1b..6a48d55 100644
--- a/include/linux/if_arp.h
+++ b/include/linux/if_arp.h
@@ -20,8 +20,8 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  */
-#ifndef _UAPI_LINUX_IF_ARP_H
-#define _UAPI_LINUX_IF_ARP_H
+#ifndef _LINUX_IF_ARP_H
+#define _LINUX_IF_ARP_H
 
 #include <linux/netdevice.h>
 
@@ -156,4 +156,4 @@
 };
 
 
-#endif /* _UAPI_LINUX_IF_ARP_H */
+#endif /* _LINUX_IF_ARP_H */
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
new file mode 100644
index 0000000..aac8b8c
--- /dev/null
+++ b/include/linux/if_bridge.h
@@ -0,0 +1,185 @@
+/*
+ *	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>
+
+#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 /* _LINUX_IF_BRIDGE_H */
diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h
index 0343e1f..730410a 100644
--- a/include/linux/if_ether.h
+++ b/include/linux/if_ether.h
@@ -18,8 +18,8 @@
  *		2 of the License, or (at your option) any later version.
  */
 
-#ifndef _UAPI_LINUX_IF_ETHER_H
-#define _UAPI_LINUX_IF_ETHER_H
+#ifndef _LINUX_IF_ETHER_H
+#define _LINUX_IF_ETHER_H
 
 #include <linux/types.h>
 
@@ -48,6 +48,7 @@
 #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       */
@@ -132,4 +133,4 @@
 } __attribute__((packed));
 
 
-#endif /* _UAPI_LINUX_IF_ETHER_H */
+#endif /* _LINUX_IF_ETHER_H */
diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index 012d95a..8ca3afe 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -1,5 +1,5 @@
-#ifndef _UAPI_LINUX_IF_LINK_H
-#define _UAPI_LINUX_IF_LINK_H
+#ifndef _LINUX_IF_LINK_H
+#define _LINUX_IF_LINK_H
 
 #include <linux/types.h>
 #include <linux/netlink.h>
@@ -203,6 +203,24 @@
 
 #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 */
@@ -283,6 +301,10 @@
 	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)
@@ -424,4 +446,4 @@
 
 #define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1)
 
-#endif /* _UAPI_LINUX_IF_LINK_H */
+#endif /* _LINUX_IF_LINK_H */
diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h
index 25a585c..dffb192 100644
--- a/include/linux/if_tun.h
+++ b/include/linux/if_tun.h
@@ -13,8 +13,8 @@
  *  GNU General Public License for more details.
  */
 
-#ifndef _UAPI__IF_TUN_H
-#define _UAPI__IF_TUN_H
+#ifndef __IF_TUN_H
+#define __IF_TUN_H
 
 #include <linux/types.h>
 #include <linux/if_ether.h>
@@ -31,9 +31,11 @@
 #define TUN_FASYNC	0x0010
 #define TUN_NOCHECKSUM	0x0020
 #define TUN_NO_PI	0x0040
+/* This flag has no real effect */
 #define TUN_ONE_QUEUE	0x0080
 #define TUN_PERSIST 	0x0100	
 #define TUN_VNET_HDR 	0x0200
+#define TUN_TAP_MQ      0x0400
 
 /* Ioctl defines */
 #define TUNSETNOCSUM  _IOW('T', 200, int) 
@@ -53,14 +55,19 @@
 #define TUNDETACHFILTER _IOW('T', 214, struct sock_fprog)
 #define TUNGETVNETHDRSZ _IOR('T', 215, int)
 #define TUNSETVNETHDRSZ _IOW('T', 216, int)
+#define TUNSETQUEUE  _IOW('T', 217, int)
 
 /* TUNSETIFF ifr flags */
 #define IFF_TUN		0x0001
 #define IFF_TAP		0x0002
 #define IFF_NO_PI	0x1000
+/* This flag has no real effect */
 #define IFF_ONE_QUEUE	0x2000
 #define IFF_VNET_HDR	0x4000
 #define IFF_TUN_EXCL	0x8000
+#define IFF_MULTI_QUEUE 0x0100
+#define IFF_ATTACH_QUEUE 0x0200
+#define IFF_DETACH_QUEUE 0x0400
 
 /* Features for GSO (TUNSETOFFLOAD). */
 #define TUN_F_CSUM	0x01	/* You can hand me unchecksummed packets. */
@@ -91,4 +98,4 @@
 	__u8   addr[0][ETH_ALEN];
 };
 
-#endif /* _UAPI__IF_TUN_H */
+#endif /* __IF_TUN_H */
diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h
index 5db5942..9f471ca 100644
--- a/include/linux/if_tunnel.h
+++ b/include/linux/if_tunnel.h
@@ -1,5 +1,5 @@
-#ifndef _UAPI_IF_TUNNEL_H_
-#define _UAPI_IF_TUNNEL_H_
+#ifndef _IF_TUNNEL_H_
+#define _IF_TUNNEL_H_
 
 #include <linux/types.h>
 #include <asm/byteorder.h>
@@ -37,6 +37,26 @@
 	struct iphdr		iph;
 };
 
+enum {
+	IFLA_IPTUN_UNSPEC,
+	IFLA_IPTUN_LINK,
+	IFLA_IPTUN_LOCAL,
+	IFLA_IPTUN_REMOTE,
+	IFLA_IPTUN_TTL,
+	IFLA_IPTUN_TOS,
+	IFLA_IPTUN_ENCAP_LIMIT,
+	IFLA_IPTUN_FLOWINFO,
+	IFLA_IPTUN_FLAGS,
+	IFLA_IPTUN_PROTO,
+	IFLA_IPTUN_PMTUDISC,
+	IFLA_IPTUN_6RD_PREFIX,
+	IFLA_IPTUN_6RD_RELAY_PREFIX,
+	IFLA_IPTUN_6RD_PREFIXLEN,
+	IFLA_IPTUN_6RD_RELAY_PREFIXLEN,
+	__IFLA_IPTUN_MAX,
+};
+#define IFLA_IPTUN_MAX	(__IFLA_IPTUN_MAX - 1)
+
 /* SIT-mode i_flags */
 #define	SIT_ISATAP	0x0001
 
@@ -93,4 +113,4 @@
 };
 
 #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/if_vlan.h
index 0744f8e..3be1ca6 100644
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -10,8 +10,8 @@
  *
  */
 
-#ifndef _UAPI_LINUX_IF_VLAN_H_
-#define _UAPI_LINUX_IF_VLAN_H_
+#ifndef _LINUX_IF_VLAN_H_
+#define _LINUX_IF_VLAN_H_
 
 
 /* VLAN IOCTLs are found in sockios.h */
@@ -60,4 +60,4 @@
 	short vlan_qos;   
 };
 
-#endif /* _UAPI_LINUX_IF_VLAN_H_ */
+#endif /* _LINUX_IF_VLAN_H_ */
diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h
index 8c469af..e34f247 100644
--- a/include/linux/inet_diag.h
+++ b/include/linux/inet_diag.h
@@ -1,5 +1,5 @@
-#ifndef _UAPI_INET_DIAG_H_
-#define _UAPI_INET_DIAG_H_
+#ifndef _INET_DIAG_H_
+#define _INET_DIAG_H_
 
 #include <linux/types.h>
 
@@ -109,9 +109,10 @@
 	INET_DIAG_TOS,
 	INET_DIAG_TCLASS,
 	INET_DIAG_SKMEMINFO,
+	INET_DIAG_SHUTDOWN,
 };
 
-#define INET_DIAG_MAX INET_DIAG_SKMEMINFO
+#define INET_DIAG_MAX INET_DIAG_SHUTDOWN
 
 
 /* INET_DIAG_MEM */
@@ -133,4 +134,4 @@
 };
 
 
-#endif /* _UAPI_INET_DIAG_H_ */
+#endif /* _INET_DIAG_H_ */
diff --git a/include/linux/l2tp.h b/include/linux/l2tp.h
index 02a567b..c4bec82 100644
--- a/include/linux/l2tp.h
+++ b/include/linux/l2tp.h
@@ -4,8 +4,8 @@
  * Author: James Chapman <jchapman@katalix.com>
  */
 
-#ifndef _UAPI_LINUX_L2TP_H_
-#define _UAPI_LINUX_L2TP_H_
+#ifndef _LINUX_L2TP_H_
+#define _LINUX_L2TP_H_
 
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -175,4 +175,4 @@
 #define L2TP_GENL_NAME		"l2tp"
 #define L2TP_GENL_VERSION	0x1
 
-#endif /* _UAPI_LINUX_L2TP_H_ */
+#endif /* _LINUX_L2TP_H_ */
diff --git a/include/linux/netconf.h b/include/linux/netconf.h
new file mode 100644
index 0000000..52c4424
--- /dev/null
+++ b/include/linux/netconf.h
@@ -0,0 +1,24 @@
+#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_MAX
+};
+#define NETCONFA_MAX	(__NETCONFA_MAX - 1)
+
+#define NETCONFA_IFINDEX_ALL		-1
+#define NETCONFA_IFINDEX_DEFAULT	-2
+
+#endif /* _LINUX_NETCONF_H_ */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 6b9500b..adc7260 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -22,8 +22,8 @@
  *
  *		Moved to /usr/include/linux for NET3
  */
-#ifndef _UAPI_LINUX_NETDEVICE_H
-#define _UAPI_LINUX_NETDEVICE_H
+#ifndef _LINUX_NETDEVICE_H
+#define _LINUX_NETDEVICE_H
 
 #include <linux/if.h>
 #include <linux/if_ether.h>
@@ -50,4 +50,4 @@
 };
 
 
-#endif /* _UAPI_LINUX_NETDEVICE_H */
+#endif /* _LINUX_NETDEVICE_H */
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index 9961e7f..f05c3d9 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -1,5 +1,5 @@
-#ifndef _UAPI__LINUX_NETFILTER_H
-#define _UAPI__LINUX_NETFILTER_H
+#ifndef __LINUX_NETFILTER_H
+#define __LINUX_NETFILTER_H
 
 #include <linux/types.h>
 
@@ -67,4 +67,4 @@
 	struct in6_addr	in6;
 };
 
-#endif /* _UAPI__LINUX_NETFILTER_H */
+#endif /* __LINUX_NETFILTER_H */
diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
index 92a06f3..4120970 100644
--- a/include/linux/netfilter/x_tables.h
+++ b/include/linux/netfilter/x_tables.h
@@ -1,5 +1,5 @@
-#ifndef _UAPI_X_TABLES_H
-#define _UAPI_X_TABLES_H
+#ifndef _X_TABLES_H
+#define _X_TABLES_H
 #include <linux/kernel.h>
 #include <linux/types.h>
 
@@ -182,4 +182,4 @@
 	             (pos)->u.match_size))
 
 
-#endif /* _UAPI_X_TABLES_H */
+#endif /* _X_TABLES_H */
diff --git a/include/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h
index 77a3e43..a5f4dc7 100644
--- a/include/linux/netfilter_ipv4.h
+++ b/include/linux/netfilter_ipv4.h
@@ -1,8 +1,8 @@
 /* IPv4-specific defines for netfilter. 
  * (C)1998 Rusty Russell -- This code is GPL.
  */
-#ifndef _UAPI__LINUX_IP_NETFILTER_H
-#define _UAPI__LINUX_IP_NETFILTER_H
+#ifndef __LINUX_IP_NETFILTER_H
+#define __LINUX_IP_NETFILTER_H
 
 
 #include <linux/netfilter.h>
@@ -76,4 +76,4 @@
 #define SO_ORIGINAL_DST 80
 
 
-#endif /* _UAPI__LINUX_IP_NETFILTER_H */
+#endif /* __LINUX_IP_NETFILTER_H */
diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h
index 6ba9b09..38542b4 100644
--- a/include/linux/netfilter_ipv4/ip_tables.h
+++ b/include/linux/netfilter_ipv4/ip_tables.h
@@ -12,8 +12,8 @@
  * 	Port numbers are stored in HOST byte order.
  */
 
-#ifndef _UAPI_IPTABLES_H
-#define _UAPI_IPTABLES_H
+#ifndef _IPTABLES_H
+#define _IPTABLES_H
 
 #include <linux/types.h>
 
@@ -224,4 +224,4 @@
 /*
  *	Main firewall chains definitions and global var's definitions.
  */
-#endif /* _UAPI_IPTABLES_H */
+#endif /* _IPTABLES_H */
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 78d5b8a..ced0e1a 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -1,5 +1,5 @@
-#ifndef _UAPI__LINUX_NETLINK_H
-#define _UAPI__LINUX_NETLINK_H
+#ifndef __LINUX_NETLINK_H
+#define __LINUX_NETLINK_H
 
 #include <linux/socket.h> /* for __kernel_sa_family_t */
 #include <linux/types.h>
@@ -150,4 +150,4 @@
 #define NLA_HDRLEN		((int) NLA_ALIGN(sizeof(struct nlattr)))
 
 
-#endif /* _UAPI__LINUX_NETLINK_H */
+#endif /* __LINUX_NETLINK_H */
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 0e3e0c1..87452b4 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -1,5 +1,5 @@
-#ifndef _UAPI__LINUX_RTNETLINK_H
-#define _UAPI__LINUX_RTNETLINK_H
+#ifndef __LINUX_RTNETLINK_H
+#define __LINUX_RTNETLINK_H
 
 #include <linux/types.h>
 #include <linux/netlink.h>
@@ -120,6 +120,18 @@
 	RTM_SETDCB,
 #define RTM_SETDCB RTM_SETDCB
 
+	RTM_NEWNETCONF = 80,
+#define RTM_NEWNETCONF RTM_NEWNETCONF
+	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_MAX,
 #define RTM_MAX		(((__RTM_MAX + 3) & ~3) - 1)
 };
@@ -222,6 +234,7 @@
 #define RTPROT_XORP	14	/* XORP */
 #define RTPROT_NTK	15	/* Netsukuku */
 #define RTPROT_DHCP	16      /* DHCP client */
+#define RTPROT_MROUTED	17      /* Multicast daemon */
 
 /* rtm_scope
 
@@ -283,6 +296,7 @@
 	RTA_MP_ALGO, /* no longer used */
 	RTA_TABLE,
 	RTA_MARK,
+	RTA_MFC_STATS,
 	__RTA_MAX
 };
 
@@ -403,6 +417,12 @@
 	} u;
 };
 
+struct rta_mfc_stats {
+	__u64	mfcs_packets;
+	__u64	mfcs_bytes;
+	__u64	mfcs_wrong_if;
+};
+
 /****
  *		General form of address family dependent message.
  ****/
@@ -585,6 +605,12 @@
 #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_MAX
 };
 #define RTNLGRP_MAX	(__RTNLGRP_MAX - 1)
@@ -607,4 +633,4 @@
 
 
 
-#endif /* _UAPI__LINUX_RTNETLINK_H */
+#endif /* __LINUX_RTNETLINK_H */
diff --git a/include/linux/sock_diag.h b/include/linux/sock_diag.h
index b00e29e..78996e2 100644
--- a/include/linux/sock_diag.h
+++ b/include/linux/sock_diag.h
@@ -1,5 +1,5 @@
-#ifndef _UAPI__SOCK_DIAG_H__
-#define _UAPI__SOCK_DIAG_H__
+#ifndef __SOCK_DIAG_H__
+#define __SOCK_DIAG_H__
 
 #include <linux/types.h>
 
@@ -23,4 +23,4 @@
 	SK_MEMINFO_VARS,
 };
 
-#endif /* _UAPI__SOCK_DIAG_H__ */
+#endif /* __SOCK_DIAG_H__ */
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 76ab0c6..8c1e501 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -1,5 +1,5 @@
-#ifndef _UAPI_LINUX_SOCKET_H
-#define _UAPI_LINUX_SOCKET_H
+#ifndef _LINUX_SOCKET_H
+#define _LINUX_SOCKET_H
 
 /*
  * Desired design of maximum size and alignment (see RFC2553)
@@ -18,4 +18,4 @@
 				/* _SS_MAXSIZE value minus size of ss_family */
 } __attribute__ ((aligned(_K_SS_ALIGNSIZE)));	/* force desired alignment */
 
-#endif /* _UAPI_LINUX_SOCKET_H */
+#endif /* _LINUX_SOCKET_H */
diff --git a/include/linux/types.h b/include/linux/types.h
index 54c5df3..23ea78f 100644
--- a/include/linux/types.h
+++ b/include/linux/types.h
@@ -1,5 +1,5 @@
-#ifndef _UAPI_LINUX_TYPES_H
-#define _UAPI_LINUX_TYPES_H
+#ifndef _LINUX_TYPES_H
+#define _LINUX_TYPES_H
 
 #include <asm/types.h>
 
@@ -48,4 +48,4 @@
 #define __aligned_le64 __le64 __attribute__((aligned(8)))
 
 #endif /*  __ASSEMBLY__ */
-#endif /* _UAPI_LINUX_TYPES_H */
+#endif /* _LINUX_TYPES_H */
diff --git a/include/linux/unix_diag.h b/include/linux/unix_diag.h
index b1d2bf1..b8a2494 100644
--- a/include/linux/unix_diag.h
+++ b/include/linux/unix_diag.h
@@ -37,6 +37,7 @@
 	UNIX_DIAG_ICONS,
 	UNIX_DIAG_RQLEN,
 	UNIX_DIAG_MEMINFO,
+	UNIX_DIAG_SHUTDOWN,
 
 	UNIX_DIAG_MAX,
 };
diff --git a/include/rt_names.h b/include/rt_names.h
index e5dbd45..37adbd3 100644
--- a/include/rt_names.h
+++ b/include/rt_names.h
@@ -3,29 +3,30 @@
 
 #include <asm/types.h>
 
-char* rtnl_rtprot_n2a(int id, char *buf, int len);
-char* rtnl_rtscope_n2a(int id, char *buf, int len);
-char* rtnl_rttable_n2a(__u32 id, char *buf, int len);
-char* rtnl_rtrealm_n2a(int id, char *buf, int len);
-char* rtnl_dsfield_n2a(int id, char *buf, int len);
-int rtnl_rtprot_a2n(__u32 *id, char *arg);
-int rtnl_rtscope_a2n(__u32 *id, char *arg);
-int rtnl_rttable_a2n(__u32 *id, char *arg);
-int rtnl_rtrealm_a2n(__u32 *id, char *arg);
-int rtnl_dsfield_a2n(__u32 *id, char *arg);
-int rtnl_group_a2n(int *id, char *arg);
+const char *rtnl_rtprot_n2a(int id, char *buf, int len);
+const char *rtnl_rtscope_n2a(int id, char *buf, int len);
+const char *rtnl_rttable_n2a(__u32 id, char *buf, int len);
+const char *rtnl_rtrealm_n2a(int id, char *buf, int len);
+const char *rtnl_dsfield_n2a(int id, char *buf, int len);
+
+int rtnl_rtprot_a2n(__u32 *id, const char *arg);
+int rtnl_rtscope_a2n(__u32 *id, const char *arg);
+int rtnl_rttable_a2n(__u32 *id, const char *arg);
+int rtnl_rtrealm_a2n(__u32 *id, const char *arg);
+int rtnl_dsfield_a2n(__u32 *id, const char *arg);
+int rtnl_group_a2n(int *id, const char *arg);
 
 const char *inet_proto_n2a(int proto, char *buf, int len);
-int inet_proto_a2n(char *buf);
+int inet_proto_a2n(const char *buf);
 
 
 const char * ll_type_n2a(int type, char *buf, int len);
-
-const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen);
-int ll_addr_a2n(char *lladdr, int len, char *arg);
+const char *ll_addr_n2a(unsigned char *addr, int alen,
+			int type, char *buf, int blen);
+int ll_addr_a2n(char *lladdr, int len, const char *arg);
 
 const char * ll_proto_n2a(unsigned short id, char *buf, int len);
-int ll_proto_a2n(unsigned short *id, char *buf);
+int ll_proto_a2n(unsigned short *id, const char *buf);
 
 
 #endif
diff --git a/include/utils.h b/include/utils.h
index 496db68..2bd8c62 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -1,6 +1,7 @@
 #ifndef __UTILS_H__
 #define __UTILS_H__ 1
 
+#include <sys/types.h>
 #include <asm/types.h>
 #include <resolv.h>
 #include <stdlib.h>
diff --git a/ip/Makefile b/ip/Makefile
index 1676f0f..2b606d4 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -4,7 +4,8 @@
     ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \
     iplink_vlan.o link_veth.o link_gre.o iplink_can.o \
     iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_vti.o \
-    iplink_vxlan.o tcp_metrics.o iplink_ipoib.o
+    iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o link_ip6tnl.o \
+    link_iptnl.o
 
 RTMONOBJ=rtmon.o
 
diff --git a/ip/ip.c b/ip/ip.c
index e0f7e60..c3ff627 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -57,6 +57,7 @@
 static int do_help(int argc, char **argv)
 {
 	usage();
+        return 0;
 }
 
 static const struct cmd {
@@ -85,6 +86,7 @@
 	{ "mroute",	do_multiroute },
 	{ "mrule",	do_multirule },
 	{ "netns",	do_netns },
+	{ "netconf",	do_ipnetconf },
 	{ "help",	do_help },
 	{ 0 }
 };
diff --git a/ip/ip6tunnel.c b/ip/ip6tunnel.c
index fcc9f33..216e982 100644
--- a/ip/ip6tunnel.c
+++ b/ip/ip6tunnel.c
@@ -128,7 +128,7 @@
 				 strcmp(*argv, "any") == 0)
 				p->proto = 0;
 			else {
-                                fprintf(stderr,"Cannot guess tunnel mode.\n");
+                                fprintf(stderr,"Unknown tunnel mode \"%s\"\n", *argv);
                                 exit(-1);
                         }
                 } else if (strcmp(*argv, "remote") == 0) {
@@ -293,7 +293,7 @@
 		buf[sizeof(buf) - 1] = '\0';
 		if ((ptr = strchr(buf, ':')) == NULL ||
 		    (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
-			fprintf(stderr, "Wrong format of /proc/net/dev. Sorry.\n");
+			fprintf(stderr, "Wrong format for /proc/net/dev. Giving up.\n");
 			goto end;
 		}
 		if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld",
@@ -309,7 +309,7 @@
 			continue;
 		type = ll_index_to_type(index);
 		if (type == -1) {
-			fprintf(stderr, "Failed to get type of [%s]\n", name);
+			fprintf(stderr, "Failed to get type of \"%s\"\n", name);
 			continue;
 		}
 		if (type != ARPHRD_TUNNEL6)
@@ -402,7 +402,7 @@
 	case AF_INET6:
 		break;
 	default:
-		fprintf(stderr, "Unsupported family:%d\n", preferred_family);
+		fprintf(stderr, "Unsupported protocol family: %d\n", preferred_family);
 		exit(-1);
 	}
 
diff --git a/ip/ip_common.h b/ip/ip_common.h
index 2fd66b7..de56810 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -16,15 +16,20 @@
 extern int iproute_monitor(int argc, char **argv);
 extern void iplink_usage(void) __attribute__((noreturn));
 extern void iproute_reset_filter(void);
+extern void ipmroute_reset_filter(void);
 extern void ipaddr_reset_filter(int);
 extern void ipneigh_reset_filter(void);
 extern void ipntable_reset_filter(void);
 extern int print_route(const struct sockaddr_nl *who,
 		       struct nlmsghdr *n, void *arg);
+extern int print_mroute(const struct sockaddr_nl *who,
+			struct nlmsghdr *n, void *arg);
 extern int print_prefix(const struct sockaddr_nl *who,
 			struct nlmsghdr *n, void *arg);
 extern int print_rule(const struct sockaddr_nl *who,
 		      struct nlmsghdr *n, void *arg);
+extern int print_netconf(const struct sockaddr_nl *who,
+			 struct nlmsghdr *n, void *arg);
 extern int do_ipaddr(int argc, char **argv);
 extern int do_ipaddrlabel(int argc, char **argv);
 extern int do_iproute(int argc, char **argv);
@@ -43,6 +48,7 @@
 extern int do_xfrm(int argc, char **argv);
 extern int do_ipl2tp(int argc, char **argv);
 extern int do_tcp_metrics(int argc, char **argv);
+extern int do_ipnetconf(int argc, char **argv);
 
 static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb)
 {
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index 5498f46..cff503f 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -19,7 +19,7 @@
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
-#include <sys/errno.h>
+#include <errno.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <string.h>
@@ -89,7 +89,7 @@
 	exit(-1);
 }
 
-void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
+static void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
 {
 	fprintf(fp, "<");
 	if (flags & IFF_UP && !(flags & IFF_RUNNING))
@@ -163,7 +163,7 @@
 		memset(&ifr, 0, sizeof(ifr));
 		strcpy(ifr.ifr_name, rta_getattr_str(tb[IFLA_IFNAME]));
 		if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) {
-			fprintf(f, "ioctl(SIOCGIFXQLEN) failed: %s\n", strerror(errno));
+			fprintf(f, "ioctl(SIOCGIFTXQLEN) failed: %s\n", strerror(errno));
 			close(s);
 			return;
 		}
@@ -461,6 +461,10 @@
 		}
 	}
 
+	if (do_link && tb[IFLA_PROMISCUITY] && show_details)
+		fprintf(fp, " promiscuity %u ",
+			*(int*)RTA_DATA(tb[IFLA_PROMISCUITY]));
+
 	if (do_link && tb[IFLA_LINKINFO] && show_details)
 		print_linktype(fp, tb[IFLA_LINKINFO]);
 
@@ -695,8 +699,8 @@
 	return 0;
 }
 
-int print_addrinfo_primary(const struct sockaddr_nl *who, struct nlmsghdr *n,
-			   void *arg)
+static int print_addrinfo_primary(const struct sockaddr_nl *who,
+				  struct nlmsghdr *n, void *arg)
 {
 	struct ifaddrmsg *ifa = NLMSG_DATA(n);
 
@@ -706,8 +710,8 @@
 	return print_addrinfo(who, n, arg);
 }
 
-int print_addrinfo_secondary(const struct sockaddr_nl *who, struct nlmsghdr *n,
-			     void *arg)
+static int print_addrinfo_secondary(const struct sockaddr_nl *who,
+				    struct nlmsghdr *n, void *arg)
 {
 	struct ifaddrmsg *ifa = NLMSG_DATA(n);
 
@@ -781,7 +785,7 @@
 	int ret;
 
 	if (isatty(STDOUT_FILENO)) {
-		fprintf(stderr, "Not sending binary stream to stdout\n");
+		fprintf(stderr, "Not sending a binary stream to stdout\n");
 		return -1;
 	}
 
diff --git a/ip/ipl2tp.c b/ip/ipl2tp.c
index f6e264a..2d22317 100644
--- a/ip/ipl2tp.c
+++ b/ip/ipl2tp.c
@@ -494,7 +494,7 @@
 			} else if (strcmp(*argv, "udp") == 0) {
 				p->encap = L2TP_ENCAPTYPE_UDP;
 			} else {
-				fprintf(stderr, "Unknown tunnel encapsulation.\n");
+				fprintf(stderr, "Unknown tunnel encapsulation \"%s\"\n", *argv);
 				exit(-1);
 			}
 		} else if (strcmp(*argv, "name") == 0) {
diff --git a/ip/iplink.c b/ip/iplink.c
index 7451aa0..ad33611 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -83,7 +83,8 @@
 
 	if (iplink_have_newlink()) {
 		fprintf(stderr, "\n");
-		fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can | bridge | ipoib }\n");
+		fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can |\n");
+		fprintf(stderr, "          bridge | ipoib | ip6tnl | ipip | sit | vxlan }\n");
 	}
 	exit(-1);
 }
@@ -93,9 +94,9 @@
 	iplink_usage();
 }
 
-static int on_off(char *msg)
+static int on_off(const char *msg, const char *realval)
 {
-	fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg);
+	fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\", not \"%s\"\n", msg, realval);
 	return -1;
 }
 
@@ -134,7 +135,7 @@
 	return l;
 }
 
-int get_link_mode(const char *mode)
+static int get_link_mode(const char *mode)
 {
 	if (strcasecmp(mode, "default") == 0)
 		return IF_LINK_MODE_DEFAULT;
@@ -193,7 +194,7 @@
 	char			buf[1024];
 };
 
-int iplink_parse_vf(int vf, int *argcp, char ***argvp,
+static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
 			   struct iplink_req *req)
 {
 	int len, argc = *argcp;
@@ -347,7 +348,7 @@
 			} else if (strcmp(*argv, "off") == 0) {
 				req->i.ifi_flags &= ~IFF_MULTICAST;
 			} else
-				return on_off("multicast");
+				return on_off("multicast", *argv);
 		} else if (strcmp(*argv, "allmulticast") == 0) {
 			NEXT_ARG();
 			req->i.ifi_change |= IFF_ALLMULTI;
@@ -356,7 +357,7 @@
 			} else if (strcmp(*argv, "off") == 0) {
 				req->i.ifi_flags &= ~IFF_ALLMULTI;
 			} else
-				return on_off("allmulticast");
+				return on_off("allmulticast", *argv);
 		} else if (strcmp(*argv, "promisc") == 0) {
 			NEXT_ARG();
 			req->i.ifi_change |= IFF_PROMISC;
@@ -365,7 +366,7 @@
 			} else if (strcmp(*argv, "off") == 0) {
 				req->i.ifi_flags &= ~IFF_PROMISC;
 			} else
-				return on_off("promisc");
+				return on_off("promisc", *argv);
 		} else if (strcmp(*argv, "trailers") == 0) {
 			NEXT_ARG();
 			req->i.ifi_change |= IFF_NOTRAILERS;
@@ -374,7 +375,7 @@
 			} else if (strcmp(*argv, "on") == 0) {
 				req->i.ifi_flags &= ~IFF_NOTRAILERS;
 			} else
-				return on_off("trailers");
+				return on_off("trailers", *argv);
 		} else if (strcmp(*argv, "arp") == 0) {
 			NEXT_ARG();
 			req->i.ifi_change |= IFF_NOARP;
@@ -383,7 +384,7 @@
 			} else if (strcmp(*argv, "off") == 0) {
 				req->i.ifi_flags |= IFF_NOARP;
 			} else
-				return on_off("noarp");
+				return on_off("noarp", *argv);
 		} else if (strcmp(*argv, "vf") == 0) {
 			struct rtattr *vflist;
 			NEXT_ARG();
@@ -416,7 +417,7 @@
 			} else if (strcmp(*argv, "off") == 0) {
 				req->i.ifi_flags &= ~IFF_DYNAMIC;
 			} else
-				return on_off("dynamic");
+				return on_off("dynamic", *argv);
 		} else if (matches(*argv, "type") == 0) {
 			NEXT_ARG();
 			*type = *argv;
@@ -851,7 +852,7 @@
 			} else if (strcmp(*argv, "off") == 0) {
 				flags &= ~IFF_MULTICAST;
 			} else
-				return on_off("multicast");
+				return on_off("multicast", *argv);
 		} else if (strcmp(*argv, "allmulticast") == 0) {
 			NEXT_ARG();
 			mask |= IFF_ALLMULTI;
@@ -860,7 +861,7 @@
 			} else if (strcmp(*argv, "off") == 0) {
 				flags &= ~IFF_ALLMULTI;
 			} else
-				return on_off("allmulticast");
+				return on_off("allmulticast", *argv);
 		} else if (strcmp(*argv, "promisc") == 0) {
 			NEXT_ARG();
 			mask |= IFF_PROMISC;
@@ -869,7 +870,7 @@
 			} else if (strcmp(*argv, "off") == 0) {
 				flags &= ~IFF_PROMISC;
 			} else
-				return on_off("promisc");
+				return on_off("promisc", *argv);
 		} else if (strcmp(*argv, "trailers") == 0) {
 			NEXT_ARG();
 			mask |= IFF_NOTRAILERS;
@@ -878,7 +879,7 @@
 			} else if (strcmp(*argv, "on") == 0) {
 				flags &= ~IFF_NOTRAILERS;
 			} else
-				return on_off("trailers");
+				return on_off("trailers", *argv);
 		} else if (strcmp(*argv, "arp") == 0) {
 			NEXT_ARG();
 			mask |= IFF_NOARP;
@@ -887,7 +888,7 @@
 			} else if (strcmp(*argv, "off") == 0) {
 				flags |= IFF_NOARP;
 			} else
-				return on_off("noarp");
+				return on_off("noarp", *argv);
 		} else if (matches(*argv, "dynamic") == 0) {
 			NEXT_ARG();
 			mask |= IFF_DYNAMIC;
@@ -896,7 +897,7 @@
 			} else if (strcmp(*argv, "off") == 0) {
 				flags &= ~IFF_DYNAMIC;
 			} else
-				return on_off("dynamic");
+				return on_off("dynamic", *argv);
 		} else {
                         if (strcmp(*argv, "dev") == 0) {
 				NEXT_ARG();
diff --git a/ip/iplink_can.c b/ip/iplink_can.c
index c8af4bc..5674358 100644
--- a/ip/iplink_can.c
+++ b/ip/iplink_can.c
@@ -68,8 +68,8 @@
 		cm->flags |= flags;
 	} else if (strcmp(arg, "off") != 0) {
 		fprintf(stderr,
-			"Error: argument of \"%s\" must be \"on\" or \"off\"\n",
-			name);
+			"Error: argument of \"%s\" must be \"on\" or \"off\", not \"%s\"\n",
+			name, arg);
 		exit(-1);
 	}
 	cm->mask |= flags;
@@ -169,7 +169,7 @@
 			usage();
 			return -1;
 		} else {
-			fprintf(stderr, "can: what is \"%s\"?\n", *argv);
+			fprintf(stderr, "can: unknown option \"%s\"\n", *argv);
 			usage();
 			return -1;
 		}
diff --git a/ip/iplink_ipoib.c b/ip/iplink_ipoib.c
index ae372bc..5c1c68c 100644
--- a/ip/iplink_ipoib.c
+++ b/ip/iplink_ipoib.c
@@ -66,7 +66,7 @@
 			explain();
 			return -1;
 		} else {
-			fprintf(stderr, "ipoib: what is \"%s\"?\n", *argv);
+			fprintf(stderr, "ipoib: unknown option \"%s\"?\n", *argv);
 			explain();
 			return -1;
 		}
diff --git a/ip/iplink_macvlan.c b/ip/iplink_macvlan.c
index ed0e34b..5b4b868 100644
--- a/ip/iplink_macvlan.c
+++ b/ip/iplink_macvlan.c
@@ -58,7 +58,7 @@
 			explain();
 			return -1;
 		} else {
-			fprintf(stderr, "macvlan: what is \"%s\"?\n", *argv);
+			fprintf(stderr, "macvlan: unknown option \"%s\"?\n", *argv);
 			explain();
 			return -1;
 		}
diff --git a/ip/iplink_macvtap.c b/ip/iplink_macvtap.c
index 6c7fe1f..bea9f0c 100644
--- a/ip/iplink_macvtap.c
+++ b/ip/iplink_macvtap.c
@@ -24,10 +24,10 @@
 	);
 }
 
-static int mode_arg(void)
+static int mode_arg(const char *arg)
 {
         fprintf(stderr, "Error: argument of \"mode\" must be \"private\", "
-		"\"vepa\", \"bridge\" or \"passthru\" \n");
+		"\"vepa\", \"bridge\" or \"passthru\", not \"%s\"\n", arg);
         return -1;
 }
 
@@ -48,14 +48,14 @@
 			else if (strcmp(*argv, "passthru") == 0)
 				mode = MACVLAN_MODE_PASSTHRU;
 			else
-				return mode_arg();
+				return mode_arg(*argv);
 
 			addattr32(n, 1024, IFLA_MACVLAN_MODE, mode);
 		} else if (matches(*argv, "help") == 0) {
 			explain();
 			return -1;
 		} else {
-			fprintf(stderr, "macvtap: what is \"%s\"?\n", *argv);
+			fprintf(stderr, "macvtap: unknown command \"%s\"?\n", *argv);
 			explain();
 			return -1;
 		}
diff --git a/ip/iplink_vlan.c b/ip/iplink_vlan.c
index 97af8d6..26ceb8d 100644
--- a/ip/iplink_vlan.c
+++ b/ip/iplink_vlan.c
@@ -33,9 +33,9 @@
 	);
 }
 
-static int on_off(char *msg)
+static int on_off(const char *msg, const char *arg)
 {
-	fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg);
+	fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\", not \"%s\"\n", msg, arg);
 	return -1;
 }
 
@@ -93,7 +93,7 @@
 			else if (strcmp(*argv, "off") == 0)
 				flags.flags &= ~VLAN_FLAG_REORDER_HDR;
 			else
-				return on_off("reorder_hdr");
+				return on_off("reorder_hdr", *argv);
 		} else if (matches(*argv, "gvrp") == 0) {
 			NEXT_ARG();
 			flags.mask |= VLAN_FLAG_GVRP;
@@ -102,7 +102,7 @@
 			else if (strcmp(*argv, "off") == 0)
 				flags.flags &= ~VLAN_FLAG_GVRP;
 			else
-				return on_off("gvrp");
+				return on_off("gvrp", *argv);
 		} else if (matches(*argv, "loose_binding") == 0) {
 			NEXT_ARG();
 			flags.mask |= VLAN_FLAG_LOOSE_BINDING;
@@ -111,7 +111,7 @@
 			else if (strcmp(*argv, "off") == 0)
 				flags.flags &= ~VLAN_FLAG_LOOSE_BINDING;
 			else
-				return on_off("loose_binding");
+				return on_off("loose_binding", *argv);
 		} else if (matches(*argv, "ingress-qos-map") == 0) {
 			NEXT_ARG();
 			if (vlan_parse_qos_map(&argc, &argv, n,
@@ -128,7 +128,7 @@
 			explain();
 			return -1;
 		} else {
-			fprintf(stderr, "vlan: what is \"%s\"?\n", *argv);
+			fprintf(stderr, "vlan: unknown command \"%s\"?\n", *argv);
 			explain();
 			return -1;
 		}
diff --git a/ip/iplink_vxlan.c b/ip/iplink_vxlan.c
index ba5c4ab..1025326 100644
--- a/ip/iplink_vxlan.c
+++ b/ip/iplink_vxlan.c
@@ -26,6 +26,8 @@
 	fprintf(stderr, "Usage: ... vxlan id VNI [ group ADDR ] [ local ADDR ]\n");
 	fprintf(stderr, "                 [ ttl TTL ] [ tos TOS ] [ dev PHYS_DEV ]\n");
 	fprintf(stderr, "                 [ port MIN MAX ] [ [no]learning ]\n");
+	fprintf(stderr, "                 [ [no]proxy ] [ [no]rsc ]\n");
+	fprintf(stderr, "                 [ [no]l2miss ] [ [no]l3miss ]\n");
 	fprintf(stderr, "\n");
 	fprintf(stderr, "Where: VNI := 0-16777215\n");
 	fprintf(stderr, "       ADDR := { IP_ADDRESS | any }\n");
@@ -44,6 +46,10 @@
 	__u8 tos = 0;
 	__u8 ttl = 0;
 	__u8 learning = 1;
+	__u8 proxy = 0;
+	__u8 rsc = 0;
+	__u8 l2miss = 0;
+	__u8 l3miss = 0;
 	__u8 noage = 0;
 	__u32 age = 0;
 	__u32 maxaddr = 0;
@@ -123,11 +129,27 @@
 			learning = 0;
 		} else if (!matches(*argv, "learning")) {
 			learning = 1;
+		} else if (!matches(*argv, "noproxy")) {
+			proxy = 0;
+		} else if (!matches(*argv, "proxy")) {
+			proxy = 1;
+		} else if (!matches(*argv, "norsc")) {
+			rsc = 0;
+		} else if (!matches(*argv, "rsc")) {
+			rsc = 1;
+		} else if (!matches(*argv, "nol2miss")) {
+			l2miss = 0;
+		} else if (!matches(*argv, "l2miss")) {
+			l2miss = 1;
+		} else if (!matches(*argv, "nol3miss")) {
+			l3miss = 0;
+		} else if (!matches(*argv, "l3miss")) {
+			l3miss = 1;
 		} else if (matches(*argv, "help") == 0) {
 			explain();
 			return -1;
 		} else {
-			fprintf(stderr, "vxlan: what is \"%s\"?\n", *argv);
+			fprintf(stderr, "vxlan: unknown command \"%s\"?\n", *argv);
 			explain();
 			return -1;
 		}
@@ -148,6 +170,10 @@
 	addattr8(n, 1024, IFLA_VXLAN_TTL, ttl);
 	addattr8(n, 1024, IFLA_VXLAN_TOS, tos);
 	addattr8(n, 1024, IFLA_VXLAN_LEARNING, learning);
+	addattr8(n, 1024, IFLA_VXLAN_PROXY, proxy);
+	addattr8(n, 1024, IFLA_VXLAN_RSC, rsc);
+	addattr8(n, 1024, IFLA_VXLAN_L2MISS, l2miss);
+	addattr8(n, 1024, IFLA_VXLAN_L3MISS, l3miss);
 	if (noage)
 		addattr32(n, 1024, IFLA_VXLAN_AGEING, 0);
 	else if (age)
@@ -190,7 +216,7 @@
 	if (tb[IFLA_VXLAN_LOCAL]) {
 		__be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_LOCAL]);
 		if (addr)
-			fprintf(f, "local %s ", 
+			fprintf(f, "local %s ",
 				format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
 	}
 
@@ -208,12 +234,24 @@
 		const struct ifla_vxlan_port_range *r
 			= RTA_DATA(tb[IFLA_VXLAN_PORT_RANGE]);
 		fprintf(f, "port %u %u ", ntohs(r->low), ntohs(r->high));
-	}	
+	}
 
 	if (tb[IFLA_VXLAN_LEARNING] &&
 	    !rta_getattr_u8(tb[IFLA_VXLAN_LEARNING]))
 		fputs("nolearning ", f);
-	
+
+	if (tb[IFLA_VXLAN_PROXY] && rta_getattr_u8(tb[IFLA_VXLAN_PROXY]))
+		fputs("proxy ", f);
+
+	if (tb[IFLA_VXLAN_RSC] && rta_getattr_u8(tb[IFLA_VXLAN_RSC]))
+		fputs("rsc ", f);
+
+	if (tb[IFLA_VXLAN_L2MISS] && rta_getattr_u8(tb[IFLA_VXLAN_L2MISS]))
+		fputs("l2miss ", f);
+
+	if (tb[IFLA_VXLAN_L3MISS] && rta_getattr_u8(tb[IFLA_VXLAN_L3MISS]))
+		fputs("l3miss ", f);
+
 	if (tb[IFLA_VXLAN_TOS] &&
 	    (tos = rta_getattr_u8(tb[IFLA_VXLAN_TOS]))) {
 		if (tos == 1)
diff --git a/ip/ipmaddr.c b/ip/ipmaddr.c
index 3ae9478..6e19465 100644
--- a/ip/ipmaddr.c
+++ b/ip/ipmaddr.c
@@ -28,6 +28,7 @@
 
 #include "rt_names.h"
 #include "utils.h"
+#include "ip_common.h"
 
 static struct {
 	char *dev;
@@ -70,7 +71,7 @@
 	inet_prefix	addr;
 };
 
-void maddr_ins(struct ma_info **lst, struct ma_info *m)
+static void maddr_ins(struct ma_info **lst, struct ma_info *m)
 {
 	struct ma_info *mp;
 
@@ -82,7 +83,7 @@
 	*lst = m;
 }
 
-void read_dev_mcast(struct ma_info **result_p)
+static void read_dev_mcast(struct ma_info **result_p)
 {
 	char buf[256];
 	FILE *fp = fopen("/proc/net/dev_mcast", "r");
@@ -119,7 +120,7 @@
 	fclose(fp);
 }
 
-void read_igmp(struct ma_info **result_p)
+static void read_igmp(struct ma_info **result_p)
 {
 	struct ma_info m;
 	char buf[256];
@@ -158,7 +159,7 @@
 }
 
 
-void read_igmp6(struct ma_info **result_p)
+static void read_igmp6(struct ma_info **result_p)
 {
 	char buf[256];
 	FILE *fp = fopen("/proc/net/igmp6", "r");
@@ -275,7 +276,7 @@
 	return 0;
 }
 
-int multiaddr_modify(int cmd, int argc, char **argv)
+static int multiaddr_modify(int cmd, int argc, char **argv)
 {
 	struct ifreq ifr;
 	int fd;
diff --git a/ip/ipmonitor.c b/ip/ipmonitor.c
index 4b1d469..86c473e 100644
--- a/ip/ipmonitor.c
+++ b/ip/ipmonitor.c
@@ -29,13 +29,16 @@
 
 static void usage(void)
 {
-	fprintf(stderr, "Usage: ip monitor [ all | LISTofOBJECTS ]\n");
+	fprintf(stderr, "Usage: ip monitor [ all | LISTofOBJECTS ] [ FILE ]\n");
+	fprintf(stderr, "LISTofOBJECTS := link | address | route | mroute | prefix |\n");
+	fprintf(stderr, "                 neigh | netconf\n");
+	fprintf(stderr, "FILE := file FILENAME\n");
 	exit(-1);
 }
 
 
-int accept_msg(const struct sockaddr_nl *who,
-	       struct nlmsghdr *n, void *arg)
+static int accept_msg(const struct sockaddr_nl *who,
+		      struct nlmsghdr *n, void *arg)
 {
 	FILE *fp = (FILE*)arg;
 
@@ -43,10 +46,26 @@
 		print_timestamp(fp);
 
 	if (n->nlmsg_type == RTM_NEWROUTE || n->nlmsg_type == RTM_DELROUTE) {
-		if (prefix_banner)
-			fprintf(fp, "[ROUTE]");
-		print_route(who, n, arg);
-		return 0;
+		struct rtmsg *r = NLMSG_DATA(n);
+		int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
+
+		if (len < 0) {
+			fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+			return -1;
+		}
+
+		if (r->rtm_family == RTNL_FAMILY_IPMR ||
+		    r->rtm_family == RTNL_FAMILY_IP6MR) {
+			if (prefix_banner)
+				fprintf(fp, "[MROUTE]");
+			print_mroute(who, n, arg);
+			return 0;
+		} else {
+			if (prefix_banner)
+				fprintf(fp, "[ROUTE]");
+			print_route(who, n, arg);
+			return 0;
+		}
 	}
 	if (n->nlmsg_type == RTM_NEWLINK || n->nlmsg_type == RTM_DELLINK) {
 		ll_remember_index(who, n, NULL);
@@ -67,7 +86,8 @@
 		print_addrlabel(who, n, arg);
 		return 0;
 	}
-	if (n->nlmsg_type == RTM_NEWNEIGH || n->nlmsg_type == RTM_DELNEIGH) {
+	if (n->nlmsg_type == RTM_NEWNEIGH || n->nlmsg_type == RTM_DELNEIGH ||
+	    n->nlmsg_type == RTM_GETNEIGH) {
 		if (prefix_banner)
 			fprintf(fp, "[NEIGH]");
 		print_neigh(who, n, arg);
@@ -85,6 +105,12 @@
 		print_rule(who, n, arg);
 		return 0;
 	}
+	if (n->nlmsg_type == RTM_NEWNETCONF) {
+		if (prefix_banner)
+			fprintf(fp, "[NETCONF]");
+		print_netconf(who, n, arg);
+		return 0;
+	}
 	if (n->nlmsg_type == 15) {
 		char *tstr;
 		time_t secs = ((__u32*)NLMSG_DATA(n))[0];
@@ -116,12 +142,15 @@
 	int llink=0;
 	int laddr=0;
 	int lroute=0;
+	int lmroute=0;
 	int lprefix=0;
 	int lneigh=0;
+	int lnetconf=0;
 
 	rtnl_close(&rth);
 	ipaddr_reset_filter(1);
 	iproute_reset_filter();
+	ipmroute_reset_filter();
 	ipneigh_reset_filter();
 
 	while (argc > 0) {
@@ -137,12 +166,18 @@
 		} else if (matches(*argv, "route") == 0) {
 			lroute=1;
 			groups = 0;
+		} else if (matches(*argv, "mroute") == 0) {
+			lmroute=1;
+			groups = 0;
 		} else if (matches(*argv, "prefix") == 0) {
 			lprefix=1;
 			groups = 0;
 		} else if (matches(*argv, "neigh") == 0) {
 			lneigh = 1;
 			groups = 0;
+		} else if (matches(*argv, "netconf") == 0) {
+			lnetconf = 1;
+			groups = 0;
 		} else if (strcmp(*argv, "all") == 0) {
 			groups = ~RTMGRP_TC;
 			prefix_banner=1;
@@ -169,6 +204,12 @@
 		if (!preferred_family || preferred_family == AF_INET6)
 			groups |= nl_mgrp(RTNLGRP_IPV6_ROUTE);
 	}
+	if (lmroute) {
+		if (!preferred_family || preferred_family == AF_INET)
+			groups |= nl_mgrp(RTNLGRP_IPV4_MROUTE);
+		if (!preferred_family || preferred_family == AF_INET6)
+			groups |= nl_mgrp(RTNLGRP_IPV6_MROUTE);
+	}
 	if (lprefix) {
 		if (!preferred_family || preferred_family == AF_INET6)
 			groups |= nl_mgrp(RTNLGRP_IPV6_PREFIX);
@@ -176,6 +217,12 @@
 	if (lneigh) {
 		groups |= nl_mgrp(RTNLGRP_NEIGH);
 	}
+	if (lnetconf) {
+		if (!preferred_family || preferred_family == AF_INET)
+			groups |= nl_mgrp(RTNLGRP_IPV4_NETCONF);
+		if (!preferred_family || preferred_family == AF_INET6)
+			groups |= nl_mgrp(RTNLGRP_IPV6_NETCONF);
+	}
 	if (file) {
 		FILE *fp;
 		fp = fopen(file, "r");
diff --git a/ip/ipmroute.c b/ip/ipmroute.c
index 945727d..345576d 100644
--- a/ip/ipmroute.c
+++ b/ip/ipmroute.c
@@ -15,6 +15,7 @@
 #include <unistd.h>
 #include <syslog.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -26,167 +27,231 @@
 #include <linux/if_arp.h>
 #include <linux/sockios.h>
 
+#include <rt_names.h>
 #include "utils.h"
-
-char filter_dev[16];
-int  filter_family;
+#include "ip_common.h"
 
 static void usage(void) __attribute__((noreturn));
 
 static void usage(void)
 {
-	fprintf(stderr, "Usage: ip mroute show [ PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n");
+	fprintf(stderr, "Usage: ip mroute show [ [ to ] PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n");
+	fprintf(stderr, "                      [ table TABLE_ID ]\n");
+	fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n");
 #if 0
 	fprintf(stderr, "Usage: ip mroute [ add | del ] DESTINATION from SOURCE [ iif DEVICE ] [ oif DEVICE ]\n");
 #endif
 	exit(-1);
 }
 
-static char *viftable[32];
-
 struct rtfilter
 {
+	int tb;
+	int af;
+	int iif;
 	inet_prefix mdst;
 	inet_prefix msrc;
 } filter;
 
-static void read_viftable(void)
+int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 {
-	char buf[256];
-	FILE *fp = fopen("/proc/net/ip_mr_vif", "r");
+	FILE *fp = (FILE*)arg;
+	struct rtmsg *r = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr * tb[RTA_MAX+1];
+	char abuf[256];
+	char obuf[256];
+	SPRINT_BUF(b1);
+	__u32 table;
+	int iif = 0;
+	int family;
 
-	if (!fp)
-		return;
-
-	if (!fgets(buf, sizeof(buf), fp)) {
-		fclose(fp);
-		return;
+	if ((n->nlmsg_type != RTM_NEWROUTE &&
+	     n->nlmsg_type != RTM_DELROUTE) ||
+	    !(n->nlmsg_flags & NLM_F_MULTI)) {
+		fprintf(stderr, "Not a multicast route: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+		return 0;
 	}
-	while (fgets(buf, sizeof(buf), fp)) {
-		int vifi;
-		char dev[256];
-
-		if (sscanf(buf, "%d%s", &vifi, dev) < 2)
-			continue;
-
-		if (vifi<0 || vifi>31)
-			continue;
-
-		viftable[vifi] = strdup(dev);
+	len -= NLMSG_LENGTH(sizeof(*r));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
 	}
-	fclose(fp);
-}
-
-static void read_mroute_list(FILE *ofp)
-{
-	char buf[256];
-	FILE *fp = fopen("/proc/net/ip_mr_cache", "r");
-
-	if (!fp)
-		return;
-
-	if (!fgets(buf, sizeof(buf), fp)) {
-		fclose(fp);
-		return;
+	if (r->rtm_type != RTN_MULTICAST) {
+		fprintf(stderr, "Not a multicast route (type: %s)\n",
+			rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+		return 0;
 	}
 
-	while (fgets(buf, sizeof(buf), fp)) {
-		inet_prefix maddr, msrc;
-		unsigned pkts, b, w;
-		int vifi;
-		char oiflist[256];
-		char sbuf[256];
-		char mbuf[256];
-		char obuf[256];
+	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+	table = rtm_get_table(r, tb);
 
-		oiflist[0] = 0;
-		if (sscanf(buf, "%x%x%d%u%u%u %[^\n]",
-			   maddr.data, msrc.data, &vifi,
-			   &pkts, &b, &w, oiflist) < 6)
-			continue;
+	if (filter.tb > 0 && filter.tb != table)
+		return 0;
 
-		if (vifi!=-1 && (vifi < 0 || vifi>31))
-			continue;
+	if (tb[RTA_IIF])
+		iif = *(int*)RTA_DATA(tb[RTA_IIF]);
+	if (filter.iif && filter.iif != iif)
+		return 0;
 
-		if (filter_dev[0] && (vifi<0 || strcmp(filter_dev, viftable[vifi])))
-			continue;
-		if (filter.mdst.family && inet_addr_match(&maddr, &filter.mdst, filter.mdst.bitlen))
-			continue;
-		if (filter.msrc.family && inet_addr_match(&msrc, &filter.msrc, filter.msrc.bitlen))
-			continue;
+	if (filter.af && filter.af != r->rtm_family)
+		return 0;
 
-		snprintf(obuf, sizeof(obuf), "(%s, %s)",
-			 format_host(AF_INET, 4, &msrc.data[0], sbuf, sizeof(sbuf)),
-			 format_host(AF_INET, 4, &maddr.data[0], mbuf, sizeof(mbuf)));
+	if (tb[RTA_DST] &&
+	    filter.mdst.bitlen > 0 &&
+	    inet_addr_match(RTA_DATA(tb[RTA_DST]), &filter.mdst, filter.mdst.bitlen))
+		return 0;
 
-		fprintf(ofp, "%-32s Iif: ", obuf);
+	if (tb[RTA_SRC] &&
+	    filter.msrc.bitlen > 0 &&
+	    inet_addr_match(RTA_DATA(tb[RTA_SRC]), &filter.msrc, filter.msrc.bitlen))
+		return 0;
 
-		if (vifi == -1)
-			fprintf(ofp, "unresolved ");
-		else
-			fprintf(ofp, "%-10s ", viftable[vifi]);
+	family = r->rtm_family == RTNL_FAMILY_IPMR ? AF_INET : AF_INET6;
 
-		if (oiflist[0]) {
-			char *next = NULL;
-			char *p = oiflist;
-			int ovifi, ottl;
+	if (n->nlmsg_type == RTM_DELROUTE)
+		fprintf(fp, "Deleted ");
 
-			fprintf(ofp, "Oifs: ");
+	if (tb[RTA_SRC])
+		len = snprintf(obuf, sizeof(obuf),
+			       "(%s, ", rt_addr_n2a(family,
+						    RTA_PAYLOAD(tb[RTA_SRC]),
+						    RTA_DATA(tb[RTA_SRC]),
+						    abuf, sizeof(abuf)));
+	else
+		len = sprintf(obuf, "(unknown, ");
+	if (tb[RTA_DST])
+		snprintf(obuf + len, sizeof(obuf) - len,
+			 "%s)", rt_addr_n2a(family, RTA_PAYLOAD(tb[RTA_DST]),
+					    RTA_DATA(tb[RTA_DST]),
+					    abuf, sizeof(abuf)));
+	else
+		snprintf(obuf + len, sizeof(obuf) - len, "unknown) ");
 
-			while (p) {
-				next = strchr(p, ' ');
-				if (next) {
-					*next = 0;
-					next++;
-				}
-				if (sscanf(p, "%d:%d", &ovifi, &ottl)<2) {
-					p = next;
-					continue;
-				}
-				p = next;
+	fprintf(fp, "%-32s Iif: ", obuf);
+	if (iif)
+		fprintf(fp, "%-10s ", ll_index_to_name(iif));
+	else
+		fprintf(fp, "unresolved ");
 
-				fprintf(ofp, "%s", viftable[ovifi]);
-				if (ottl>1)
-					fprintf(ofp, "(ttl %d) ", ovifi);
-				else
-					fprintf(ofp, " ");
+	if (tb[RTA_MULTIPATH]) {
+		struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]);
+		int first = 1;
+
+		len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
+
+		for (;;) {
+			if (len < sizeof(*nh))
+				break;
+			if (nh->rtnh_len > len)
+				break;
+
+			if (first) {
+				fprintf(fp, "Oifs: ");
+				first = 0;
 			}
+			fprintf(fp, "%s", ll_index_to_name(nh->rtnh_ifindex));
+			if (nh->rtnh_hops > 1)
+				fprintf(fp, "(ttl %d) ", nh->rtnh_hops);
+			else
+				fprintf(fp, " ");
+			len -= NLMSG_ALIGN(nh->rtnh_len);
+			nh = RTNH_NEXT(nh);
 		}
-
-		if (show_stats && b) {
-			fprintf(ofp, "%s  %u packets, %u bytes", _SL_, pkts, b);
-			if (w)
-				fprintf(ofp, ", %u arrived on wrong iif.", w);
-		}
-		fprintf(ofp, "\n");
 	}
-	fclose(fp);
+	if (show_stats && tb[RTA_MFC_STATS]) {
+		struct rta_mfc_stats *mfcs = RTA_DATA(tb[RTA_MFC_STATS]);
+
+		fprintf(fp, "%s  %"PRIu64" packets, %"PRIu64" bytes", _SL_,
+			(uint64_t)mfcs->mfcs_packets,
+			(uint64_t)mfcs->mfcs_bytes);
+		if (mfcs->mfcs_wrong_if)
+			fprintf(fp, ", %"PRIu64" arrived on wrong iif.",
+				(uint64_t)mfcs->mfcs_wrong_if);
+	}
+	fprintf(fp, "\n");
+	fflush(fp);
+	return 0;
 }
 
+void ipmroute_reset_filter(void)
+{
+	memset(&filter, 0, sizeof(filter));
+	filter.mdst.bitlen = -1;
+	filter.msrc.bitlen = -1;
+}
 
 static int mroute_list(int argc, char **argv)
 {
+	char *id = NULL;
+	int family;
+
+	ipmroute_reset_filter();
+	if (preferred_family == AF_UNSPEC)
+		family = AF_INET;
+	else
+		family = AF_INET6;
+	if (family == AF_INET) {
+		filter.af = RTNL_FAMILY_IPMR;
+		filter.tb = RT_TABLE_DEFAULT;  /* for backward compatibility */
+	} else
+		filter.af = RTNL_FAMILY_IP6MR;
+
 	while (argc > 0) {
-		if (strcmp(*argv, "iif") == 0) {
+		if (matches(*argv, "table") == 0) {
+			__u32 tid;
 			NEXT_ARG();
-			strncpy(filter_dev, *argv, sizeof(filter_dev)-1);
+			if (rtnl_rttable_a2n(&tid, *argv)) {
+				if (strcmp(*argv, "all") == 0) {
+					filter.tb = 0;
+				} else if (strcmp(*argv, "help") == 0) {
+					usage();
+				} else {
+					invarg("table id value is invalid\n", *argv);
+				}
+			} else
+				filter.tb = tid;
+		} else if (strcmp(*argv, "iif") == 0) {
+			NEXT_ARG();
+			id = *argv;
 		} else if (matches(*argv, "from") == 0) {
 			NEXT_ARG();
-			get_prefix(&filter.msrc, *argv, AF_INET);
+			get_prefix(&filter.msrc, *argv, family);
 		} else {
 			if (strcmp(*argv, "to") == 0) {
 				NEXT_ARG();
 			}
 			if (matches(*argv, "help") == 0)
 				usage();
-			get_prefix(&filter.mdst, *argv, AF_INET);
+			get_prefix(&filter.mdst, *argv, family);
 		}
-		argv++; argc--;
+		argc--; argv++;
 	}
 
-	read_viftable();
-	read_mroute_list(stdout);
-	return 0;
+	ll_init_map(&rth);
+
+	if (id)  {
+		int idx;
+
+		if ((idx = ll_name_to_index(id)) == 0) {
+			fprintf(stderr, "Cannot find device \"%s\"\n", id);
+			return -1;
+		}
+		filter.iif = idx;
+	}
+
+	if (rtnl_wilddump_request(&rth, filter.af, RTM_GETROUTE) < 0) {
+		perror("Cannot send dump request");
+		return 1;
+	}
+
+	if (rtnl_dump_filter(&rth, print_mroute, stdout) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		exit(1);
+	}
+
+	exit(0);
 }
 
 int do_multiroute(int argc, char **argv)
diff --git a/ip/ipneigh.c b/ip/ipneigh.c
index 56e56b2..d76e035 100644
--- a/ip/ipneigh.c
+++ b/ip/ipneigh.c
@@ -52,7 +52,7 @@
 	exit(-1);
 }
 
-int nud_state_a2n(unsigned *state, char *arg)
+static int nud_state_a2n(unsigned *state, const char *arg)
 {
 	if (matches(arg, "permanent") == 0)
 		*state = NUD_PERMANENT;
@@ -189,7 +189,8 @@
 	struct rtattr * tb[NDA_MAX+1];
 	char abuf[256];
 
-	if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) {
+	if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH &&
+	    n->nlmsg_type != RTM_GETNEIGH) {
 		fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n",
 			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
 
@@ -251,6 +252,8 @@
 
 	if (n->nlmsg_type == RTM_DELNEIGH)
 		fprintf(fp, "delete ");
+	else if (n->nlmsg_type == RTM_GETNEIGH)
+		fprintf(fp, "miss ");
 	if (tb[NDA_DST]) {
 		fprintf(fp, "%s ",
 			format_host(r->ndm_family,
@@ -310,13 +313,13 @@
 	return 0;
 }
 
-void ipneigh_reset_filter()
+void ipneigh_reset_filter(void)
 {
 	memset(&filter, 0, sizeof(filter));
 	filter.state = ~0;
 }
 
-int do_show_or_flush(int argc, char **argv, int flush)
+static int do_show_or_flush(int argc, char **argv, int flush)
 {
 	char *filter_dev = NULL;
 	int state_given = 0;
diff --git a/ip/ipnetconf.c b/ip/ipnetconf.c
new file mode 100644
index 0000000..8ceadfe
--- /dev/null
+++ b/ip/ipnetconf.c
@@ -0,0 +1,183 @@
+/*
+ * ipnetconf.c		"ip netconf".
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Nicolas Dichtel, <nicolas.dichtel@6wind.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+static struct
+{
+	int family;
+	int ifindex;
+} filter;
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip netconf show [ dev STRING ]\n");
+	exit(-1);
+}
+
+#define NETCONF_RTA(r)	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct netconfmsg))))
+
+int print_netconf(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	FILE *fp = (FILE*)arg;
+	struct netconfmsg *ncm = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr *tb[NETCONFA_MAX+1];
+
+	if (n->nlmsg_type == NLMSG_ERROR)
+		return -1;
+	if (n->nlmsg_type != RTM_NEWNETCONF) {
+		fprintf(stderr, "Not RTM_NEWNETCONF: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+
+		return -1;
+	}
+	len -= NLMSG_SPACE(sizeof(*ncm));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	if (filter.family && filter.family != ncm->ncm_family)
+		return 0;
+
+	parse_rtattr(tb, NETCONFA_MAX, NETCONF_RTA(ncm),
+		     NLMSG_PAYLOAD(n, sizeof(*ncm)));
+
+	switch (ncm->ncm_family) {
+	case AF_INET:
+		fprintf(fp, "ipv4 ");
+		break;
+	case AF_INET6:
+		fprintf(fp, "ipv6 ");
+		break;
+	default:
+		fprintf(fp, "unknown ");
+		break;
+	}
+
+	if (tb[NETCONFA_IFINDEX]) {
+		int *ifindex = (int *)RTA_DATA(tb[NETCONFA_IFINDEX]);
+
+		switch (*ifindex) {
+		case NETCONFA_IFINDEX_ALL:
+			fprintf(fp, "all ");
+			break;
+		case NETCONFA_IFINDEX_DEFAULT:
+			fprintf(fp, "default ");
+			break;
+		default:
+			fprintf(fp, "dev %s ", ll_index_to_name(*ifindex));
+			break;
+		}
+	}
+
+	if (tb[NETCONFA_FORWARDING])
+		fprintf(fp, "forwarding %s ",
+			*(int *)RTA_DATA(tb[NETCONFA_FORWARDING])?"on":"off");
+	if (tb[NETCONFA_RP_FILTER]) {
+		int rp_filter = *(int *)RTA_DATA(tb[NETCONFA_RP_FILTER]);
+
+		if (rp_filter == 0)
+			fprintf(fp, "rp_filter off ");
+		else if (rp_filter == 1)
+			fprintf(fp, "rp_filter strict ");
+		else if (rp_filter == 2)
+			fprintf(fp, "rp_filter loose ");
+		else
+			fprintf(fp, "rp_filter unknown mode ");
+	}
+	if (tb[NETCONFA_MC_FORWARDING])
+		fprintf(fp, "mc_forwarding %d ",
+			*(int *)RTA_DATA(tb[NETCONFA_MC_FORWARDING]));
+
+	fprintf(fp, "\n");
+	fflush(fp);
+	return 0;
+}
+
+static void ipnetconf_reset_filter(void)
+{
+	memset(&filter, 0, sizeof(filter));
+}
+
+static int do_show(int argc, char **argv)
+{
+	struct {
+		struct nlmsghdr		n;
+		struct netconfmsg	ncm;
+		char			buf[1024];
+	} req;
+
+	ipnetconf_reset_filter();
+	filter.family = preferred_family;
+	if (filter.family == AF_UNSPEC)
+		filter.family = AF_INET;
+	filter.ifindex = NETCONFA_IFINDEX_ALL;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			filter.ifindex = ll_name_to_index(*argv);
+			if (filter.ifindex <= 0) {
+				fprintf(stderr, "Device \"%s\" does not exist.\n",
+					*argv);
+				return -1;
+			}
+		}
+		argv++; argc--;
+	}
+
+	ll_init_map(&rth);
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct netconfmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
+	req.n.nlmsg_type = RTM_GETNETCONF;
+	req.ncm.ncm_family = filter.family;
+	addattr_l(&req.n, sizeof(req), NETCONFA_IFINDEX, &filter.ifindex,
+		  sizeof(filter.ifindex));
+
+	rtnl_send(&rth, &req.n, req.n.nlmsg_len);
+	rtnl_listen(&rth, print_netconf, stdout);
+
+	return 0;
+}
+
+int do_ipnetconf(int argc, char **argv)
+{
+	if (argc > 0) {
+		if (matches(*argv, "show") == 0 ||
+		    matches(*argv, "lst") == 0 ||
+		    matches(*argv, "list") == 0)
+			return do_show(argc-1, argv+1);
+		if (matches(*argv, "help") == 0)
+			usage();
+	} else
+		return do_show(0, NULL);
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip netconf help\".\n", *argv);
+	exit(-1);
+}
diff --git a/ip/ipnetns.c b/ip/ipnetns.c
index e41a598..b047b97 100644
--- a/ip/ipnetns.c
+++ b/ip/ipnetns.c
@@ -13,6 +13,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <unistd.h>
+#include <ctype.h>
 
 #include "utils.h"
 #include "ip_common.h"
@@ -40,17 +41,16 @@
 }
 #endif /* HAVE_SETNS */
 
-
-static void usage(void) __attribute__((noreturn));
-
-static void usage(void)
+static int usage(void)
 {
 	fprintf(stderr, "Usage: ip netns list\n");
 	fprintf(stderr, "       ip netns add NAME\n");
 	fprintf(stderr, "       ip netns delete NAME\n");
+	fprintf(stderr, "       ip netns identify PID\n");
+	fprintf(stderr, "       ip netns pids NAME\n");
 	fprintf(stderr, "       ip netns exec NAME cmd ...\n");
 	fprintf(stderr, "       ip netns monitor\n");
-	exit(-1);
+	return EXIT_FAILURE;
 }
 
 int get_netns_fd(const char *name)
@@ -75,7 +75,7 @@
 
 	dir = opendir(NETNS_RUN_DIR);
 	if (!dir)
-		return 0;
+		return EXIT_SUCCESS;
 
 	while ((entry = readdir(dir)) != NULL) {
 		if (strcmp(entry->d_name, ".") == 0)
@@ -85,7 +85,7 @@
 		printf("%s\n", entry->d_name);
 	}
 	closedir(dir);
-	return 0;
+	return EXIT_SUCCESS;
 }
 
 static void bind_etc(const char *name)
@@ -127,48 +127,193 @@
 
 	if (argc < 1) {
 		fprintf(stderr, "No netns name specified\n");
-		return -1;
+		return EXIT_FAILURE;
 	}
 	if (argc < 2) {
-		fprintf(stderr, "No cmd specified\n");
-		return -1;
+		fprintf(stderr, "No command specified\n");
+		return EXIT_FAILURE;
 	}
 	name = argv[0];
 	cmd = argv[1];
 	snprintf(net_path, sizeof(net_path), "%s/%s", NETNS_RUN_DIR, name);
 	netns = open(net_path, O_RDONLY);
 	if (netns < 0) {
-		fprintf(stderr, "Cannot open network namespace: %s\n",
-			strerror(errno));
-		return -1;
+		fprintf(stderr, "Cannot open network namespace \"%s\": %s\n",
+			name, strerror(errno));
+		return EXIT_FAILURE;
 	}
 	if (setns(netns, CLONE_NEWNET) < 0) {
-		fprintf(stderr, "seting the network namespace failed: %s\n",
-			strerror(errno));
-		return -1;
+		fprintf(stderr, "seting the network namespace \"%s\" failed: %s\n",
+			name, strerror(errno));
+		return EXIT_FAILURE;
 	}
 
 	if (unshare(CLONE_NEWNS) < 0) {
 		fprintf(stderr, "unshare failed: %s\n", strerror(errno));
-		return -1;
+		return EXIT_FAILURE;
+	}
+	/* Don't let any mounts propogate back to the parent */
+	if (mount("", "/", "none", MS_SLAVE | MS_REC, NULL)) {
+		fprintf(stderr, "\"mount --make-rslave /\" failed: %s\n",
+			strerror(errno));
+		return EXIT_FAILURE;
 	}
 	/* Mount a version of /sys that describes the network namespace */
 	if (umount2("/sys", MNT_DETACH) < 0) {
 		fprintf(stderr, "umount of /sys failed: %s\n", strerror(errno));
-		return -1;
+		return EXIT_FAILURE;
 	}
 	if (mount(name, "/sys", "sysfs", 0, NULL) < 0) {
 		fprintf(stderr, "mount of /sys failed: %s\n",strerror(errno));
-		return -1;
+		return EXIT_FAILURE;
 	}
 
 	/* Setup bind mounts for config files in /etc */
 	bind_etc(name);
 
 	if (execvp(cmd, argv + 1)  < 0)
-		fprintf(stderr, "exec of %s failed: %s\n",
+		fprintf(stderr, "exec of \"%s\" failed: %s\n",
 			cmd, strerror(errno));
-	exit(-1);
+	return EXIT_FAILURE;
+}
+
+static int is_pid(const char *str)
+{
+	int ch;
+	for (; (ch = *str); str++) {
+		if (!isdigit(ch))
+			return 0;
+	}
+	return 1;
+}
+
+static int netns_pids(int argc, char **argv)
+{
+	const char *name;
+	char net_path[MAXPATHLEN];
+	int netns;
+	struct stat netst;
+	DIR *dir;
+	struct dirent *entry;
+
+	if (argc < 1) {
+		fprintf(stderr, "No netns name specified\n");
+		return EXIT_FAILURE;
+	}
+	if (argc > 1) {
+		fprintf(stderr, "extra arguments specified\n");
+		return EXIT_FAILURE;
+	}
+
+	name = argv[0];
+	snprintf(net_path, sizeof(net_path), "%s/%s", NETNS_RUN_DIR, name);
+	netns = open(net_path, O_RDONLY);
+	if (netns < 0) {
+		fprintf(stderr, "Cannot open network namespace: %s\n",
+			strerror(errno));
+		return EXIT_FAILURE;
+	}
+	if (fstat(netns, &netst) < 0) {
+		fprintf(stderr, "Stat of netns failed: %s\n",
+			strerror(errno));
+		return EXIT_FAILURE;
+	}
+	dir = opendir("/proc/");
+	if (!dir) {
+		fprintf(stderr, "Open of /proc failed: %s\n",
+			strerror(errno));
+		return EXIT_FAILURE;
+	}
+	while((entry = readdir(dir))) {
+		char pid_net_path[MAXPATHLEN];
+		struct stat st;
+		if (!is_pid(entry->d_name))
+			continue;
+		snprintf(pid_net_path, sizeof(pid_net_path), "/proc/%s/ns/net",
+			entry->d_name);
+		if (stat(pid_net_path, &st) != 0)
+			continue;
+		if ((st.st_dev == netst.st_dev) &&
+		    (st.st_ino == netst.st_ino)) {
+			printf("%s\n", entry->d_name);
+		}
+	}
+	closedir(dir);
+	return EXIT_SUCCESS;
+	
+}
+
+static int netns_identify(int argc, char **argv)
+{
+	const char *pidstr;
+	char net_path[MAXPATHLEN];
+	int netns;
+	struct stat netst;
+	DIR *dir;
+	struct dirent *entry;
+
+	if (argc < 1) {
+		fprintf(stderr, "No pid specified\n");
+		return EXIT_FAILURE;
+	}
+	if (argc > 1) {
+		fprintf(stderr, "extra arguments specified\n");
+		return EXIT_FAILURE;
+	}
+	pidstr = argv[0];
+
+	if (!is_pid(pidstr)) {
+		fprintf(stderr, "Specified string '%s' is not a pid\n",
+			pidstr);
+		return EXIT_FAILURE;
+	}
+
+	snprintf(net_path, sizeof(net_path), "/proc/%s/ns/net", pidstr);
+	netns = open(net_path, O_RDONLY);
+	if (netns < 0) {
+		fprintf(stderr, "Cannot open network namespace: %s\n",
+			strerror(errno));
+		return EXIT_FAILURE;
+	}
+	if (fstat(netns, &netst) < 0) {
+		fprintf(stderr, "Stat of netns failed: %s\n",
+			strerror(errno));
+		return EXIT_FAILURE;
+	}
+	dir = opendir(NETNS_RUN_DIR);
+	if (!dir) {
+		/* Succeed treat a missing directory as an empty directory */
+		if (errno == ENOENT)
+			return EXIT_SUCCESS;
+
+		fprintf(stderr, "Failed to open directory %s:%s\n",
+			NETNS_RUN_DIR, strerror(errno));
+		return EXIT_FAILURE;
+	}
+
+	while((entry = readdir(dir))) {
+		char name_path[MAXPATHLEN];
+		struct stat st;
+
+		if (strcmp(entry->d_name, ".") == 0)
+			continue;
+		if (strcmp(entry->d_name, "..") == 0)
+			continue;
+
+		snprintf(name_path, sizeof(name_path), "%s/%s",	NETNS_RUN_DIR,
+			entry->d_name);
+
+		if (stat(name_path, &st) != 0)
+			continue;
+
+		if ((st.st_dev == netst.st_dev) &&
+		    (st.st_ino == netst.st_ino)) {
+			printf("%s\n", entry->d_name);
+		}
+	}
+	closedir(dir);
+	return EXIT_SUCCESS;
+	
 }
 
 static int netns_delete(int argc, char **argv)
@@ -178,18 +323,18 @@
 
 	if (argc < 1) {
 		fprintf(stderr, "No netns name specified\n");
-		return -1;
+		return EXIT_FAILURE;
 	}
 
 	name = argv[0];
 	snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);
 	umount2(netns_path, MNT_DETACH);
 	if (unlink(netns_path) < 0) {
-		fprintf(stderr, "Cannot remove %s: %s\n",
+		fprintf(stderr, "Cannot remove namespace file \"%s\": %s\n",
 			netns_path, strerror(errno));
-		return -1;
+		return EXIT_FAILURE;
 	}
-	return 0;
+	return EXIT_SUCCESS;
 }
 
 static int netns_add(int argc, char **argv)
@@ -205,10 +350,11 @@
 	char netns_path[MAXPATHLEN];
 	const char *name;
 	int fd;
+	int made_netns_run_dir_mount = 0;
 
 	if (argc < 1) {
 		fprintf(stderr, "No netns name specified\n");
-		return -1;
+		return EXIT_FAILURE;
 	}
 	name = argv[0];
 
@@ -217,17 +363,40 @@
 	/* Create the base netns directory if it doesn't exist */
 	mkdir(NETNS_RUN_DIR, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
 
+	/* Make it possible for network namespace mounts to propogate between
+	 * mount namespaces.  This makes it likely that a unmounting a network
+	 * namespace file in one namespace will unmount the network namespace
+	 * file in all namespaces allowing the network namespace to be freed
+	 * sooner.
+	 */
+	while (mount("", NETNS_RUN_DIR, "none", MS_SHARED | MS_REC, NULL)) {
+		/* Fail unless we need to make the mount point */
+		if (errno != EINVAL || made_netns_run_dir_mount) {
+			fprintf(stderr, "mount --make-shared %s failed: %s\n",
+				NETNS_RUN_DIR, strerror(errno));
+			return EXIT_FAILURE;
+		}
+
+		/* Upgrade NETNS_RUN_DIR to a mount point */
+		if (mount(NETNS_RUN_DIR, NETNS_RUN_DIR, "none", MS_BIND, NULL)) {
+			fprintf(stderr, "mount --bind %s %s failed: %s\n",
+				NETNS_RUN_DIR, NETNS_RUN_DIR, strerror(errno));
+			return EXIT_FAILURE;
+		}
+		made_netns_run_dir_mount = 1;
+	}
+
 	/* Create the filesystem state */
 	fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0);
 	if (fd < 0) {
-		fprintf(stderr, "Could not create %s: %s\n",
+		fprintf(stderr, "Cannot not create namespace file \"%s\": %s\n",
 			netns_path, strerror(errno));
-		return -1;
+		return EXIT_FAILURE;
 	}
 	close(fd);
 	if (unshare(CLONE_NEWNET) < 0) {
-		fprintf(stderr, "Failed to create a new network namespace: %s\n",
-			strerror(errno));
+		fprintf(stderr, "Failed to create a new network namespace \"%s\": %s\n",
+			name, strerror(errno));
 		goto out_delete;
 	}
 
@@ -237,11 +406,10 @@
 			netns_path, strerror(errno));
 		goto out_delete;
 	}
-	return 0;
+	return EXIT_SUCCESS;
 out_delete:
 	netns_delete(argc, argv);
-	exit(-1);
-	return -1;
+	return EXIT_FAILURE;
 }
 
 
@@ -254,19 +422,19 @@
 	if (fd < 0) {
 		fprintf(stderr, "inotify_init failed: %s\n",
 			strerror(errno));
-		return -1;
+		return EXIT_FAILURE;
 	}
 	if (inotify_add_watch(fd, NETNS_RUN_DIR, IN_CREATE | IN_DELETE) < 0) {
 		fprintf(stderr, "inotify_add_watch failed: %s\n",
 			strerror(errno));
-		return -1;
+		return EXIT_FAILURE;
 	}
 	for(;;) {
 		ssize_t len = read(fd, buf, sizeof(buf));
 		if (len < 0) {
 			fprintf(stderr, "read failed: %s\n",
 				strerror(errno));
-			return -1;
+			return EXIT_FAILURE;
 		}
 		for (event = (struct inotify_event *)buf;
 		     (char *)event < &buf[len];
@@ -277,7 +445,7 @@
 				printf("delete %s\n", event->name);
 		}
 	}
-	return 0;
+	return EXIT_SUCCESS;
 }
 
 int do_netns(int argc, char **argv)
@@ -290,7 +458,7 @@
 		return netns_list(argc-1, argv+1);
 
 	if (matches(*argv, "help") == 0)
-		usage();
+		return usage();
 
 	if (matches(*argv, "add") == 0)
 		return netns_add(argc-1, argv+1);
@@ -298,6 +466,12 @@
 	if (matches(*argv, "delete") == 0)
 		return netns_delete(argc-1, argv+1);
 
+	if (matches(*argv, "identify") == 0)
+		return netns_identify(argc-1, argv+1);
+
+	if (matches(*argv, "pids") == 0)
+		return netns_pids(argc-1, argv+1);
+
 	if (matches(*argv, "exec") == 0)
 		return netns_exec(argc-1, argv+1);
 
@@ -305,5 +479,5 @@
 		return netns_monitor(argc-1, argv+1);
 
 	fprintf(stderr, "Command \"%s\" is unknown, try \"ip netns help\".\n", *argv);
-	exit(-1);
+	return EXIT_FAILURE;
 }
diff --git a/ip/ipntable.c b/ip/ipntable.c
index 639f512..5751114 100644
--- a/ip/ipntable.c
+++ b/ip/ipntable.c
@@ -27,6 +27,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/time.h>
+#include <sys/socket.h>
 #include <time.h>
 
 #include "utils.h"
diff --git a/ip/ipprefix.c b/ip/ipprefix.c
index d8327be..9725077 100644
--- a/ip/ipprefix.c
+++ b/ip/ipprefix.c
@@ -26,8 +26,11 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/socket.h>
 #include <netinet/icmp6.h>
+
 #include "utils.h"
+#include "ip_common.h"
 
 /* prefix flags; see kernel's net/ipv6/addrconf.c and include/net/if_inet6.h */
 #define IF_PREFIX_ONLINK	0x01
@@ -59,7 +62,7 @@
 		return 0;
 
 	if (prefix->prefix_family != AF_INET6) {
-		fprintf(stderr, "wrong family %d\n", prefix->prefix_family);
+		fprintf(stderr, "incorrect protocol family: %d\n", prefix->prefix_family);
 		return 0;
 	}
 	if (prefix->prefix_type != ND_OPT_PREFIX_INFORMATION) {
diff --git a/ip/iproute.c b/ip/iproute.c
index c60156f..2c2a331 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -92,7 +92,7 @@
 
 static struct
 {
-	int tb;
+	unsigned int tb;
 	int cloned;
 	int flushed;
 	char *flushb;
@@ -124,7 +124,7 @@
 	return 0;
 }
 
-int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len)
+static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len)
 {
 	struct rtmsg *r = NLMSG_DATA(n);
 	inet_prefix dst;
@@ -263,7 +263,7 @@
 	return 1;
 }
 
-int calc_host_len(struct rtmsg *r)
+static int calc_host_len(const struct rtmsg *r)
 {
 	if (r->rtm_family == AF_INET6)
 		return 128;
@@ -625,7 +625,9 @@
 }
 
 
-int parse_one_nh(struct rtmsg *r, struct rtattr *rta, struct rtnexthop *rtnh, int *argcp, char ***argvp)
+static int parse_one_nh(struct rtmsg *r, struct rtattr *rta,
+			struct rtnexthop *rtnh,
+			int *argcp, char ***argvp)
 {
 	int argc = *argcp;
 	char **argv = *argvp;
@@ -668,7 +670,8 @@
 	return 0;
 }
 
-int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r, int argc, char **argv)
+static int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r,
+			  int argc, char **argv)
 {
 	char buf[1024];
 	struct rtattr *rta = (void*)buf;
@@ -699,8 +702,7 @@
 	return 0;
 }
 
-
-int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
+static int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
 {
 	struct {
 		struct nlmsghdr 	n;
@@ -1070,7 +1072,8 @@
 
 static __u32 route_dump_magic = 0x45311224;
 
-int save_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static int save_route(const struct sockaddr_nl *who, struct nlmsghdr *n,
+		      void *arg)
 {
 	int ret;
 	int len = n->nlmsg_len;
@@ -1099,7 +1102,7 @@
 	int ret;
 
 	if (isatty(STDOUT_FILENO)) {
-		fprintf(stderr, "Not sending binary stream to stdout\n");
+		fprintf(stderr, "Not sending a binary stream to stdout\n");
 		return -1;
 	}
 
@@ -1363,7 +1366,7 @@
 }
 
 
-int iproute_get(int argc, char **argv)
+static int iproute_get(int argc, char **argv)
 {
 	struct {
 		struct nlmsghdr 	n;
@@ -1445,7 +1448,7 @@
 	}
 
 	if (req.r.rtm_dst_len == 0) {
-		fprintf(stderr, "need at least destination address\n");
+		fprintf(stderr, "need at least a destination address\n");
 		exit(1);
 	}
 
@@ -1528,7 +1531,8 @@
 	exit(0);
 }
 
-int restore_handler(const struct sockaddr_nl *nl, struct nlmsghdr *n, void *arg)
+static int restore_handler(const struct sockaddr_nl *nl, struct nlmsghdr *n,
+			   void *arg)
 {
 	int ret;
 
@@ -1562,7 +1566,7 @@
 	return 0;
 }
 
-int iproute_restore(void)
+static int iproute_restore(void)
 {
 	if (route_dump_check_magic())
 		exit(-1);
@@ -1584,7 +1588,7 @@
 	exit(rtnl_from_file(stdin, &show_handler, NULL));
 }
 
-void iproute_reset_filter()
+void iproute_reset_filter(void)
 {
 	memset(&filter, 0, sizeof(filter));
 	filter.mdst.bitlen = -1;
diff --git a/ip/iptunnel.c b/ip/iptunnel.c
index 0cf6cf8..4275f26 100644
--- a/ip/iptunnel.c
+++ b/ip/iptunnel.c
@@ -41,7 +41,7 @@
 	fprintf(stderr, "\n");
 	fprintf(stderr, "Where: NAME := STRING\n");
 	fprintf(stderr, "       ADDR := { IP_ADDRESS | any }\n");
-	fprintf(stderr, "       TOS  := { NUMBER | inherit }\n");
+	fprintf(stderr, "       TOS  := { STRING | 00..ff | inherit | inherit/STRING | inherit/00..ff }\n");
 	fprintf(stderr, "       TTL  := { 1..255 | inherit }\n");
 	fprintf(stderr, "       KEY  := { DOTTED_QUAD | NUMBER }\n");
 	exit(-1);
@@ -102,7 +102,7 @@
 				p->iph.protocol = IPPROTO_IPIP;
 				p->i_flags |= VTI_ISVTI;
 			} else {
-				fprintf(stderr,"Cannot guess tunnel mode.\n");
+				fprintf(stderr,"Unknown tunnel mode \"%s\"\n", *argv);
 				exit(-1);
 			}
 		} else if (strcmp(*argv, "key") == 0) {
@@ -114,7 +114,7 @@
 				p->i_key = p->o_key = get_addr32(*argv);
 			else {
 				if (get_unsigned(&uval, *argv, 0)<0) {
-					fprintf(stderr, "invalid value of \"key\"\n");
+					fprintf(stderr, "invalid value for \"key\": \"%s\"; it should be an unsigned integer\n", *argv);
 					exit(-1);
 				}
 				p->i_key = p->o_key = htonl(uval);
@@ -127,7 +127,7 @@
 				p->i_key = get_addr32(*argv);
 			else {
 				if (get_unsigned(&uval, *argv, 0)<0) {
-					fprintf(stderr, "invalid value of \"ikey\"\n");
+					fprintf(stderr, "invalid value for \"ikey\": \"%s\"; it should be an unsigned integer\n", *argv);
 					exit(-1);
 				}
 				p->i_key = htonl(uval);
@@ -140,7 +140,7 @@
 				p->o_key = get_addr32(*argv);
 			else {
 				if (get_unsigned(&uval, *argv, 0)<0) {
-					fprintf(stderr, "invalid value of \"okey\"\n");
+					fprintf(stderr, "invalid value for \"okey\": \"%s\"; it should be an unsigned integer\n", *argv);
 					exit(-1);
 				}
 				p->o_key = htonl(uval);
@@ -188,14 +188,21 @@
 		} else if (strcmp(*argv, "tos") == 0 ||
 			   strcmp(*argv, "tclass") == 0 ||
 			   matches(*argv, "dsfield") == 0) {
+			char *dsfield;
 			__u32 uval;
 			NEXT_ARG();
+			dsfield = *argv;
+			strsep(&dsfield, "/");
 			if (strcmp(*argv, "inherit") != 0) {
-				if (rtnl_dsfield_a2n(&uval, *argv))
-					invarg("bad TOS value", *argv);
-				p->iph.tos = uval;
+				dsfield = *argv;
+				p->iph.tos = 0;
 			} else
 				p->iph.tos = 1;
+			if (dsfield) {
+				if (rtnl_dsfield_a2n(&uval, dsfield))
+					invarg("bad TOS value", *argv);
+				p->iph.tos |= uval;
+			}
 		} else {
 			if (strcmp(*argv, "name") == 0) {
 				NEXT_ARG();
@@ -235,7 +242,7 @@
 
 	if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
 		if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
-			fprintf(stderr, "Keys are not allowed with ipip and sit.\n");
+			fprintf(stderr, "Keys are not allowed with ipip and sit tunnels\n");
 			return -1;
 		}
 	}
@@ -255,7 +262,7 @@
 		p->o_flags |= GRE_KEY;
 	}
 	if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) {
-		fprintf(stderr, "Broadcast tunnel requires a source address.\n");
+		fprintf(stderr, "A broadcast tunnel requires a source address\n");
 		return -1;
 	}
 	if (isatap)
@@ -437,7 +444,7 @@
 		buf[sizeof(buf) - 1] = 0;
 		if ((ptr = strchr(buf, ':')) == NULL ||
 		    (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
-			fprintf(stderr, "Wrong format of /proc/net/dev. Sorry.\n");
+			fprintf(stderr, "Wrong format for /proc/net/dev. Giving up.\n");
 			fclose(fp);
 			return -1;
 		}
@@ -454,7 +461,7 @@
 			continue;
 		type = ll_index_to_type(index);
 		if (type == -1) {
-			fprintf(stderr, "Failed to get type of [%s]\n", name);
+			fprintf(stderr, "Failed to get type of \"%s\"\n", name);
 			continue;
 		}
 		if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
@@ -551,17 +558,17 @@
 			strncpy(medium, *argv, IFNAMSIZ-1);
 			devname++;
 		} else {
-			fprintf(stderr,"%s: Invalid PRL parameter.\n", *argv);
+			fprintf(stderr,"Invalid PRL parameter \"%s\"\n", *argv);
 			exit(-1);
 		}
 		if (count > 1) {
-			fprintf(stderr,"One PRL entry at a time.\n");
+			fprintf(stderr,"One PRL entry at a time\n");
 			exit(-1);
 		}
 		argc--; argv++;
 	}
 	if (devname == 0) {
-		fprintf(stderr, "Must specify dev.\n");
+		fprintf(stderr, "Must specify device\n");
 		exit(-1);
 	}
 
@@ -601,13 +608,13 @@
 			strncpy(medium, *argv, IFNAMSIZ-1);
 			devname++;
 		} else {
-			fprintf(stderr,"%s: Invalid 6RD parameter.\n", *argv);
+			fprintf(stderr,"Invalid 6RD parameter \"%s\"\n", *argv);
 			exit(-1);
 		}
 		argc--; argv++;
 	}
 	if (devname == 0) {
-		fprintf(stderr, "Must specify dev.\n");
+		fprintf(stderr, "Must specify device\n");
 		exit(-1);
 	}
 
@@ -630,7 +637,7 @@
 	case AF_INET6:
 		return do_ip6tunnel(argc, argv);
 	default:
-		fprintf(stderr, "Unsupported family:%d\n", preferred_family);
+		fprintf(stderr, "Unsupported protocol family: %d\n", preferred_family);
 		exit(-1);
 	}
 
@@ -654,6 +661,6 @@
 	} else
 		return do_show(0, NULL);
 
-	fprintf(stderr, "Command \"%s\" is unknown, try \"ip tunnel help\".\n", *argv);
+	fprintf(stderr, "Command \"%s\" is unknown, try \"ip tunnel help\"\n", *argv);
 	exit(-1);
 }
diff --git a/ip/iptuntap.c b/ip/iptuntap.c
index 20914e1..3d9ad7d 100644
--- a/ip/iptuntap.c
+++ b/ip/iptuntap.c
@@ -128,7 +128,7 @@
 				}
 				ifr->ifr_flags |= IFF_TAP;
 			} else {
-				fprintf(stderr,"Cannot guess tunnel mode.\n");
+				fprintf(stderr,"Unknown tunnel mode \"%s\"\n", *argv);
 				exit(-1);
 			}
 		} else if (uid && matches(*argv, "user") == 0) {
diff --git a/ip/ipxfrm.c b/ip/ipxfrm.c
index c7b3420..db51918 100644
--- a/ip/ipxfrm.c
+++ b/ip/ipxfrm.c
@@ -25,6 +25,7 @@
  *	Masahide NAKAMURA @USAGI
  */
 
+#include <alloca.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -38,6 +39,7 @@
 
 #include "utils.h"
 #include "xfrm.h"
+#include "ip_common.h"
 
 #define STRBUF_SIZE	(128)
 #define STRBUF_CAT(buf, str) \
@@ -555,16 +557,13 @@
 static void xfrm_aead_print(struct xfrm_algo_aead *algo, int len,
 			    FILE *fp, const char *prefix)
 {
-	struct {
-		struct xfrm_algo algo;
-		char key[algo->alg_key_len / 8];
-	} base;
+	struct xfrm_algo *base_algo = alloca(sizeof(*base_algo) + algo->alg_key_len / 8);
 
-	memcpy(base.algo.alg_name, algo->alg_name, sizeof(base.algo.alg_name));
-	base.algo.alg_key_len = algo->alg_key_len;
-	memcpy(base.algo.alg_key, algo->alg_key, algo->alg_key_len / 8);
+	memcpy(base_algo->alg_name, algo->alg_name, sizeof(base_algo->alg_name));
+	base_algo->alg_key_len = algo->alg_key_len;
+	memcpy(base_algo->alg_key, algo->alg_key, algo->alg_key_len / 8);
 
-	__xfrm_algo_print(&base.algo, XFRMA_ALG_AEAD, len, fp, prefix, 0);
+	__xfrm_algo_print(base_algo, XFRMA_ALG_AEAD, len, fp, prefix, 0);
 
 	fprintf(fp, " %d", algo->alg_icv_len);
 
@@ -574,16 +573,13 @@
 static void xfrm_auth_trunc_print(struct xfrm_algo_auth *algo, int len,
 				  FILE *fp, const char *prefix)
 {
-	struct {
-		struct xfrm_algo algo;
-		char key[algo->alg_key_len / 8];
-	} base;
+	struct xfrm_algo *base_algo = alloca(sizeof(*base_algo) + algo->alg_key_len / 8);
 
-	memcpy(base.algo.alg_name, algo->alg_name, sizeof(base.algo.alg_name));
-	base.algo.alg_key_len = algo->alg_key_len;
-	memcpy(base.algo.alg_key, algo->alg_key, algo->alg_key_len / 8);
+	memcpy(base_algo->alg_name, algo->alg_name, sizeof(base_algo->alg_name));
+	base_algo->alg_key_len = algo->alg_key_len;
+	memcpy(base_algo->alg_key, algo->alg_key, algo->alg_key_len / 8);
 
-	__xfrm_algo_print(&base.algo, XFRMA_ALG_AUTH_TRUNC, len, fp, prefix, 0);
+	__xfrm_algo_print(base_algo, XFRMA_ALG_AUTH_TRUNC, len, fp, prefix, 0);
 
 	fprintf(fp, " %d", algo->alg_trunc_len);
 
@@ -1236,7 +1232,7 @@
 				uval = htonl(get_addr32(*argv));
 			else {
 				if (get_unsigned(&uval, *argv, 0)<0) {
-					fprintf(stderr, "invalid value of \"key\"\n");
+					fprintf(stderr, "invalid value for \"key\"; it should be an unsigned integer\n");
 					exit(-1);
 				}
 			}
diff --git a/ip/link_gre.c b/ip/link_gre.c
index 839fb29..7e0b896 100644
--- a/ip/link_gre.c
+++ b/ip/link_gre.c
@@ -141,7 +141,7 @@
 			else {
 				if (get_unsigned(&uval, *argv, 0) < 0) {
 					fprintf(stderr,
-						"Invalid value for \"key\"\n");
+						"Invalid value for \"key\": \"%s\"; it should be an unsigned integer\n", *argv);
 					exit(-1);
 				}
 				uval = htonl(uval);
@@ -157,7 +157,7 @@
 				uval = get_addr32(*argv);
 			else {
 				if (get_unsigned(&uval, *argv, 0)<0) {
-					fprintf(stderr, "invalid value of \"ikey\"\n");
+					fprintf(stderr, "invalid value for \"ikey\": \"%s\"; it should be an unsigned integer\n", *argv);
 					exit(-1);
 				}
 				uval = htonl(uval);
@@ -172,7 +172,7 @@
 				uval = get_addr32(*argv);
 			else {
 				if (get_unsigned(&uval, *argv, 0)<0) {
-					fprintf(stderr, "invalid value of \"okey\"\n");
+					fprintf(stderr, "invalid value for \"okey\": \"%s\"; it should be an unsigned integer\n", *argv);
 					exit(-1);
 				}
 				uval = htonl(uval);
@@ -247,7 +247,7 @@
 		oflags |= GRE_KEY;
 	}
 	if (IN_MULTICAST(ntohl(daddr)) && !saddr) {
-		fprintf(stderr, "Broadcast tunnel requires a source address.\n");
+		fprintf(stderr, "A broadcast tunnel requires a source address.\n");
 		return -1;
 	}
 
diff --git a/ip/link_ip6tnl.c b/ip/link_ip6tnl.c
new file mode 100644
index 0000000..2947364
--- /dev/null
+++ b/ip/link_ip6tnl.c
@@ -0,0 +1,344 @@
+/*
+ * link_ip6tnl.c	ip6tnl driver module
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Nicolas Dichtel <nicolas.dichtel@6wind.com>
+ *
+ */
+
+#include <string.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+#include <linux/ip6_tunnel.h>
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+#include "tunnel.h"
+
+#define IP6_FLOWINFO_TCLASS	htonl(0x0FF00000)
+#define IP6_FLOWINFO_FLOWLABEL	htonl(0x000FFFFF)
+
+#define DEFAULT_TNL_HOP_LIMIT	(64)
+
+static void usage(void) __attribute__((noreturn));
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip link { add | set | change | replace | del } NAME\n");
+	fprintf(stderr, "          type ip6tnl [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(stderr, "          [ dev PHYS_DEV ] [ encaplimit ELIM ]\n");
+	fprintf(stderr ,"          [ hoplimit HLIM ] [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n");
+	fprintf(stderr, "          [ dscp inherit ] [ fwmark inherit ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME      := STRING\n");
+	fprintf(stderr, "       ADDR      := IPV6_ADDRESS\n");
+	fprintf(stderr, "       ELIM      := { none | 0..255 }(default=%d)\n",
+		IPV6_DEFAULT_TNL_ENCAP_LIMIT);
+	fprintf(stderr, "       HLIM      := 0..255 (default=%d)\n",
+		DEFAULT_TNL_HOP_LIMIT);
+	fprintf(stderr, "       TCLASS    := { 0x0..0xff | inherit }\n");
+	fprintf(stderr, "       FLOWLABEL := { 0x0..0xfffff | inherit }\n");
+	exit(-1);
+}
+
+static int ip6tunnel_parse_opt(struct link_util *lu, int argc, char **argv,
+			       struct nlmsghdr *n)
+{
+	struct {
+		struct nlmsghdr n;
+		struct ifinfomsg i;
+		char buf[2048];
+	} req;
+	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
+	struct rtattr *tb[IFLA_MAX + 1];
+	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
+	struct rtattr *iptuninfo[IFLA_IPTUN_MAX + 1];
+	int len;
+	struct in6_addr laddr;
+	struct in6_addr raddr;
+	__u8 hop_limit = DEFAULT_TNL_HOP_LIMIT;
+	__u8 encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
+	__u32 flowinfo = 0;
+	__u32 flags = 0;
+	__u32 link = 0;
+	__u8 proto = 0;
+
+	memset(&laddr, 0, sizeof(laddr));
+	memset(&raddr, 0, sizeof(raddr));
+
+	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
+		memset(&req, 0, sizeof(req));
+
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETLINK;
+		req.i.ifi_family = preferred_family;
+		req.i.ifi_index = ifi->ifi_index;
+
+		if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0) {
+get_failed:
+			fprintf(stderr,
+				"Failed to get existing tunnel info.\n");
+			return -1;
+		}
+
+		len = req.n.nlmsg_len;
+		len -= NLMSG_LENGTH(sizeof(*ifi));
+		if (len < 0)
+			goto get_failed;
+
+		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
+
+		if (!tb[IFLA_LINKINFO])
+			goto get_failed;
+
+		parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
+
+		if (!linkinfo[IFLA_INFO_DATA])
+			goto get_failed;
+
+		parse_rtattr_nested(iptuninfo, IFLA_IPTUN_MAX,
+				    linkinfo[IFLA_INFO_DATA]);
+
+		if (iptuninfo[IFLA_IPTUN_LOCAL])
+			memcpy(&laddr, RTA_DATA(iptuninfo[IFLA_IPTUN_LOCAL]),
+			       sizeof(laddr));
+
+		if (iptuninfo[IFLA_IPTUN_REMOTE])
+			memcpy(&raddr, RTA_DATA(iptuninfo[IFLA_IPTUN_REMOTE]),
+			       sizeof(raddr));
+
+		if (iptuninfo[IFLA_IPTUN_TTL])
+			hop_limit = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TTL]);
+
+		if (iptuninfo[IFLA_IPTUN_ENCAP_LIMIT])
+			encap_limit = rta_getattr_u8(iptuninfo[IFLA_IPTUN_ENCAP_LIMIT]);
+
+		if (iptuninfo[IFLA_IPTUN_FLOWINFO])
+			flowinfo = rta_getattr_u32(iptuninfo[IFLA_IPTUN_FLOWINFO]);
+
+		if (iptuninfo[IFLA_IPTUN_FLAGS])
+			flags = rta_getattr_u32(iptuninfo[IFLA_IPTUN_FLAGS]);
+
+		if (iptuninfo[IFLA_IPTUN_LINK])
+			link = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LINK]);
+
+		if (iptuninfo[IFLA_IPTUN_PROTO])
+			proto = rta_getattr_u8(iptuninfo[IFLA_IPTUN_PROTO]);
+	}
+
+	while (argc > 0) {
+		if (matches(*argv, "mode") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "ipv6/ipv6") == 0 ||
+			    strcmp(*argv, "ip6ip6") == 0)
+				proto = IPPROTO_IPV6;
+			else if (strcmp(*argv, "ip/ipv6") == 0 ||
+				 strcmp(*argv, "ipv4/ipv6") == 0 ||
+				 strcmp(*argv, "ipip6") == 0 ||
+				 strcmp(*argv, "ip4ip6") == 0)
+				proto = IPPROTO_IPIP;
+			else if (strcmp(*argv, "any/ipv6") == 0 ||
+				 strcmp(*argv, "any") == 0)
+				proto = 0;
+			else
+				invarg("Cannot guess tunnel mode.", *argv);
+		} else if (strcmp(*argv, "remote") == 0) {
+			inet_prefix addr;
+			NEXT_ARG();
+			get_prefix(&addr, *argv, preferred_family);
+			if (addr.family == AF_UNSPEC)
+				invarg("\"remote\" address family is AF_UNSPEC", *argv);
+			memcpy(&raddr, addr.data, addr.bytelen);
+		} else if (strcmp(*argv, "local") == 0) {
+			inet_prefix addr;
+			NEXT_ARG();
+			get_prefix(&addr, *argv, preferred_family);
+			if (addr.family == AF_UNSPEC)
+				invarg("\"local\" address family is AF_UNSPEC", *argv);
+			memcpy(&laddr, addr.data, addr.bytelen);
+		} else if (matches(*argv, "dev") == 0) {
+			NEXT_ARG();
+			link = if_nametoindex(*argv);
+			if (link == 0)
+				invarg("\"dev\" is invalid", *argv);
+		} else if (strcmp(*argv, "hoplimit") == 0 ||
+			   strcmp(*argv, "ttl") == 0 ||
+			   strcmp(*argv, "hlim") == 0) {
+			__u8 uval;
+			NEXT_ARG();
+			if (get_u8(&uval, *argv, 0))
+				invarg("invalid HLIM", *argv);
+			hop_limit = uval;
+		} else if (matches(*argv, "encaplimit") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "none") == 0) {
+				flags |= IP6_TNL_F_IGN_ENCAP_LIMIT;
+			} else {
+				__u8 uval;
+				if (get_u8(&uval, *argv, 0) < -1)
+					invarg("invalid ELIM", *argv);
+				encap_limit = uval;
+				flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT;
+			}
+		} else if (strcmp(*argv, "tclass") == 0 ||
+			   strcmp(*argv, "tc") == 0 ||
+			   strcmp(*argv, "tos") == 0 ||
+			   matches(*argv, "dsfield") == 0) {
+			__u8 uval;
+			NEXT_ARG();
+			flowinfo &= ~IP6_FLOWINFO_TCLASS;
+			if (strcmp(*argv, "inherit") == 0)
+				flags |= IP6_TNL_F_USE_ORIG_TCLASS;
+			else {
+				if (get_u8(&uval, *argv, 16))
+					invarg("invalid TClass", *argv);
+				flowinfo |= htonl((__u32)uval << 20) & IP6_FLOWINFO_TCLASS;
+				flags &= ~IP6_TNL_F_USE_ORIG_TCLASS;
+			}
+		} else if (strcmp(*argv, "flowlabel") == 0 ||
+			   strcmp(*argv, "fl") == 0) {
+			__u32 uval;
+			NEXT_ARG();
+			flowinfo &= ~IP6_FLOWINFO_FLOWLABEL;
+			if (strcmp(*argv, "inherit") == 0)
+				flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
+			else {
+				if (get_u32(&uval, *argv, 16))
+					invarg("invalid Flowlabel", *argv);
+				if (uval > 0xFFFFF)
+					invarg("invalid Flowlabel", *argv);
+				flowinfo |= htonl(uval) & IP6_FLOWINFO_FLOWLABEL;
+				flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
+			}
+		} else if (strcmp(*argv, "dscp") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") != 0)
+				invarg("not inherit", *argv);
+			flags |= IP6_TNL_F_RCV_DSCP_COPY;
+		} else if (strcmp(*argv, "fwmark") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") != 0)
+				invarg("not inherit", *argv);
+			flags |= IP6_TNL_F_USE_ORIG_FWMARK;
+		} else
+			usage();
+		argc--, argv++;
+	}
+
+	addattr8(n, 1024, IFLA_IPTUN_PROTO, proto);
+	addattr_l(n, 1024, IFLA_IPTUN_LOCAL, &laddr, sizeof(laddr));
+	addattr_l(n, 1024, IFLA_IPTUN_REMOTE, &raddr, sizeof(raddr));
+	addattr8(n, 1024, IFLA_IPTUN_TTL, hop_limit);
+	addattr8(n, 1024, IFLA_IPTUN_ENCAP_LIMIT, encap_limit);
+	addattr32(n, 1024, IFLA_IPTUN_FLOWINFO, flowinfo);
+	addattr32(n, 1024, IFLA_IPTUN_FLAGS, flags);
+	addattr32(n, 1024, IFLA_IPTUN_LINK, link);
+
+	return 0;
+}
+
+static void ip6tunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+	char s1[256];
+	char s2[64];
+	int flags = 0;
+	__u32 flowinfo = 0;
+
+	if (!tb)
+		return;
+
+	if (tb[IFLA_IPTUN_FLAGS])
+		flags = rta_getattr_u32(tb[IFLA_IPTUN_FLAGS]);
+
+	if (tb[IFLA_IPTUN_FLOWINFO])
+		flowinfo = rta_getattr_u32(tb[IFLA_IPTUN_FLOWINFO]);
+
+	if (tb[IFLA_IPTUN_PROTO]) {
+		switch (rta_getattr_u8(tb[IFLA_IPTUN_PROTO])) {
+		case IPPROTO_IPIP:
+			fprintf(f, "ipip6 ");
+			break;
+		case IPPROTO_IPV6:
+			fprintf(f, "ip6ip6 ");
+			break;
+		case 0:
+			fprintf(f, "any ");
+			break;
+		}
+	}
+
+	if (tb[IFLA_IPTUN_REMOTE]) {
+		fprintf(f, "remote %s ",
+			rt_addr_n2a(AF_INET6,
+				    RTA_PAYLOAD(tb[IFLA_IPTUN_REMOTE]),
+				    RTA_DATA(tb[IFLA_IPTUN_REMOTE]),
+				    s1, sizeof(s1)));
+	}
+
+	if (tb[IFLA_IPTUN_LOCAL]) {
+		fprintf(f, "local %s ",
+			rt_addr_n2a(AF_INET6,
+				    RTA_PAYLOAD(tb[IFLA_IPTUN_LOCAL]),
+				    RTA_DATA(tb[IFLA_IPTUN_LOCAL]),
+				    s1, sizeof(s1)));
+	}
+
+	if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) {
+		unsigned link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]);
+		const char *n = if_indextoname(link, s2);
+
+		if (n)
+			fprintf(f, "dev %s ", n);
+		else
+			fprintf(f, "dev %u ", link);
+	}
+
+	if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
+		printf("encaplimit none ");
+	else if (tb[IFLA_IPTUN_ENCAP_LIMIT])
+		fprintf(f, "encaplimit %u ",
+			rta_getattr_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]));
+
+	if (tb[IFLA_IPTUN_TTL])
+		fprintf(f, "hoplimit %u ", rta_getattr_u8(tb[IFLA_IPTUN_TTL]));
+
+	if (flags & IP6_TNL_F_USE_ORIG_TCLASS)
+		printf("tclass inherit ");
+	else if (tb[IFLA_IPTUN_FLOWINFO]) {
+		__u32 val = ntohl(flowinfo & IP6_FLOWINFO_TCLASS);
+
+		printf("tclass 0x%02x ", (__u8)(val >> 20));
+	}
+
+	if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
+		printf("flowlabel inherit ");
+	else
+		printf("flowlabel 0x%05x ", ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
+
+	printf("(flowinfo 0x%08x) ", ntohl(flowinfo));
+
+	if (flags & IP6_TNL_F_RCV_DSCP_COPY)
+		printf("dscp inherit ");
+
+	if (flags & IP6_TNL_F_MIP6_DEV)
+		fprintf(f, "mip6 ");
+
+	if (flags & IP6_TNL_F_USE_ORIG_FWMARK)
+		fprintf(f, "fwmark inherit ");
+}
+
+struct link_util ip6tnl_link_util = {
+	.id = "ip6tnl",
+	.maxattr = IFLA_IPTUN_MAX,
+	.parse_opt = ip6tunnel_parse_opt,
+	.print_opt = ip6tunnel_print_opt,
+};
diff --git a/ip/link_iptnl.c b/ip/link_iptnl.c
new file mode 100644
index 0000000..b00d8d9
--- /dev/null
+++ b/ip/link_iptnl.c
@@ -0,0 +1,340 @@
+/*
+ * link_iptnl.c	ipip and sit driver module
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Nicolas Dichtel <nicolas.dichtel@6wind.com>
+ *
+ */
+
+#include <string.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+#include "tunnel.h"
+
+static void usage(int sit) __attribute__((noreturn));
+static void usage(int sit)
+{
+	fprintf(stderr, "Usage: ip link { add | set | change | replace | del } NAME\n");
+	fprintf(stderr, "          type { ipip | sit } [ remote ADDR ] [ local ADDR ]\n");
+	fprintf(stderr, "          [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n");
+	fprintf(stderr, "          [ 6rd-prefix ADDR ] [ 6rd-relay_prefix ADDR ] [ 6rd-reset ]\n");
+	if (sit)
+		fprintf(stderr, "          [ isatap ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME := STRING\n");
+	fprintf(stderr, "       ADDR := { IP_ADDRESS | any }\n");
+	fprintf(stderr, "       TOS  := { NUMBER | inherit }\n");
+	fprintf(stderr, "       TTL  := { 1..255 | inherit }\n");
+	exit(-1);
+}
+
+static int iptunnel_parse_opt(struct link_util *lu, int argc, char **argv,
+			      struct nlmsghdr *n)
+{
+	struct {
+		struct nlmsghdr n;
+		struct ifinfomsg i;
+		char buf[2048];
+	} req;
+	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
+	struct rtattr *tb[IFLA_MAX + 1];
+	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
+	struct rtattr *iptuninfo[IFLA_IPTUN_MAX + 1];
+	int len;
+	__u32 link = 0;
+	__u32 laddr = 0;
+	__u32 raddr = 0;
+	__u8 ttl = 0;
+	__u8 tos = 0;
+	__u8 pmtudisc = 1;
+	__u16 iflags = 0;
+	struct in6_addr ip6rdprefix;
+	__u16 ip6rdprefixlen = 0;
+	__u32 ip6rdrelayprefix = 0;
+	__u16 ip6rdrelayprefixlen = 0;
+
+	memset(&ip6rdprefix, 0, sizeof(ip6rdprefix));
+
+	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
+		memset(&req, 0, sizeof(req));
+
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETLINK;
+		req.i.ifi_family = preferred_family;
+		req.i.ifi_index = ifi->ifi_index;
+
+		if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0) {
+get_failed:
+			fprintf(stderr,
+				"Failed to get existing tunnel info.\n");
+			return -1;
+		}
+
+		len = req.n.nlmsg_len;
+		len -= NLMSG_LENGTH(sizeof(*ifi));
+		if (len < 0)
+			goto get_failed;
+
+		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
+
+		if (!tb[IFLA_LINKINFO])
+			goto get_failed;
+
+		parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
+
+		if (!linkinfo[IFLA_INFO_DATA])
+			goto get_failed;
+
+		parse_rtattr_nested(iptuninfo, IFLA_IPTUN_MAX,
+				    linkinfo[IFLA_INFO_DATA]);
+
+		if (iptuninfo[IFLA_IPTUN_LOCAL])
+			laddr = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LOCAL]);
+
+		if (iptuninfo[IFLA_IPTUN_REMOTE])
+			raddr = rta_getattr_u32(iptuninfo[IFLA_IPTUN_REMOTE]);
+
+		if (iptuninfo[IFLA_IPTUN_TTL])
+			ttl = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TTL]);
+
+		if (iptuninfo[IFLA_IPTUN_TOS])
+			tos = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TOS]);
+
+		if (iptuninfo[IFLA_IPTUN_PMTUDISC])
+			pmtudisc =
+				rta_getattr_u8(iptuninfo[IFLA_IPTUN_PMTUDISC]);
+
+		if (iptuninfo[IFLA_IPTUN_FLAGS])
+			iflags = rta_getattr_u16(iptuninfo[IFLA_IPTUN_FLAGS]);
+
+		if (iptuninfo[IFLA_IPTUN_LINK])
+			link = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LINK]);
+
+		if (iptuninfo[IFLA_IPTUN_6RD_PREFIX])
+			memcpy(&ip6rdprefix,
+			       RTA_DATA(iptuninfo[IFLA_IPTUN_6RD_PREFIX]),
+			       sizeof(laddr));
+
+		if (iptuninfo[IFLA_IPTUN_6RD_PREFIXLEN])
+			ip6rdprefixlen =
+				rta_getattr_u16(iptuninfo[IFLA_IPTUN_6RD_PREFIXLEN]);
+
+		if (iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIX])
+			ip6rdrelayprefix =
+				rta_getattr_u32(iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIX]);
+
+		if (iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN])
+			ip6rdrelayprefixlen =
+				rta_getattr_u16(iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]);
+	}
+
+	while (argc > 0) {
+		if (strcmp(*argv, "remote") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "any"))
+				raddr = get_addr32(*argv);
+			else
+				raddr = 0;
+		} else if (strcmp(*argv, "local") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "any"))
+				laddr = get_addr32(*argv);
+			else
+				laddr = 0;
+		} else if (matches(*argv, "dev") == 0) {
+			NEXT_ARG();
+			link = if_nametoindex(*argv);
+			if (link == 0)
+				invarg("\"dev\" is invalid", *argv);
+		} else if (strcmp(*argv, "ttl") == 0 ||
+			   strcmp(*argv, "hoplimit") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") != 0) {
+				if (get_u8(&ttl, *argv, 0))
+					invarg("invalid TTL\n", *argv);
+			} else
+				ttl = 0;
+		} else if (strcmp(*argv, "tos") == 0 ||
+			   strcmp(*argv, "tclass") == 0 ||
+			   matches(*argv, "dsfield") == 0) {
+			__u32 uval;
+			NEXT_ARG();
+			if (strcmp(*argv, "inherit") != 0) {
+				if (rtnl_dsfield_a2n(&uval, *argv))
+					invarg("bad TOS value", *argv);
+				tos = uval;
+			} else
+				tos = 1;
+		} else if (strcmp(*argv, "nopmtudisc") == 0) {
+			pmtudisc = 0;
+		} else if (strcmp(*argv, "pmtudisc") == 0) {
+			pmtudisc = 1;
+		} else if (strcmp(lu->id, "sit") == 0 &&
+			   strcmp(*argv, "isatap") == 0) {
+			iflags |= SIT_ISATAP;
+		} else if (strcmp(*argv, "6rd-prefix") == 0) {
+			inet_prefix prefix;
+			NEXT_ARG();
+			if (get_prefix(&prefix, *argv, AF_INET6))
+				invarg("invalid 6rd_prefix\n", *argv);
+			memcpy(&ip6rdprefix, prefix.data, 16);
+			ip6rdprefixlen = prefix.bitlen;
+		} else if (strcmp(*argv, "6rd-relay_prefix") == 0) {
+			inet_prefix prefix;
+			NEXT_ARG();
+			if (get_prefix(&prefix, *argv, AF_INET))
+				invarg("invalid 6rd-relay_prefix\n", *argv);
+			memcpy(&ip6rdrelayprefix, prefix.data, 4);
+			ip6rdrelayprefixlen = prefix.bitlen;
+		} else if (strcmp(*argv, "6rd-reset") == 0) {
+			inet_prefix prefix;
+			get_prefix(&prefix, "2002::", AF_INET6);
+			memcpy(&ip6rdprefix, prefix.data, 16);
+			ip6rdprefixlen = 16;
+			ip6rdrelayprefix = 0;
+			ip6rdrelayprefixlen = 0;
+		} else
+			usage(strcmp(lu->id, "sit") == 0);
+		argc--, argv++;
+	}
+
+	if (ttl && pmtudisc == 0) {
+		fprintf(stderr, "ttl != 0 and noptmudisc are incompatible\n");
+		exit(-1);
+	}
+
+	addattr32(n, 1024, IFLA_IPTUN_LINK, link);
+	addattr32(n, 1024, IFLA_IPTUN_LOCAL, laddr);
+	addattr32(n, 1024, IFLA_IPTUN_REMOTE, raddr);
+	addattr8(n, 1024, IFLA_IPTUN_TTL, ttl);
+	addattr8(n, 1024, IFLA_IPTUN_TOS, tos);
+	addattr8(n, 1024, IFLA_IPTUN_PMTUDISC, pmtudisc);
+	if (strcmp(lu->id, "sit") == 0) {
+		addattr16(n, 1024, IFLA_IPTUN_FLAGS, iflags);
+		if (ip6rdprefixlen) {
+			addattr_l(n, 1024, IFLA_IPTUN_6RD_PREFIX,
+				  &ip6rdprefix, sizeof(ip6rdprefix));
+			addattr16(n, 1024, IFLA_IPTUN_6RD_PREFIXLEN,
+				  ip6rdprefixlen);
+			addattr32(n, 1024, IFLA_IPTUN_6RD_RELAY_PREFIX,
+				  ip6rdrelayprefix);
+			addattr16(n, 1024, IFLA_IPTUN_6RD_RELAY_PREFIXLEN,
+				  ip6rdrelayprefixlen);
+		}
+	}
+
+	return 0;
+}
+
+static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+	char s1[1024];
+	char s2[64];
+	const char *local = "any";
+	const char *remote = "any";
+
+	if (!tb)
+		return;
+
+	if (tb[IFLA_IPTUN_REMOTE]) {
+		unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_REMOTE]);
+
+		if (addr)
+			remote = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
+	}
+
+	fprintf(f, "remote %s ", remote);
+
+	if (tb[IFLA_IPTUN_LOCAL]) {
+		unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_LOCAL]);
+
+		if (addr)
+			local = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
+	}
+
+	fprintf(f, "local %s ", local);
+
+	if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) {
+		unsigned link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]);
+		const char *n = if_indextoname(link, s2);
+
+		if (n)
+			fprintf(f, "dev %s ", n);
+		else
+			fprintf(f, "dev %u ", link);
+	}
+
+	if (tb[IFLA_IPTUN_TTL] && rta_getattr_u8(tb[IFLA_IPTUN_TTL]))
+		fprintf(f, "ttl %d ", rta_getattr_u8(tb[IFLA_IPTUN_TTL]));
+	else
+		fprintf(f, "ttl inherit ");
+
+	if (tb[IFLA_IPTUN_TOS] && rta_getattr_u8(tb[IFLA_IPTUN_TOS])) {
+		int tos = rta_getattr_u8(tb[IFLA_IPTUN_TOS]);
+
+		fputs("tos ", f);
+		if (tos == 1)
+			fputs("inherit ", f);
+		else
+			fprintf(f, "0x%x ", tos);
+	}
+
+	if (tb[IFLA_IPTUN_PMTUDISC] && rta_getattr_u8(tb[IFLA_IPTUN_PMTUDISC]))
+		fprintf(f, "pmtudisc ");
+	else
+		fprintf(f, "nopmtudisc ");
+
+	if (tb[IFLA_IPTUN_FLAGS]) {
+		__u16 iflags = rta_getattr_u16(tb[IFLA_IPTUN_FLAGS]);
+
+		if (iflags & SIT_ISATAP)
+			fprintf(f, "isatap ");
+	}
+
+	if (tb[IFLA_IPTUN_6RD_PREFIXLEN] &&
+	    *(__u16 *)RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIXLEN])) {
+		__u16 prefixlen = rta_getattr_u16(tb[IFLA_IPTUN_6RD_PREFIXLEN]);
+		__u16 relayprefixlen =
+			rta_getattr_u16(tb[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]);
+		__u32 relayprefix =
+			rta_getattr_u32(tb[IFLA_IPTUN_6RD_RELAY_PREFIX]);
+
+		printf("6rd-prefix %s/%u ",
+		       inet_ntop(AF_INET6, RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIX]),
+				 s1, sizeof(s1)),
+		       prefixlen);
+		if (relayprefix) {
+			printf("6rd-relay_prefix %s/%u ",
+			       format_host(AF_INET, 4, &relayprefix, s1,
+					   sizeof(s1)),
+			       relayprefixlen);
+		}
+	}
+}
+
+struct link_util ipip_link_util = {
+	.id = "ipip",
+	.maxattr = IFLA_IPTUN_MAX,
+	.parse_opt = iptunnel_parse_opt,
+	.print_opt = iptunnel_print_opt,
+};
+
+struct link_util sit_link_util = {
+	.id = "sit",
+	.maxattr = IFLA_IPTUN_MAX,
+	.parse_opt = iptunnel_parse_opt,
+	.print_opt = iptunnel_print_opt,
+};
diff --git a/ip/link_vti.c b/ip/link_vti.c
index 385f435..77a7482 100644
--- a/ip/link_vti.c
+++ b/ip/link_vti.c
@@ -117,7 +117,7 @@
 			else {
 				if (get_unsigned(&uval, *argv, 0) < 0) {
 					fprintf(stderr,
-						"Invalid value for \"key\"\n");
+						"Invalid value for \"key\": \"%s\"; it should be an unsigned integer\n", *argv);
 					exit(-1);
 				}
 				uval = htonl(uval);
@@ -132,7 +132,7 @@
 				uval = get_addr32(*argv);
 			else {
 				if (get_unsigned(&uval, *argv, 0) < 0) {
-					fprintf(stderr, "invalid value of \"ikey\"\n");
+					fprintf(stderr, "invalid value for \"ikey\": \"%s\"; it should be an unsigned integer\n", *argv);
 					exit(-1);
 				}
 				uval = htonl(uval);
@@ -146,7 +146,7 @@
 				uval = get_addr32(*argv);
 			else {
 				if (get_unsigned(&uval, *argv, 0) < 0) {
-					fprintf(stderr, "invalid value of \"okey\"\n");
+					fprintf(stderr, "invalid value for \"okey\": \"%s\"; it should be an unsigned integer\n", *argv);
 					exit(-1);
 				}
 				uval = htonl(uval);
@@ -155,7 +155,7 @@
 		} else if (!matches(*argv, "remote")) {
 			NEXT_ARG();
 			if (!strcmp(*argv, "any")) {
-				fprintf(stderr, "invalid value of \"remote\"\n");
+				fprintf(stderr, "invalid value for \"remote\": \"%s\"\n", *argv);
 				exit(-1);
 			} else {
 				daddr = get_addr32(*argv);
@@ -163,7 +163,7 @@
 		} else if (!matches(*argv, "local")) {
 			NEXT_ARG();
 			if (!strcmp(*argv, "any")) {
-				fprintf(stderr, "invalid value of \"local\"\n");
+				fprintf(stderr, "invalid value for \"local\": \"%s\"\n", *argv);
 				exit(-1);
 			} else {
 				saddr = get_addr32(*argv);
diff --git a/ip/rtmon.c b/ip/rtmon.c
index c1416a0..9227eac 100644
--- a/ip/rtmon.c
+++ b/ip/rtmon.c
@@ -56,7 +56,7 @@
 	return 0;
 }
 
-void usage(void)
+static void usage(void)
 {
 	fprintf(stderr, "Usage: rtmon file FILE [ all | LISTofOBJECTS]\n");
 	fprintf(stderr, "LISTofOBJECTS := [ link ] [ address ] [ route ]\n");
diff --git a/ip/tcp_metrics.c b/ip/tcp_metrics.c
index 34e1d8e..a01e1fb 100644
--- a/ip/tcp_metrics.c
+++ b/ip/tcp_metrics.c
@@ -268,7 +268,7 @@
 	case AF_INET6:
 		break;
 	default:
-		fprintf(stderr, "Unsupported family:%d\n", preferred_family);
+		fprintf(stderr, "Unsupported protocol family: %d\n", preferred_family);
 		return -1;
 	}
 
diff --git a/ip/tunnel.c b/ip/tunnel.c
index b176d3f..a6a2846 100644
--- a/ip/tunnel.c
+++ b/ip/tunnel.c
@@ -74,7 +74,7 @@
 	fd = socket(preferred_family, SOCK_DGRAM, 0);
 	err = ioctl(fd, SIOCGETTUNNEL, &ifr);
 	if (err)
-		fprintf(stderr, "get tunnel %s failed: %s\n", basedev, 
+		fprintf(stderr, "get tunnel \"%s\" failed: %s\n", basedev,
 			strerror(errno));
 
 	close(fd);
@@ -95,7 +95,7 @@
 	fd = socket(preferred_family, SOCK_DGRAM, 0);
 	err = ioctl(fd, cmd, &ifr);
 	if (err)
-		fprintf(stderr, "add tunnel %s failed: %s\n", ifr.ifr_name,
+		fprintf(stderr, "add tunnel \"%s\" failed: %s\n", ifr.ifr_name,
 			strerror(errno));
 	close(fd);
 	return err;
@@ -116,7 +116,7 @@
 	fd = socket(preferred_family, SOCK_DGRAM, 0);
 	err = ioctl(fd, SIOCDELTUNNEL, &ifr);
 	if (err)
-		fprintf(stderr, "delete tunnel %s failed: %s\n",
+		fprintf(stderr, "delete tunnel \"%s\" failed: %s\n",
 			ifr.ifr_name, strerror(errno));
 	close(fd);
 	return err;
diff --git a/ip/xfrm_monitor.c b/ip/xfrm_monitor.c
index ea17987..bfc48f1 100644
--- a/ip/xfrm_monitor.c
+++ b/ip/xfrm_monitor.c
@@ -206,7 +206,7 @@
 	return 0;
 }
 
-void xfrm_ae_flags_print(__u32 flags, void *arg)
+static void xfrm_ae_flags_print(__u32 flags, void *arg)
 {
 	FILE *fp = (FILE*)arg;
 	fprintf(fp, " (0x%x) ", flags);
diff --git a/ip/xfrm_policy.c b/ip/xfrm_policy.c
index 8e3aec5..bf263e0 100644
--- a/ip/xfrm_policy.c
+++ b/ip/xfrm_policy.c
@@ -896,7 +896,7 @@
 	exit(0);
 }
 
-int print_spdinfo( struct nlmsghdr *n, void *arg)
+static int print_spdinfo( struct nlmsghdr *n, void *arg)
 {
 	FILE *fp = (FILE*)arg;
 	__u32 *f = NLMSG_DATA(n);
diff --git a/ip/xfrm_state.c b/ip/xfrm_state.c
index 0d98e78..35957bd 100644
--- a/ip/xfrm_state.c
+++ b/ip/xfrm_state.c
@@ -1053,7 +1053,7 @@
 	exit(0);
 }
 
-int print_sadinfo(struct nlmsghdr *n, void *arg)
+static int print_sadinfo(struct nlmsghdr *n, void *arg)
 {
 	FILE *fp = (FILE*)arg;
 	__u32 *f = NLMSG_DATA(n);
diff --git a/lib/Makefile b/lib/Makefile
index bfbe672..a42b885 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,3 +1,5 @@
+include ../Config
+
 CFLAGS += -fPIC
 
 UTILOBJ=utils.o rt_names.o ll_types.o ll_proto.o ll_addr.o inet_proto.o
diff --git a/lib/inet_proto.c b/lib/inet_proto.c
index a55e0e7..57a8351 100644
--- a/lib/inet_proto.c
+++ b/lib/inet_proto.c
@@ -20,9 +20,10 @@
 #include <netdb.h>
 #include <string.h>
 
+#include "rt_names.h"
 #include "utils.h"
 
-char *inet_proto_n2a(int proto, char *buf, int len)
+const char *inet_proto_n2a(int proto, char *buf, int len)
 {
 	static char ncache[16];
 	static int icache = -1;
@@ -42,7 +43,7 @@
 	return buf;
 }
 
-int inet_proto_a2n(char *buf)
+int inet_proto_a2n(const char *buf)
 {
 	static char ncache[16];
 	static int icache = -1;
diff --git a/lib/ipx_ntop.c b/lib/ipx_ntop.c
index 7b6d728..1e46bc2 100644
--- a/lib/ipx_ntop.c
+++ b/lib/ipx_ntop.c
@@ -1,5 +1,6 @@
 #include <errno.h>
 #include <sys/types.h>
+#include <sys/socket.h>
 #include <netinet/in.h>
 
 #include "utils.h"
diff --git a/lib/ipx_pton.c b/lib/ipx_pton.c
index 1a52b7f..3dca271 100644
--- a/lib/ipx_pton.c
+++ b/lib/ipx_pton.c
@@ -1,6 +1,7 @@
 #include <errno.h>
 #include <string.h>
 #include <sys/types.h>
+#include <sys/socket.h>
 #include <netinet/in.h>
 
 #include "utils.h"
diff --git a/lib/ll_addr.c b/lib/ll_addr.c
index f558050..c12ab07 100644
--- a/lib/ll_addr.c
+++ b/lib/ll_addr.c
@@ -57,7 +57,7 @@
 }
 
 /*NB: lladdr is char * (rather than u8 *) because sa_data is char * (1003.1g) */
-int ll_addr_a2n(char *lladdr, int len, char *arg)
+int ll_addr_a2n(char *lladdr, int len, const char *arg)
 {
 	if (strchr(arg, '.')) {
 		inet_prefix pfx;
diff --git a/lib/ll_proto.c b/lib/ll_proto.c
index 3337b14..3aa8252 100644
--- a/lib/ll_proto.c
+++ b/lib/ll_proto.c
@@ -100,10 +100,10 @@
         return buf;
 }
 
-int ll_proto_a2n(unsigned short *id, char *buf)
+int ll_proto_a2n(unsigned short *id, const char *buf)
 {
         int i;
-        for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
+        for (i=0; i < sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
                  if (strcasecmp(llproto_names[i].name, buf) == 0) {
 			 *id = htons(llproto_names[i].id);
 			 return 0;
diff --git a/lib/rt_names.c b/lib/rt_names.c
index 52ecdb2..02f1417 100644
--- a/lib/rt_names.c
+++ b/lib/rt_names.c
@@ -29,12 +29,12 @@
 
 struct rtnl_hash_entry {
 	struct rtnl_hash_entry *next;
-	char *			name;
+	const char *		name;
 	unsigned int		id;
 };
 
 static void
-rtnl_hash_initialize(char *file, struct rtnl_hash_entry **hash, int size)
+rtnl_hash_initialize(const char *file, struct rtnl_hash_entry **hash, int size)
 {
 	struct rtnl_hash_entry *entry;
 	char buf[512];
@@ -73,7 +73,7 @@
 	fclose(fp);
 }
 
-static void rtnl_tab_initialize(char *file, char **tab, int size)
+static void rtnl_tab_initialize(const char *file, char **tab, int size)
 {
 	char buf[512];
 	FILE *fp;
@@ -127,7 +127,6 @@
 };
 
 
-
 static int rtnl_rtprot_init;
 
 static void rtnl_rtprot_initialize(void)
@@ -137,10 +136,10 @@
 			    rtnl_rtprot_tab, 256);
 }
 
-char * rtnl_rtprot_n2a(int id, char *buf, int len)
+const char * rtnl_rtprot_n2a(int id, char *buf, int len)
 {
 	if (id<0 || id>=256) {
-		snprintf(buf, len, "%d", id);
+		snprintf(buf, len, "%u", id);
 		return buf;
 	}
 	if (!rtnl_rtprot_tab[id]) {
@@ -149,11 +148,11 @@
 	}
 	if (rtnl_rtprot_tab[id])
 		return rtnl_rtprot_tab[id];
-	snprintf(buf, len, "%d", id);
+	snprintf(buf, len, "%u", id);
 	return buf;
 }
 
-int rtnl_rtprot_a2n(__u32 *id, char *arg)
+int rtnl_rtprot_a2n(__u32 *id, const char *arg)
 {
 	static char *cache = NULL;
 	static unsigned long res;
@@ -186,7 +185,6 @@
 }
 
 
-
 static char * rtnl_rtscope_tab[256] = {
 	"global",
 };
@@ -204,7 +202,7 @@
 			    rtnl_rtscope_tab, 256);
 }
 
-char * rtnl_rtscope_n2a(int id, char *buf, int len)
+const char *rtnl_rtscope_n2a(int id, char *buf, int len)
 {
 	if (id<0 || id>=256) {
 		snprintf(buf, len, "%d", id);
@@ -220,9 +218,9 @@
 	return buf;
 }
 
-int rtnl_rtscope_a2n(__u32 *id, char *arg)
+int rtnl_rtscope_a2n(__u32 *id, const char *arg)
 {
-	static char *cache = NULL;
+	static const char *cache = NULL;
 	static unsigned long res;
 	char *end;
 	int i;
@@ -253,7 +251,6 @@
 }
 
 
-
 static char * rtnl_rtrealm_tab[256] = {
 	"unknown",
 };
@@ -267,7 +264,7 @@
 			    rtnl_rtrealm_tab, 256);
 }
 
-char * rtnl_rtrealm_n2a(int id, char *buf, int len)
+const char *rtnl_rtrealm_n2a(int id, char *buf, int len)
 {
 	if (id<0 || id>=256) {
 		snprintf(buf, len, "%d", id);
@@ -284,7 +281,7 @@
 }
 
 
-int rtnl_rtrealm_a2n(__u32 *id, char *arg)
+int rtnl_rtrealm_a2n(__u32 *id, const char *arg)
 {
 	static char *cache = NULL;
 	static unsigned long res;
@@ -336,7 +333,7 @@
 			     rtnl_rttable_hash, 256);
 }
 
-char * rtnl_rttable_n2a(__u32 id, char *buf, int len)
+const char * rtnl_rttable_n2a(__u32 id, char *buf, int len)
 {
 	struct rtnl_hash_entry *entry;
 
@@ -355,9 +352,9 @@
 	return buf;
 }
 
-int rtnl_rttable_a2n(__u32 *id, char *arg)
+int rtnl_rttable_a2n(__u32 *id, const char *arg)
 {
-	static char *cache = NULL;
+	static const char *cache = NULL;
 	static unsigned long res;
 	struct rtnl_hash_entry *entry;
 	char *end;
@@ -404,7 +401,7 @@
 			    rtnl_rtdsfield_tab, 256);
 }
 
-char * rtnl_dsfield_n2a(int id, char *buf, int len)
+const char *rtnl_dsfield_n2a(int id, char *buf, int len)
 {
 	if (id<0 || id>=256) {
 		snprintf(buf, len, "%d", id);
@@ -421,7 +418,7 @@
 }
 
 
-int rtnl_dsfield_a2n(__u32 *id, char *arg)
+int rtnl_dsfield_a2n(__u32 *id, const char *arg)
 {
 	static char *cache = NULL;
 	static unsigned long res;
@@ -469,9 +466,9 @@
 			     rtnl_group_hash, 256);
 }
 
-int rtnl_group_a2n(int *id, char *arg)
+int rtnl_group_a2n(int *id, const char *arg)
 {
-	static char *cache = NULL;
+	static const char *cache = NULL;
 	static unsigned long res;
 	struct rtnl_hash_entry *entry;
 	char *end;
diff --git a/lib/utils.c b/lib/utils.c
index 7ecaab3..5bcdbcf 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -15,6 +15,7 @@
 #include <unistd.h>
 #include <syslog.h>
 #include <fcntl.h>
+#include <limits.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <string.h>
@@ -22,6 +23,7 @@
 #include <arpa/inet.h>
 #include <asm/types.h>
 #include <linux/pkt_sched.h>
+#include <linux/param.h>
 #include <time.h>
 #include <sys/time.h>
 #include <errno.h>
diff --git a/man/Makefile b/man/Makefile
index 67fea05..749faa1 100644
--- a/man/Makefile
+++ b/man/Makefile
@@ -2,19 +2,13 @@
 INSTALLDIR=install -m 0755 -d
 INSTALLMAN=install -m 0644
 
-SUBDIRS = man3 man8
+SUBDIRS = man3 man7 man8
 
-all:
-	@for subdir in $(SUBDIRS); do $(MAKE) -C $$subdir; done
+all clean install:
+	@for subdir in $(SUBDIRS); do $(MAKE) -C $$subdir $@ || exit $$?; done
 
 distclean: clean
 
-clean:
-	@for subdir in $(SUBDIRS); do $(MAKE) -C $$subdir clean; done
-
-install:
-	@for subdir in $(SUBDIRS); do $(MAKE) -C $$subdir install; done
-
 .PHONY: install clean distclean
 
 .EXPORT_ALL_VARIABLES:
diff --git a/man/man3/libnetlink.3 b/man/man3/libnetlink.3
index 15a478a..e999bd6 100644
--- a/man/man3/libnetlink.3
+++ b/man/man3/libnetlink.3
@@ -100,7 +100,7 @@
 means to not use a filter.
 .B junk
 is used to filter messages not destined to the local socket.
-Only one message bundle is received. Unless there is no message 
+Only one message bundle is received. If there is a message
 pending, this function does not block.
 
 .TP
@@ -112,7 +112,7 @@
 and the
 .B jarg
 cookie as arguments. It will get called for all received messages.
-Only one message bundle is received. Unless there is no message 
+Only one message bundle is received. If there is a message
 pending this function does not block.
 
 .TP
@@ -123,7 +123,7 @@
 .B file
 and passes the messages to
 .B handler
-for parsing. The file contains raw data as received from a rtnetlink socket.
+for parsing. The file should contain raw data as received from a rtnetlink socket.
 .PP
 The following functions are useful to construct custom rtnetlink messages. For
 simple database dumping with filtering it is better to use the higher level
@@ -183,6 +183,8 @@
 with a variable length data value.
 
 .SH BUGS
+This library is meant for internal use, use libmnl for new programs.
+
 The functions sometimes use fprintf and exit when a fatal error occurs.
 This library should be named librtnetlink.
 
diff --git a/man/man7/Makefile b/man/man7/Makefile
new file mode 100644
index 0000000..ccfd839
--- /dev/null
+++ b/man/man7/Makefile
@@ -0,0 +1,13 @@
+MAN7PAGES = tc-hfsc.7
+
+all:
+
+distclean: clean
+
+clean:
+
+install:
+	$(INSTALLDIR) $(DESTDIR)$(MANDIR)/man7
+	$(INSTALLMAN) $(MAN7PAGES) $(DESTDIR)$(MANDIR)/man7
+
+.PHONY: install clean distclean
diff --git a/man/man7/tc-hfsc.7 b/man/man7/tc-hfsc.7
index d4e63f2..ca04961 100644
--- a/man/man7/tc-hfsc.7
+++ b/man/man7/tc-hfsc.7
@@ -4,13 +4,12 @@
 .
 .SH "HISTORY & INTRODUCTION"
 .
-HFSC \- \fBHierarchical Fair Service Curve\fR was first presented at
+HFSC (Hierarchical Fair Service Curve) is a network packet scheduling algorithm that was first presented at
 SIGCOMM'97. Developed as a part of ALTQ (ALTernative Queuing) on NetBSD, found
 its way quickly to other BSD systems, and then a few years ago became part of
 the linux kernel. Still, it's not the most popular scheduling algorithm \-
-especially if compared to HTB \- and it's not well documented from enduser's
-perspective. This introduction aims to explain how HFSC works without
-going to deep into math side of things (although some if it will be
+especially if compared to HTB \- and it's not well documented for the enduser. This introduction aims to explain how HFSC works without using
+too much math (although some math it will be
 inevitable).
 
 In short HFSC aims to:
@@ -30,10 +29,10 @@
 .
 The main "selling" point of HFSC is feature \fB(1)\fR, which is achieved by
 using nonlinear service curves (more about what it actually is later). This is
-particularly useful in VoIP or games, where not only guarantee of consistent
-bandwidth is important, but initial delay of a data stream as well. Note that
+particularly useful in VoIP or games, where not only a guarantee of consistent
+bandwidth is important, but also limiting the initial delay of a data stream. Note that
 it matters only for leaf classes (where the actual queues are) \- thus class
-hierarchy is ignored in realtime case.
+hierarchy is ignored in the realtime case.
 
 Feature \fB(2)\fR is well, obvious \- any algorithm featuring class hierarchy
 (such as HTB or CBQ) strives to achieve that. HFSC does that well, although
@@ -44,8 +43,8 @@
 situations where it's either not possible to guarantee service of all curves at
 the same time, and/or it's impossible to do so fairly. Both will be explained
 later. Note that this is mainly related to interior (aka aggregate) classes, as
-the leafs are already handled by \fB(1)\fR. Still \- it's perfectly possible to
-create a leaf class w/o realtime service, and in such case \- the caveats will
+the leafs are already handled by \fB(1)\fR. Still, it's perfectly possible to
+create a leaf class without realtime service, and in such a case the caveats will
 naturally extend to leaf classes as well.
 
 .SH ABBREVIATIONS
@@ -62,21 +61,22 @@
 .SH "BASICS OF HFSC"
 .
 To understand how HFSC works, we must first introduce a service curve.
-Overall, it's a nondecreasing function of some time unit, returning amount of
-service (allowed or allocated amount of bandwidth) by some specific point in
-time. The purpose of it should be subconsciously obvious \- if a class was
-allowed to transfer not less than the amount specified by its service curve \-
-then service curve is not violated.
+Overall, it's a nondecreasing function of some time unit, returning the amount
+of
+service (an allowed or allocated amount of bandwidth) at some specific point in
+time. The purpose of it should be subconsciously obvious: if a class was
+allowed to transfer not less than the amount specified by its service curve,
+then the service curve is not violated.
 
-Still \- we need more elaborate criterion than just the above (although in
-most generic case it can be reduced to it). The criterion has to take two
+Still, we need more elaborate criterion than just the above (although in
+the most generic case it can be reduced to it). The criterion has to take two
 things into account:
 .
 .RS 4
 .IP \(bu 4
 idling periods
 .IP \(bu
-ability to "look back", so if during current active period service curve is violated, maybe it
+the ability to "look back", so if during current active period the service curve is violated, maybe it
 isn't if we count excess bandwidth received during earlier active period(s)
 .RE
 .PP
@@ -102,9 +102,9 @@
 .RE
 .
 .PP
-Consider \fB(a)\fR \- if the service received during both periods meets
-\fB(1)\fR, then all is good. But what if it doesn't do so during the 2nd
-period ? If the amount of service received during the 1st period is bigger
+Consider \fB(a)\fR: if the service received during both periods meets
+\fB(1)\fR, then all is well. But what if it doesn't do so during the 2nd
+period? If the amount of service received during the 1st period is larger
 than the service curve, then it might compensate for smaller service during
 the 2nd period \fIand\fR the gap \- if the gap is small enough.
 
@@ -172,42 +172,43 @@
 .SH "REALTIME CRITERION"
 .
 RT criterion \fIignores class hierarchy\fR and guarantees precise bandwidth and
-delay allocation. We say that packet is eligible for sending, when current real
-time is bigger than eligible time. From all packets eligible, the one most
-suited for sending, is the one with the smallest deadline time. Sounds simply,
-but consider following example:
+delay allocation. We say that a packet is eligible for sending, when the
+current real
+time is later than the eligible time of the packet. From all eligible packets, the one most
+suited for sending is the one with the shortest deadline time. This sounds
+simple, but consider the following example:
 
-Interface 10mbit, two classes, both with two\-piece linear service curves:
+Interface 10Mbit, two classes, both with two\-piece linear service curves:
 .RS 4
 .IP \(bu 4
-1st class \- 2mbit for 100ms, then 7mbit (convex \- 1st slope < 2nd slope)
+1st class \- 2Mbit for 100ms, then 7Mbit (convex \- 1st slope < 2nd slope)
 .IP \(bu
-2nd class \- 7mbit for 100ms, then 2mbit (concave \- 1st slope > 2nd slope)
+2nd class \- 7Mbit for 100ms, then 2Mbit (concave \- 1st slope > 2nd slope)
 .RE
 .PP
 Assume for a moment, that we only use D() for both finding eligible packets,
 and choosing the most fitting one, thus eligible time would be computed as
 D^(\-1)(w) and deadline time would be computed as D^(\-1)(w+l). If the 2nd
 class starts sending packets 1 second after the 1st class, it's of course
-impossible to guarantee 14mbit, as the interface capability is only 10mbit.
+impossible to guarantee 14Mbit, as the interface capability is only 10Mbit.
 The only workaround in this scenario is to allow the 1st class to send the
 packets earlier that would normally be allowed. That's where separate E() comes
 to help. Putting all the math aside (see HFSC paper for details), E() for RT
 concave service curve is just like D(), but for the RT convex service curve \-
 it's constructed using \fIonly\fR RT service curve's 2nd slope (in our example
-\- 7mbit).
+ 7Mbit).
 
 The effect of such E() \- packets will be sent earlier, and at the same time
-D() \fIwill\fR be updated \- so current deadline time calculated from it will
-be bigger. Thus, when the 2nd class starts sending packets later, both the 1st
-and the 2nd class will be eligible, but the 2nd session's deadline time will be
-smaller and its packets will be sent first. When the 1st class becomes idle at
-some later point, the 2nd class will be able to "buffer" up again for later
-active period of the 1st class.
+D() \fIwill\fR be updated \- so the current deadline time calculated from it
+will be later. Thus, when the 2nd class starts sending packets later, both
+the 1st and the 2nd class will be eligible, but the 2nd session's deadline
+time will be smaller and its packets will be sent first. When the 1st class
+becomes idle at some later point, the 2nd class will be able to "buffer" up
+again for later active period of the 1st class.
 
 A short remark \- in a situation, where the total amount of bandwidth
-available on the interface is bigger than the allocated total realtime parts
-(imagine interface 10 mbit, but 1mbit/2mbit and 2mbit/1mbit classes), the sole
+available on the interface is larger than the allocated total realtime parts
+(imagine a 10 Mbit interface, but 1Mbit/2Mbit and 2Mbit/1Mbit classes), the sole
 speed of the interface could suffice to guarantee the times.
 
 Important part of RT criterion is that apart from updating its D() and E(),
@@ -233,18 +234,18 @@
 of virtual times of all active subclasses \- the one with the smallest vt wins
 and gets scheduled. One immediate conclusion from this fact is that absolute
 values don't matter \- only ratios between them (so for example, two children
-classes with simple linear 1mbit service curves will get the same treatment
-from LS criterion's perspective, as if they were 5mbit). The other conclusion
+classes with simple linear 1Mbit service curves will get the same treatment
+from LS criterion's perspective, as if they were 5Mbit). The other conclusion
 is, that in perfectly fluid system with linear curves, all virtual times across
 whole class hierarchy would be equal.
 
-Why is VC defined in term of virtual time (and what is it) ?
+Why is VC defined in term of virtual time (and what is it)?
 
 Imagine an example: class A with two children \- A1 and A2, both with let's say
-10mbit SCs. If A2 is idle, A1 receives all the bandwidth of A (and update its
+10Mbit SCs. If A2 is idle, A1 receives all the bandwidth of A (and update its
 V() in the process). When A2 becomes active, A1's virtual time is already
-\fIfar\fR bigger than A2's one. Considering the type of decision made by LS
-criterion, A1 would become idle for a lot of time. We can workaround this
+\fIfar\fR later than A2's one. Considering the type of decision made by LS
+criterion, A1 would become idle for a long time. We can workaround this
 situation by adjusting virtual time of the class becoming active \- we do that
 by getting such time "up to date". HFSC uses a mean of the smallest and the
 biggest virtual time of currently active children fit for sending. As it's not
@@ -259,20 +260,20 @@
 during certain time periods:
 
 .RS 4
-Recall the example from RT section, slightly modified (with 3mbit slopes
-instead of 2mbit ones):
+Recall the example from RT section, slightly modified (with 3Mbit slopes
+instead of 2Mbit ones):
 
 .IP \(bu 4
-1st class \- 3mbit for 100ms, then 7mbit (convex \- 1st slope < 2nd slope)
+1st class \- 3Mbit for 100ms, then 7Mbit (convex \- 1st slope < 2nd slope)
 .IP \(bu
-2nd class \- 7mbit for 100ms, then 3mbit (concave \- 1st slope > 2nd slope)
+2nd class \- 7Mbit for 100ms, then 3Mbit (concave \- 1st slope > 2nd slope)
 
 .PP
-They sum up nicely to 10mbit \- interface's capacity. But if we wanted to only
+They sum up nicely to 10Mbit \- the interface's capacity. But if we wanted to only
 use LS for guarantees and fairness \- it simply won't work. In LS context,
 only V() is used for making decision which class to schedule. If the 2nd class
 becomes active when the 1st one is in its second slope, the fairness will be
-preserved \- ratio will be 1:1 (7mbit:7mbit), but LS itself is of course
+preserved \- ratio will be 1:1 (7Mbit:7Mbit), but LS itself is of course
 unable to guarantee the absolute values themselves \- as it would have to go
 beyond of what the interface is capable of.
 .RE
@@ -287,28 +288,28 @@
 subtrees, arbitrated by their common (root here) parent:
 
 .nf
-R (root) -\ 10mbit
+R (root) -\ 10Mbit
 
-A  \- 7mbit, then 3mbit
-A1 \- 5mbit, then 2mbit
-A2 \- 2mbit, then 1mbit
+A  \- 7Mbit, then 3Mbit
+A1 \- 5Mbit, then 2Mbit
+A2 \- 2Mbit, then 1Mbit
 
-B  \- 3mbit, then 7mbit
+B  \- 3Mbit, then 7Mbit
 .fi
 
 R arbitrates between left subtree (A) and right (B). Assume that A2 and B are
 constantly backlogged, and at some later point A1 becomes backlogged (when all
 other classes are in their 2nd linear part).
 
-What happens now ? B (choice made by R) will \fIalways\fR get 7 mbit as R is
+What happens now? B (choice made by R) will \fIalways\fR get 7 Mbit as R is
 only (obviously) concerned with the ratio between its direct children. Thus A
-subtree gets 3mbit, but its children would want (at the point when A1 became
-backlogged) 5mbit + 1mbit. That's of course impossible, as they can only get
-3mbit due to interface limitation.
+subtree gets 3Mbit, but its children would want (at the point when A1 became
+backlogged) 5Mbit + 1Mbit. That's of course impossible, as they can only get
+3Mbit due to interface limitation.
 
 In the left subtree \- we have the same situation as previously (fair split
 between A1 and A2, but violated guarantees), but in the whole tree \- there's
-no fairness (B got 7mbit, but A1 and A2 have to fit together in 3mbit) and
+no fairness (B got 7Mbit, but A1 and A2 have to fit together in 3Mbit) and
 there's no guarantees for all classes (only B got what it wanted). Even if we
 violated fairness in the A subtree and set A2's service curve to 0, A1 would
 still not get the required bandwidth.
@@ -317,83 +318,83 @@
 .SH "UPPERLIMIT CRITERION"
 .
 UL criterion is an extensions to LS one, that permits sending packets only
-if current real time is bigger than fit\-time ('ft'). So the modified LS
+if current real time is later than fit\-time ('ft'). So the modified LS
 criterion becomes: choose the smallest virtual time from all active children,
 such that fit\-time < current real time also holds. Fit\-time is calculated
 from F(), which is based on UL service curve. As you can see, its role is
 kinda similar to E() used in RT criterion. Also, for obvious reasons \- you
 can't specify UL service curve without LS one.
 
-Main purpose of UL service curve is to limit HFSC to bandwidth available on the
+The main purpose of the UL service curve is to limit HFSC to bandwidth available on the
 upstream router (think adsl home modem/router, and linux server as
-nat/firewall/etc. with 100mbit+ connection to mentioned modem/router).
+NAT/firewall/etc. with 100Mbit+ connection to mentioned modem/router).
 Typically, it's used to create a single class directly under root, setting
-linear UL service curve to available bandwidth \- and then creating your class
-structure from that class downwards. Of course, you're free to add UL service
-(linear or not) curve to any class with LS criterion.
+a linear UL service curve to available bandwidth \- and then creating your class
+structure from that class downwards. Of course, you're free to add a UL service
+curve (linear or not) to any class with LS criterion.
 
-Important part about UL service curve is, that whenever at some point in time
+An important part about the UL service curve is that whenever at some point in time
 a class doesn't qualify for linksharing due to its fit\-time, the next time it
-does qualify, it will update its virtual time to the smallest virtual time of
-all active children fit for linksharing. This way, one of the main things LS
+does qualify it will update its virtual time to the smallest virtual time of
+all active children fit for linksharing. This way, one of the main things the LS
 criterion tries to achieve \- equality of all virtual times across whole
 hierarchy \- is preserved (in perfectly fluid system with only linear curves,
 all virtual times would be equal).
 
 Without that, 'vt' would lag behind other virtual times, and could cause
-problems. Consider interface with capacity 10mbit, and following leaf classes
+problems. Consider an interface with a capacity of 10Mbit, and the following leaf classes
 (just in case you're skipping this text quickly \- this example shows behavior
 that \f(BIdoesn't happen\fR):
 
 .nf
-A \- ls 5.0mbit
-B \- ls 2.5mbit
-C \- ls 2.5mbit, ul 2.5mbit
+A \- ls 5.0Mbit
+B \- ls 2.5Mbit
+C \- ls 2.5Mbit, ul 2.5Mbit
 .fi
 
-If B was idle, while A and C were constantly backlogged, they would normally
+If B was idle, while A and C were constantly backlogged, A and C would normally
 (as far as LS criterion is concerned) divide bandwidth in 2:1 ratio. But due
-to UL service curve in place, C would get at most 2.5mbit, and A would get the
-remaining 7.5mbit. The longer the backlogged period, the more virtual times of
+to UL service curve in place, C would get at most 2.5Mbit, and A would get the
+remaining 7.5Mbit. The longer the backlogged period, the more the virtual times of
 A and C would drift apart. If B became backlogged at some later point in time,
 its virtual time would be set to (A's\~vt\~+\~C's\~vt)/2, thus blocking A from
-sending any traffic, until B's virtual time catches up with A.
+sending any traffic until B's virtual time catches up with A.
 .
 .SH "SEPARATE LS / RT SCs"
 .
-Another difference from original HFSC paper, is that RT and LS SCs can be
-specified separately. Moreover \- leaf classes are allowed to have only either
-RT SC or LS SC. For interior classes, only LS SCs make sense \- Any RT SC will
+Another difference from the original HFSC paper is that RT and LS SCs can be
+specified separately. Moreover, leaf classes are allowed to have only either
+RT SC or LS SC. For interior classes, only LS SCs make sense: any RT SC will
 be ignored.
 .
 .SH "CORNER CASES"
 .
-Separate service curves for LS and RT criteria can lead to certain traps,
+Separate service curves for LS and RT criteria can lead to certain traps
 that come from "fighting" between ideal linksharing and enforced realtime
 guarantees. Those situations didn't exist in original HFSC paper, where
 specifying separate LS / RT service curves was not discussed.
 
-Consider interface with capacity 10mbit, with following leaf classes:
+Consider an interface with a 10Mbit capacity, with the following leaf classes:
 
 .nf
-A \- ls 5.0mbit, rt 8mbit
-B \- ls 2.5mbit
-C \- ls 2.5mbit
+A \- ls 5.0Mbit, rt 8Mbit
+B \- ls 2.5Mbit
+C \- ls 2.5Mbit
 .fi
 
 Imagine A and C are constantly backlogged. As B is idle, A and C would divide
 bandwidth in 2:1 ratio, considering LS service curve (so in theory \- 6.66 and
-3.33). Alas RT criterion takes priority, so A will get 8mbit and LS will be
-able to compensate class C for only 2 mbit \- this will cause discrepancy
+3.33). Alas RT criterion takes priority, so A will get 8Mbit and LS will be
+able to compensate class C for only 2 Mbit \- this will cause discrepancy
 between virtual times of A and C.
 
-Assume this situation lasts for a lot of time with no idle periods, and
+Assume this situation lasts for a long time with no idle periods, and
 suddenly B becomes active. B's virtual time will be updated to
 (A's\~vt\~+\~C's\~vt)/2, effectively landing in the middle between A's and C's
 virtual time. The effect \- B, having no RT guarantees, will be punished and
 will not be allowed to transfer until C's virtual time catches up.
 
-If the interface had higher capacity \- for example 100mbit, this example
+If the interface had a higher capacity, for example 100Mbit, this example
 would behave perfectly fine though.
 
 Let's look a bit closer at the above example \- it "cleverly" invalidates one
@@ -401,8 +402,8 @@
 times across class hierarchy. Leaf classes without RT service curves are
 literally left to their own fate (governed by messed up virtual times).
 
-Also - it doesn't make much sense. Class A will always be guaranteed up to
-8mbit, and this is more than any absolute bandwidth that could happen from its
+Also, it doesn't make much sense. Class A will always be guaranteed up to
+8Mbit, and this is more than any absolute bandwidth that could happen from its
 LS criterion (excluding trivial case of only A being active). If the bandwidth
 taken by A is smaller than absolute value from LS criterion, the unused part
 will be automatically assigned to other active classes (as A has idling periods
@@ -411,7 +412,7 @@
 if extra speed is needed (e.g. due to latency), non linear service curves
 should be used in such case.
 
-In the other words - LS criterion is meaningless in the above example.
+In the other words: the LS criterion is meaningless in the above example.
 
 You can quickly "workaround" it by making sure each leaf class has RT service
 curve assigned (thus guaranteeing all of them will get some bandwidth), but it
@@ -422,13 +423,13 @@
 "overusing" RT curve a bit:
 
 .nf
-A \- ls 5.0mbit, rt 9mbit/30ms, then 1mbit
-B \- ls 2.5mbit
-C \- ls 2.5mbit
+A \- ls 5.0Mbit, rt 9Mbit/30ms, then 1Mbit
+B \- ls 2.5Mbit
+C \- ls 2.5Mbit
 .fi
 
 Here, the vt of A will "spike" in the initial period, but then A will never get more
-than 1mbit, until B & C catch up. Then everything will be back to normal.
+than 1Mbit until B & C catch up. Then everything will be back to normal.
 .
 .SH "LINUX AND TIMER RESOLUTION"
 .
@@ -457,43 +458,43 @@
 
 This is important to keep those settings in mind, as in scenario like: no
 tickless, no HR timers, frequency set to 100hz \- throttling accuracy would be
-at 10ms. It doesn't automatically mean you would be limited to ~0.8mbit/s
+at 10ms. It doesn't automatically mean you would be limited to ~0.8Mbit/s
 (assuming packets at ~1KB) \- as long as your queues are prepared to cover for
-timer inaccuracy. Of course, in case of e.g. locally generated udp traffic \-
+timer inaccuracy. Of course, in case of e.g. locally generated UDP traffic \-
 appropriate socket size is needed as well. Short example to make it more
 understandable (assume hardcore anti\-schedule settings \- HZ=100, no HR
 timers, no tickless):
 
 .nf
 tc qdisc add dev eth0 root handle 1:0 hfsc default 1
-tc class add dev eth0 parent 1:0 classid 1:1 hfsc rt m2 10mbit
+tc class add dev eth0 parent 1:0 classid 1:1 hfsc rt m2 10Mbit
 .fi
 
-Assuming packet of ~1KB size and HZ=100, that averages to ~0.8mbit \- anything
-beyond it (e.g. the above example with specified rate over 10x bigger) will
+Assuming packet of ~1KB size and HZ=100, that averages to ~0.8Mbit \- anything
+beyond it (e.g. the above example with specified rate over 10x larger) will
 require appropriate queuing and cause bursts every ~10 ms. As you can
 imagine, any HFSC's RT guarantees will be seriously invalidated by that.
 Aforementioned example is mainly important if you deal with old hardware \- as
-it's particularly popular for home server chores. Even then, you can easily
+is particularly popular for home server chores. Even then, you can easily
 set HZ=1000 and have very accurate scheduling for typical adsl speeds.
 
 Anything modern (apic or even hpet msi based timers + \&'tickless system')
-will provide enough accuracy for superb 1gbit scheduling. For example, on one
-of basically cheap dual core AMD boards I have with following settings:
+will provide enough accuracy for superb 1Gbit scheduling. For example, on one
+of my cheap dual-core AMD boards I have the following settings:
 
 .nf
 tc qdisc add dev eth0 parent root handle 1:0 hfsc default 1
-tc class add dev eth0 paretn 1:0 classid 1:1 hfsc rt m2 300mbit
+tc class add dev eth0 parent 1:0 classid 1:1 hfsc rt m2 300mbit
 .fi
 
-And simple:
+And a simple:
 
 .nf
 nc \-u dst.host.com 54321 </dev/zero
 nc \-l \-p 54321 >/dev/null
 .fi
 
-\&...will yield following effects over period of ~10 seconds (taken from
+\&...will yield the following effects over a period of ~10 seconds (taken from
 /proc/interrupts):
 
 .nf
@@ -502,16 +503,16 @@
 .fi
 
 That's roughly 31000/s. Now compare it with HZ=1000 setting. The obvious
-drawback of it is that cpu load can be rather extensive with servicing that
-many timer interrupts. Example with 300mbit RT service curve on 1gbit link is
+drawback of it is that cpu load can be rather high with servicing that
+many timer interrupts. The example with 300Mbit RT service curve on 1Gbit link is
 particularly ugly, as it requires a lot of throttling with minuscule delays.
 
-Also note that it's just an example showing capability of current hardware.
-The above example (essentially 300mbit TBF emulator) is pointless on internal
-interface to begin with \- you will pretty much always want regular LS service
-curve there, and in such scenario HFSC simply doesn't throttle at all.
+Also note that it's just an example showing the capabilities of current hardware.
+The above example (essentially a 300Mbit TBF emulator) is pointless on an internal
+interface to begin with: you will pretty much always want a regular LS service
+curve there, and in such a scenario HFSC simply doesn't throttle at all.
 
-300mbit RT service curve (selected columns from mpstat \-P ALL 1):
+300Mbit RT service curve (selected columns from mpstat \-P ALL 1):
 
 .nf
 10:56:43 PM  CPU  %sys     %irq   %soft   %idle
@@ -520,28 +521,28 @@
 10:56:44 PM    1   4.95   12.87    6.93   73.27
 .fi
 
-So, in rare case you need those speeds with only RT service curve, or with UL
-service curve \- remember about drawbacks.
+So, in the rare case you need those speeds with only a RT service curve, or with a UL
+service curve: remember the drawbacks.
 .
 .SH "CAVEAT: RANDOM ONLINE EXAMPLES"
 .
 For reasons unknown (though well guessed), many examples you can google love to
 overuse UL criterion and stuff it in every node possible. This makes no sense
 and works against what HFSC tries to do (and does pretty damn well). Use UL
-where it makes sense - on the uppermost node to match upstream router's uplink
-capacity. Or - in special cases, such as testing (limit certain subtree to some
-speed) or customers that must never get more than certain speed. In the last
-case you can usually achieve the same by just using RT criterion without LS+UL
+where it makes sense: on the uppermost node to match upstream router's uplink
+capacity. Or in special cases, such as testing (limit certain subtree to some
+speed), or customers that must never get more than certain speed. In the last
+case you can usually achieve the same by just using a RT criterion without LS+UL
 on leaf nodes.
 
-As for router case - remember it's good to differentiate between "traffic to
+As for the router case - remember it's good to differentiate between "traffic to
 router" (remote console, web config, etc.) and "outgoing traffic", so for
 example:
 
 .nf
 tc qdisc add dev eth0 root handle 1:0 hfsc default 0x8002
-tc class add dev eth0 parent 1:0 classid 1:999 hfsc rt m2 50mbit
-tc class add dev eth0 parent 1:0 classid 1:1 hfsc ls m2 2mbit ul m2 2mbit
+tc class add dev eth0 parent 1:0 classid 1:999 hfsc rt m2 50Mbit
+tc class add dev eth0 parent 1:0 classid 1:1 hfsc ls m2 2Mbit ul m2 2Mbit
 .fi
 
 \&... so "internet" tree under 1:1 and "router itself" as 1:999
diff --git a/man/man8/Makefile b/man/man8/Makefile
index 4bad9d6..d208f3b 100644
--- a/man/man8/Makefile
+++ b/man/man8/Makefile
@@ -9,7 +9,7 @@
 	ip-addrlabel.8 ip-l2tp.8 \
 	ip-maddress.8 ip-monitor.8 ip-mroute.8 ip-neighbour.8 \
 	ip-netns.8 ip-ntable.8 ip-rule.8 ip-tunnel.8 ip-xfrm.8 \
-	ip-tcp_metrics.8
+	ip-tcp_metrics.8 ip-netconf.8
 
 all: $(TARGETS)
 
diff --git a/man/man8/arpd.8 b/man/man8/arpd.8
index a14044b..6b9a43a 100644
--- a/man/man8/arpd.8
+++ b/man/man8/arpd.8
@@ -4,12 +4,12 @@
 arpd \- userspace arp daemon.
 
 .SH SYNOPSIS
-Usage: arpd [ -lkh? ] [ -a N ] [ -b dbase ] [ -B number ] [ -f file ] [-p interval ] [ -n time ] [ -R rate ] [ interfaces ]
+Usage: arpd [ -lkh? ] [ -a N ] [ -b dbase ] [ -B number ] [ -f file ] [-p interval ] [ -n time ] [ -R rate ] [ <INTERFACES> ]
 
 .SH DESCRIPTION
 The
 .B arpd
-daemon collects gratuitous ARP information, saving it on local disk and feeding it to kernel on demand to avoid redundant broadcasting due to limited size of kernel ARP cache.
+daemon collects gratuitous ARP information, saving it on local disk and feeding it to the kernel on demand to avoid redundant broadcasting due to limited size of the kernel ARP cache.
 
 .SH OPTIONS
 .TP
@@ -17,41 +17,41 @@
 Print help
 .TP
 -l
-Dump arpd database to stdout and exit. Output consists of three columns: interface index, IP address and MAC address. Negative entries for dead hosts are also shown, in this case MAC address is replaced by word FAILED followed by colon and time when the fact that host is dead was proven the last time.
+Dump the arpd database to stdout and exit. The output consists of three columns: the interface index, the IP address of the interface, and the MAC address of the interface. Negative entries for dead hosts are also shown, in this case the MAC address is replaced by the word FAILED followed by a colon and the most recent time when the fact that the host is dead was proven.
 .TP
 -f <FILE>
-Read and load arpd database from FILE in text format similar dumped by option -l. Exit after load, probably listing resulting database, if option -l is also given. If FILE is -, stdin is read to get ARP table.
+Read and load an arpd database from FILE in a text format similar to that dumped by option -l. Exit after load, possibly listing resulting database, if option -l is also given. If FILE is -, stdin is read to get the ARP table.
 .TP
 -b <DATABASE>
-location of database file. Default location is /var/lib/arpd/arpd.db
+the location of the database file. The default location is /var/lib/arpd/arpd.db
 .TP
 -a <NUMBER>
-arpd not only passively listens ARP on wire, but also send brodcast queries itself. NUMBER is number of such queries to make before destination is considered as dead. When arpd is started as kernel helper (i.e. with app_solicit enabled in sysctl or even with option -k) without this option and still did not learn enough information, you can observe 1 second gaps in service. Not fatal, but not good.
+With this option, arpd not only passively listens for ARP packets on the interface, but also sends brodcast queries itself. NUMBER is the number of such queries to make before a destination is considered dead. When arpd is started as kernel helper (i.e. with app_solicit enabled in sysctl or even with option -k) without this option and still did not learn enough information, you can observe 1 second gaps in service. Not fatal, but not good.
 .TP
 -k
-Suppress sending broadcast queries by kernel. It takes sense together with option -a.
+Suppress sending broadcast queries by the kernel. This option only makes sense together with option -a.
 .TP
 -n <TIME>
-Timeout of negative cache. When resolution fails arpd suppresses further attempts to resolve for this period. It makes sense only together with option -k This timeout should not be too much longer than boot time of a typical host not supporting gratuitous ARP. Default value is 60 seconds.
+Specifies the timeout of the negative cache. When resolution fails, arpd suppresses further attempts to resolve for this period. This option only makes sense together with option '-k'. This timeout should not be too much longer than the boot time of a typical host not supporting gratuitous ARP. Default value is 60 seconds.
 .TP
 -p <TIME>
-Time to wait in seconds between polling attempts to the kernel ARP table. TIME may be a floating point number.  The default value is 30.
+The time to wait in seconds between polling attempts to the kernel ARP table. TIME may be a floating point number.  The default value is 30.
 .TP
 -R <RATE>
 Maximal steady rate of broadcasts sent by arpd in packets per second. Default value is 1.
 .TP
 -B <NUMBER>
-Number of broadcasts sent by <tt/arpd/ back to back. Default value is 3. Together with option <tt/-R/ this option allows to police broadcasting not to exceed B+R*T over any interval of time T.
+The number of broadcasts sent by arpd back to back. Default value is 3. Together with the -R option, this option ensures that the number of ARP queries that are broadcast does not exceed B+R*T over any interval of time T.
 .P
-<INTERFACE> is the name of networking interface to watch. If no interfaces given, arpd monitors all the interfaces. In this case arpd does not adjust sysctl parameters, it is supposed user does this himself after arpd is started.
+<INTERFACES> is a list of names of networking interfaces to watch. If no interfaces are given, arpd monitors all the interfaces. In this case arpd does not adjust sysctl parameters, it is assumed that the user does this himself after arpd is started.
 .P
-Signals
-.br
-arpd exits gracefully syncing database and restoring adjusted sysctl parameters, when receives SIGINT or SIGTERM. SIGHUP syncs database to disk. SIGUSR1 sends some statistics to syslog. Effect of another signals is undefined, they may corrupt database and leave sysctl praameters in an unpredictable state.
+.SH SIGNALS
+.TP
+When arpd receives a SIGINT or SIGTERM signal, it exits gracefully, syncing the database and restoring adjusted sysctl parameters. On a SIGHUP it syncs the database to disk. With SIGUSR1 it sends some statistics to syslog. The effect of any other signals is undefined. In particular, they may corrupt the database and leave the sysctl parameters in an unpredictable state.
 .P
-Note
-.br
-In order for arpd to be able to serve as ARP resolver, kernel must be compiled with the option CONFIG_ARPD and, in the case when interface list in not given on command line, variable app_solicit on interfaces of interest should be in /proc/sys/net/ipv4/neigh/*. If this is not made arpd still collects gratuitous ARP information in its database.
+.SH NOTE
+.TP
+In order for arpd to be able to serve as ARP resolver, the kernel must be compiled with the option CONFIG_ARPD and, in the case when interface list in not given on command line, variable app_solicit on interfaces of interest should be in /proc/sys/net/ipv4/neigh/*. If this is not made arpd still collects gratuitous ARP information in its database.
 .SH EXAMPLES
 .TP
 arpd -b /var/tmp/arpd.db
@@ -64,6 +64,6 @@
 Enable kernel helper, leaving leading role to kernel.
 .TP
 arpd -b /var/tmp/arpd.db -a 3 -k eth0 eth1
-Completely replace kernel resolution on interfaces eth0 and eth1. In this case kernel still does unicast probing to validate entries, but all the broadcast activity is suppressed and made under authority of arpd.
+Completely replace kernel resolution on interfaces eth0 and eth1. In this case the kernel still does unicast probing to validate entries, but all the broadcast activity is suppressed and made under authority of arpd.
 .PP
-This is mode which arpd is supposed to work normally. It is not default just to prevent occasional enabling of too aggressive mode occasionally.
+This is the mode in which arpd normally is supposed to work. It is not the default to prevent occasional enabling of too aggressive a mode.
diff --git a/man/man8/bridge.8 b/man/man8/bridge.8
index 5ce8219..fd91618 100644
--- a/man/man8/bridge.8
+++ b/man/man8/bridge.8
@@ -47,8 +47,8 @@
 
 .TP
 .BR "\-s" , " \-stats", " \-statistics"
-output more information.  If the option
-appears twice or more, the amount of information increases.
+output more information.  If this option
+is given multiple times, the amount of information increases.
 As a rule, the information is statistics or some time values.
 
 
@@ -135,7 +135,7 @@
 
 .SS bridge fdb show - list forwarding entries.
 
-This commands displays current forwarding table.
+This command displays the current forwarding table.
 
 .PP
 With the
@@ -154,7 +154,7 @@
 command is the first in the command line and then the object list follows:
 
 .BR "bridge monitor" " [ " all " |"
-.IR LISTofOBJECTS " ]"
+.IR OBJECT-LIST " ]"
 
 .I OBJECT-LIST
 is the list of object types that we want to monitor.
@@ -186,7 +186,7 @@
 
 .SH SEE ALSO
 .BR ip (8)
-.br
+.SH BUGS
 .RB "Please direct bugreports and patches to: " <netdev@vger.kernel.org>
 
 .SH AUTHOR
diff --git a/man/man8/ip-addrlabel.8 b/man/man8/ip-addrlabel.8
index 3252bd2..fefc3ef 100644
--- a/man/man8/ip-addrlabel.8
+++ b/man/man8/ip-addrlabel.8
@@ -34,12 +34,12 @@
 .BR "ip addrlabel" " { " list " | " flush " }"
 
 .SH "DESCRIPTION"
-IPv6 address label is used for address selection
-described in RFC 3484.  Precedence is managed by userspace,
-and only label is stored in kernel.
+IPv6 address labels are used for address selection;
+they are described in RFC 3484.  Precedence is managed by userspace,
+and only the label itself is stored in the kernel.
 
 .SS ip addrlabel add - add an address label
-the command adds an address label entry to the kernel.
+add an address label entry to the kernel.
 .TP
 .BI prefix " PREFIX"
 .TP
@@ -50,15 +50,15 @@
 the label for the prefix.
 0xffffffff is reserved.
 .SS ip addrlabel del - delete an address label
-the command deletes an address label entry in the kernel.
+delete an address label entry from the kernel.
 .B Arguments:
 coincide with the arguments of
 .B ip addrlabel add
-but label is not required.
+but the label is not required.
 .SS ip addrlabel list - list address labels
-the command show contents of address labels.
+list the current address label entries in the kernel.
 .SS ip addrlabel flush - flush address labels
-the command flushes the contents of address labels and it does not restore default settings.
+flush all address labels in the kernel. This does not restore any default settings.
 
 .SH SEE ALSO
 .br
diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in
index 43c4ac6..8d2a6f9 100644
--- a/man/man8/ip-link.8.in
+++ b/man/man8/ip-link.8.in
@@ -1,4 +1,4 @@
-.TH IP\-LINK 8 "20 Dec 2011" "iproute2" "Linux"
+.TH IP\-LINK 8 "13 Dec 2012" "iproute2" "Linux"
 .SH "NAME"
 ip-link \- network device configuration
 .SH "SYNOPSIS"
@@ -59,7 +59,10 @@
 .BR vcan " | "
 .BR veth " | "
 .BR vlan " | "
-.BR vxlan " ]"
+.BR vxlan " |"
+.BR ip6tnl " |"
+.BR ipip " |"
+.BR sit " ]"
 
 .ti -8
 .BI "ip link delete " DEVICE
@@ -174,6 +177,15 @@
 .sp
 .BR vxlan
 - Virtual eXtended LAN
+.sp
+.BR ip6tnl
+- Virtual tunnel interface IPv4|IPv6 over IPv6
+.sp
+.BR ipip
+- Virtual tunnel interface IPv4 over IPv4
+.sp
+.BR sit
+- Virtual tunnel interface IPv6 over IPv4
 .in -8
 
 .TP
diff --git a/man/man8/ip-maddress.8 b/man/man8/ip-maddress.8
index afae551..e0bad47 100644
--- a/man/man8/ip-maddress.8
+++ b/man/man8/ip-maddress.8
@@ -15,11 +15,11 @@
 .ti -8
 
 .BR "ip maddress" " [ " add " | " del " ]"
-.IB MULTIADDR " dev " STRING
+.IB MULTIADDR " dev " NAME
 
 .ti -8
 .BR "ip maddress show" " [ " dev
-.IR STRING " ]"
+.IR NAME " ]"
 
 .SH DESCRIPTION
 .B maddress
@@ -33,14 +33,14 @@
 
 .SS ip maddress add - add a multicast address
 .SS ip maddress delete - delete a multicast address
-these commands attach/detach a static link layer multicast address
+these commands attach/detach a static link-layer multicast address
 to listen on the interface.
 Note that it is impossible to join protocol multicast groups
-statically.  This command only manages link layer addresses.
+statically.  This command only manages link-layer addresses.
 
 .TP
 .BI address " LLADDRESS " (default)
-the link layer multicast address.
+the link-layer multicast address.
 
 .TP
 .BI dev " NAME"
diff --git a/man/man8/ip-monitor.8 b/man/man8/ip-monitor.8
index 351a744..b6e8d1d 100644
--- a/man/man8/ip-monitor.8
+++ b/man/man8/ip-monitor.8
@@ -1,4 +1,4 @@
-.TH IP\-MONITOR 8 "20 Dec 2011" "iproute2" "Linux"
+.TH IP\-MONITOR 8 "13 Dec 2012" "iproute2" "Linux"
 .SH "NAME"
 ip-monitor, rtmon \- state monitoring
 .SH "SYNOPSIS"
@@ -6,8 +6,12 @@
 .ad l
 .in +8
 .ti -8
-.BR "ip monitor" " [ " all " |"
-.IR LISTofOBJECTS " ]"
+.BR "ip " " [ "
+.IR ip-OPTIONS " ]"
+.BR  "monitor" " [ " all " |"
+.IR OBJECT-LIST " ] ["
+.BI file " FILENAME "
+]
 .sp
 
 .SH DESCRIPTION
@@ -20,12 +24,15 @@
 command is the first in the command line and then the object list follows:
 
 .BR "ip monitor" " [ " all " |"
-.IR LISTofOBJECTS " ]"
+.IR OBJECT-LIST " ] ["
+.BI file " FILENAME "
+]
 
 .I OBJECT-LIST
 is the list of object types that we want to monitor.
 It may contain
-.BR link ", " address " and " route "."
+.BR link ", " address ", " route ", " mroute ", " prefix ", "
+.BR neigh " and " netconf "."
 If no
 .B file
 argument is given,
@@ -34,9 +41,12 @@
 described in previous sections.
 
 .P
-If a file name is given, it does not listen on RTNETLINK,
-but opens the file containing RTNETLINK messages saved in binary format
-and dumps them.  Such a history file can be generated with the
+If the
+.BI file
+option is given, the program does not listen on RTNETLINK,
+but opens the given file, and dumps its contents. The file
+should contain RTNETLINK messages saved in binary format.
+Such a file can be generated with the
 .B rtmon
 utility.  This utility has a command line syntax similar to
 .BR "ip monitor" .
@@ -53,7 +63,7 @@
 later.
 
 .P
-Certainly, it is possible to start
+Nevertheless, it is possible to start
 .B rtmon
 at any time.
 It prepends the history with the state snapshot dumped at the moment
diff --git a/man/man8/ip-mroute.8 b/man/man8/ip-mroute.8
index 98aab88..3b708cf 100644
--- a/man/man8/ip-mroute.8
+++ b/man/man8/ip-mroute.8
@@ -1,4 +1,4 @@
-.TH IP\-MROUTE 8 "20 Dec 2011" "iproute2" "Linux"
+.TH IP\-MROUTE 8 "13 Dec 2012" "iproute2" "Linux"
 .SH "NAME"
 ip-mroute \- multicast routing cache management
 .SH "SYNOPSIS"
@@ -6,16 +6,19 @@
 .ad l
 .in +8
 .ti -8
-.BR "ip mroute show" " ["
+.BR "ip " " [ ip-OPTIONS ] " "mroute show" " [ [ "
+.BR " to " " ] "
 .IR PREFIX " ] [ "
 .B  from
 .IR PREFIX " ] [ "
 .B  iif
-.IR DEVICE " ]"
+.IR DEVICE " ] [ "
+.B table
+.IR TABLE_ID " ] "
 
 .SH DESCRIPTION
 .B mroute
-objects are multicast routing cache entries created by a user level
+objects are multicast routing cache entries created by a user-level
 mrouting daemon (f.e.
 .B pimd
 or
@@ -25,7 +28,7 @@
 Due to the limitations of the current interface to the multicast routing
 engine, it is impossible to change
 .B mroute
-objects administratively, so we may only display them.  This limitation
+objects administratively, so we can only display them.  This limitation
 will be removed in the future.
 
 .SS ip mroute show - list mroute cache entries
@@ -42,6 +45,11 @@
 .BI from " PREFIX"
 the prefix selecting the IP source addresses of the multicast route.
 
+.TP
+.BI table " TABLE_ID"
+the table id selecting the multicast table. It can be
+.BR local ", " main ", " default ", " all " or a number."
+
 .SH SEE ALSO
 .br
 .BR ip (8)
diff --git a/man/man8/ip-neighbour.8 b/man/man8/ip-neighbour.8
index 34980c5..5d9768f 100644
--- a/man/man8/ip-neighbour.8
+++ b/man/man8/ip-neighbour.8
@@ -40,7 +40,7 @@
 objects that establish bindings between protocol addresses and
 link layer addresses for hosts sharing the same link.
 Neighbour entries are organized into tables. The IPv4 neighbour table
-is known by another name - the ARP table.
+is also known by another name - the ARP table.
 
 .P
 The corresponding commands display neighbour bindings
diff --git a/man/man8/ip-netconf.8 b/man/man8/ip-netconf.8
new file mode 100644
index 0000000..2718258
--- /dev/null
+++ b/man/man8/ip-netconf.8
@@ -0,0 +1,36 @@
+.TH IP\-NETCONF 8 "13 Dec 2012" "iproute2" "Linux"
+.SH "NAME"
+ip-netconf \- network configuration monitoring
+.SH "SYNOPSIS"
+.sp
+.ad l
+.in +8
+.ti -8
+.BR "ip " " [ ip-OPTIONS ] " "netconf show" " [ "
+.B dev
+.IR NAME " ]"
+
+.SH DESCRIPTION
+The
+.B ip netconf
+utility can monitor IPv4 and IPv6 parameters (see
+.BR "/proc/sys/net/ipv[4|6]/conf/[all|DEV]/" ")"
+like forwarding, rp_filter
+or mc_forwarding status.
+
+If no interface is specified, the entry
+.B all
+is displayed.
+
+.SS ip netconf show - display network parameters
+
+.TP
+.BI dev " NAME"
+the name of the device to display network parameters for.
+
+.SH SEE ALSO
+.br
+.BR ip (8)
+
+.SH AUTHOR
+Original Manpage by Nicolas Dichtel <nicolas.dichtel@6wind.com>
diff --git a/man/man8/ip-netns.8 b/man/man8/ip-netns.8
index 349ee7e..87534be 100644
--- a/man/man8/ip-netns.8
+++ b/man/man8/ip-netns.8
@@ -1,4 +1,4 @@
-.TH IP\-NETNS 8 "20 Dec 2011" "iproute2" "Linux"
+.TH IP\-NETNS 8 "16 Jan 2013" "iproute2" "Linux"
 .SH NAME
 ip-netns \- process network namespace management
 .SH SYNOPSIS
@@ -20,9 +20,20 @@
 .I NETNSNAME
 
 .ti -8
+.BR "ip netns identify"
+.I PID
+
+.ti -8
+.BR "ip netns pids"
+.I NETNSNAME
+
+.ti -8
 .BR "ip netns exec "
 .I NETNSNAME command ...
 
+.ti -8
+.BR "ip netns monitor"
+
 .SH DESCRIPTION
 A network namespace is logically another copy of the network stack,
 with its own routes, firewall rules, and network devices.
@@ -37,8 +48,8 @@
 .B setns(2)
 system call to change the network namespace associated with a task.
 
-The convention for network namespace aware applications is to look
-for global network configuration files first in
+For applications that are aware of network namespaces, the convention
+is to look for global network configuration files first in
 .BR "/etc/netns/" NAME "/"
 then in
 .BR "/etc/".
@@ -54,11 +65,62 @@
 their traditional location in /etc.
 
 .SS ip netns list - show all of the named network namespaces
+
+This command displays all of the network namespaces in /var/run/netns
+
 .SS ip netns add NAME - create a new named network namespace
+
+If NAME is available in /var/run/netns/ this command creates a new
+network namespace and assigns NAME.
+
 .SS ip netns delete NAME - delete the name of a network namespace
+
+If NAME is present in /var/run/netns it is umounted and the mount
+point is removed.  If this is the last user of the network namespace the
+network namespace will be freed, otherwise the network namespace
+persists until it has no more users.  ip netns delete may fail if
+the mount point is in use in another mount namespace.
+
+.SS ip netns identify PID - Report network namespaces names for process
+
+This command walks through /var/run/netns and finds all the network
+namespace names for network namespace of the specified process.
+
+.SS ip netns pids NAME - Report processes in the named network namespace
+
+This command walks through proc and finds all of the process who have
+the named network namespace as their primary network namespace.
+
 .SS ip netns exec NAME cmd ... - Run cmd in the named network namespace
 
+This command allows applications that are network namespace unaware
+to be run in something other than the default network namespace with
+all of the configuration for the specified network namespace appearing
+in the customary global locations.  A network namespace and bind mounts
+are used to move files from their network namespace specific location
+to their default locations without affecting other processes.
+
+.SS ip netns monitor - Report as network namespace names are added and deleted
+
+This command watches network namespace name addition and deletion events
+and prints a line for each event it sees.
+
 .SH EXAMPLES
+.PP
+ip netns list
+.RS
+Shows the list of current named network namespaces
+.RE
+.PP
+ip netns add vpn
+.RS
+Creates a network namespace and names it vpn
+.RE
+.PP
+ip netns exec vpn ip link set lo up
+.RS
+Bring up the loopback interface in the vpn network namespace.
+.RE
 
 .SH SEE ALSO
 .br
diff --git a/man/man8/ip-route.8.in b/man/man8/ip-route.8.in
index f06fcba..2c35a97 100644
--- a/man/man8/ip-route.8.in
+++ b/man/man8/ip-route.8.in
@@ -1,4 +1,4 @@
-.TH IP\-ROUTE 8 "20 Dec 2011" "iproute2" "Linux"
+.TH IP\-ROUTE 8 "13 Dec 2012" "iproute2" "Linux"
 .SH "NAME"
 ip-route \- routing table management
 .SH "SYNOPSIS"
@@ -7,7 +7,7 @@
 .in +8
 .ti -8
 .B ip
-.RI "[ " OPTIONS " ]"
+.RI "[ " ip-OPTIONS " ]"
 .B route
 .RI " { " COMMAND " | "
 .BR help " }"
diff --git a/man/man8/ip-rule.8 b/man/man8/ip-rule.8
index 0f62a53..36f461b 100644
--- a/man/man8/ip-rule.8
+++ b/man/man8/ip-rule.8
@@ -75,16 +75,16 @@
 .B selector
 and an
 .B action predicate.
-The RPDB is scanned in the order of increasing priority. The selector
+The RPDB is scanned in order of decreasing priority. The selector
 of each rule is applied to {source address, destination address, incoming
 interface, tos, fwmark} and, if the selector matches the packet,
 the action is performed.  The action predicate may return with success.
 In this case, it will either give a route or failure indication
 and the RPDB lookup is terminated. Otherwise, the RPDB program
-continues on the next rule.
+continues with the next rule.
 
 .P
-Semantically, natural action is to select the nexthop and the output device.
+Semantically, the natural action is to select the nexthop and the output device.
 
 .P
 At startup time the kernel configures the default RPDB consisting of three
diff --git a/man/man8/ip-tunnel.8 b/man/man8/ip-tunnel.8
index 37ba542..b408517 100644
--- a/man/man8/ip-tunnel.8
+++ b/man/man8/ip-tunnel.8
@@ -47,7 +47,6 @@
 .RB "[ [" no "]" pmtudisc " ]"
 .RB "[ " dev
 .IR PHYS_DEV " ]"
-.RB "[ " "dscp inherit" " ]"
 
 .ti -8
 .IR MODE " := "
@@ -58,8 +57,12 @@
 .BR any " }"
 
 .ti -8
-.IR TOS " := { " NUMBER " |"
-.BR inherit " }"
+.IR TOS " := { " STRING " | " 00 ".." ff " |"
+.BR inherit " |"
+.BI "inherit/" STRING
+.R " |"
+.BI "inherit/" 00 ".." ff
+.R " }"
 
 .ti -8
 .IR ELIM " := {"
@@ -132,11 +135,21 @@
 .BI dsfield " T"
 .TP
 .BI tclass " T"
-set a fixed TOS (or traffic class in IPv6)
-.I T
-on tunneled packets.
-The default value is:
-.BR "inherit" .
+set the type of service (IPv4) or traffic class (IPv6) field on
+tunneled packets, which can be specified as either a two-digit
+hex value (e.g. c0) or a predefined string (e.g. internet).
+The value
+.B inherit
+causes the field to be copied from the original IP header. The
+values
+.BI "inherit/" STRING
+or
+.BI "inherit/" 00 ".." ff
+will set the field to
+.I STRING
+or
+.IR 00 ".." ff
+when tunneling non-IP packets. The default value is 00.
 
 .TP
 .BI dev " NAME"
@@ -202,11 +215,6 @@
 .B It isn't work. Don't use it.
 
 .TP
-.BR "dscp inherit"
-.RB ( " only IPv6 tunnels " )
-Inherit DS field between inner and outer header.
-
-.TP
 .BI encaplim " ELIM"
 .RB ( " only IPv6 tunnels " )
 set a fixed encapsulation limit.  Default is 4.
diff --git a/man/man8/ip.8 b/man/man8/ip.8
index 9063049..881e1ff 100644
--- a/man/man8/ip.8
+++ b/man/man8/ip.8
@@ -36,24 +36,24 @@
 utility and exit.
 
 .TP
-.BR "\-s" , " \-stats", " \-statistics"
+.BR "\-s" , " \-stats" , " \-statistics"
 output more information.  If the option
 appears twice or more, the amount of information increases.
 As a rule, the information is statistics or some time values.
 
 .TP
-.BR "\-l" , " \-loops"
+.BR "\-l" , " \-loops " <COUNT>
 Specify maximum number of loops the 'ip addr flush' logic
 will attempt before giving up.  The default is 10.
 Zero (0) means loop until all addresses are removed.
 
 .TP
-.BR "\-f" , " \-family"
-followed by protocol family identifier:
-.BR "inet" , " inet6" , "bridge" , "ipx" , "dnet"
+.BR "\-f" , " \-family " <FAMILY>
+Specifies the protocol family to use. The protocol family identifier can be one of
+.BR "inet" , " inet6" , " bridge" , " ipx" , " dnet"
 or
-.BR link ,
-enforce the protocol family to use.  If the option is not present,
+.BR link .
+If this option is not present,
 the protocol family is guessed from other arguments.  If the rest
 of the command line does not give enough information to guess the
 family,
@@ -178,9 +178,9 @@
 
 .PP
 The names of all objects may be written in full or
-abbreviated form, f.e.
+abbreviated form, for exampe
 .B address
-is abbreviated as
+can be abbreviated as
 .B addr
 or just
 .B a.
@@ -230,7 +230,7 @@
 .br
 .RB "IP Command reference " ip-cref.ps
 .SH REPORTING BUGS
-Report bug to the Network Developers mailing list
+Report any bugs to the Network Developers mailing list
 .B <netdev@vger.kernel.org>
 where the development and maintenance is primarily done.
 You do not have to be subscribed to the list to send a message there.
diff --git a/man/man8/tc-ematch.8 b/man/man8/tc-ematch.8
index 2eafc29..957a22e 100644
--- a/man/man8/tc-ematch.8
+++ b/man/man8/tc-ematch.8
@@ -1,4 +1,4 @@
-.TH filter ematch "6 August 2012" iproute2 Linux
+.TH ematch 8 "6 August 2012" iproute2 Linux
 .
 .SH NAME
 ematch \- extended matches for use with "basic" or "flow" filters
diff --git a/man/man8/tc.8 b/man/man8/tc.8
index e9a7229..bbc6790 100644
--- a/man/man8/tc.8
+++ b/man/man8/tc.8
@@ -75,7 +75,7 @@
 
 .TP
 POLICING
-Where shaping deals with transmission of traffic, policing pertains to traffic
+Whereas shaping deals with transmission of traffic, policing pertains to traffic
 arriving. Policing thus occurs on ingress.
 
 .TP
@@ -203,7 +203,7 @@
 .B leaf qdisc
 which by default has
 .B pfifo
-behaviour though another qdisc can be attached in place. This qdisc may again
+behaviour, although another qdisc can be attached in place. This qdisc may again
 contain classes, but each class can have only one leaf qdisc.
 
 When a packet enters a classful qdisc it can be
@@ -233,7 +233,7 @@
 All qdiscs, classes and filters have IDs, which can either be specified
 or be automatically assigned.
 
-ID's consist of a major number and a minor number, separated by a colon.
+IDs consist of a major number and a minor number, separated by a colon.
 Both major and minor number are limited to 16 bits. There are two special
 values: root is signified by major and minor of all ones, and unspecified
 is all zeros.
diff --git a/misc/arpd.c b/misc/arpd.c
index dd1de80..ec9d570 100644
--- a/misc/arpd.c
+++ b/misc/arpd.c
@@ -92,7 +92,7 @@
 int broadcast_burst = 3000;
 int poll_timeout = 30000;
 
-void usage(void)
+static void usage(void)
 {
 	fprintf(stderr,
 		"Usage: arpd [ -lkh? ] [ -a N ] [ -b dbase ] [ -B number ]"
@@ -100,7 +100,7 @@
 	exit(1);
 }
 
-int handle_if(int ifindex)
+static int handle_if(int ifindex)
 {
 	int i;
 
@@ -115,7 +115,7 @@
 
 int sysctl_adjusted;
 
-void do_sysctl_adjustments(void)
+static void do_sysctl_adjustments(void)
 {
 	int i;
 
@@ -148,7 +148,7 @@
 	sysctl_adjusted = 1;
 }
 
-void undo_sysctl_adjustments(void)
+static void undo_sysctl_adjustments(void)
 {
 	int i;
 
@@ -178,7 +178,7 @@
 }
 
 
-int send_probe(int ifindex, __u32 addr)
+static int send_probe(int ifindex, __u32 addr)
 {
 	struct ifreq ifr;
 	struct sockaddr_in dst;
@@ -238,7 +238,7 @@
 
 /* Be very tough on sending probes: 1 per second with burst of 3. */
 
-int queue_active_probe(int ifindex, __u32 addr)
+static int queue_active_probe(int ifindex, __u32 addr)
 {
 	static struct timeval prev;
 	static int buckets;
@@ -262,7 +262,7 @@
 	return -1;
 }
 
-int respond_to_kernel(int ifindex, __u32 addr, char *lla, int llalen)
+static int respond_to_kernel(int ifindex, __u32 addr, char *lla, int llalen)
 {
 	struct {
 		struct nlmsghdr 	n;
@@ -286,7 +286,7 @@
 	return rtnl_send(&rth, &req, req.n.nlmsg_len) <= 0;
 }
 
-void prepare_neg_entry(__u8 *ndata, __u32 stamp)
+static void prepare_neg_entry(__u8 *ndata, __u32 stamp)
 {
 	ndata[0] = 0xFF;
 	ndata[1] = 0;
@@ -297,7 +297,7 @@
 }
 
 
-int do_one_request(struct nlmsghdr *n)
+static int do_one_request(struct nlmsghdr *n)
 {
 	struct ndmsg *ndm = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
@@ -426,12 +426,12 @@
 	return 0;
 }
 
-void load_initial_table(void)
+static void load_initial_table(void)
 {
 	rtnl_wilddump_request(&rth, AF_INET, RTM_GETNEIGH);
 }
 
-void get_kern_msg(void)
+static void get_kern_msg(void)
 {
 	int status;
 	struct nlmsghdr *h;
@@ -477,7 +477,7 @@
 }
 
 /* Receive gratuitous ARP messages and store them, that's all. */
-void get_arp_pkt(void)
+static void get_arp_pkt(void)
 {
 	unsigned char buf[1024];
 	struct sockaddr_ll sll;
@@ -532,7 +532,7 @@
 	dbase->put(dbase, &dbkey, &dbdat, 0);
 }
 
-void catch_signal(int sig, void (*handler)(int))
+static void catch_signal(int sig, void (*handler)(int))
 {
 	struct sigaction sa;
 
@@ -548,21 +548,21 @@
 sigjmp_buf env;
 volatile int in_poll;
 
-void sig_exit(int signo)
+static void sig_exit(int signo)
 {
 	do_exit = 1;
 	if (in_poll)
 		siglongjmp(env, 1);
 }
 
-void sig_sync(int signo)
+static void sig_sync(int signo)
 {
 	do_sync = 1;
 	if (in_poll)
 		siglongjmp(env, 1);
 }
 
-void sig_stats(int signo)
+static void sig_stats(int signo)
 {
 	do_sync = 1;
 	do_stats = 1;
@@ -570,7 +570,7 @@
 		siglongjmp(env, 1);
 }
 
-void send_stats(void)
+static void send_stats(void)
 {
 	syslog(LOG_INFO, "arp_rcv: n%lu c%lu app_rcv: tot %lu hits %lu bad %lu neg %lu sup %lu",
 	       stats.arp_new, stats.arp_change,
diff --git a/misc/ifstat.c b/misc/ifstat.c
index e7fbaa8..6d0ad8c 100644
--- a/misc/ifstat.c
+++ b/misc/ifstat.c
@@ -115,7 +115,7 @@
 	return 0;
 }
 
-void load_info(void)
+static void load_info(void)
 {
 	struct ifstat_ent *db, *n;
 	struct rtnl_handle rth;
@@ -146,7 +146,7 @@
 	}
 }
 
-void load_raw_table(FILE *fp)
+static void load_raw_table(FILE *fp)
 {
 	char buf[4096];
 	struct ifstat_ent *db = NULL;
@@ -209,7 +209,7 @@
 	}
 }
 
-void dump_raw_db(FILE *fp, int to_hist)
+static void dump_raw_db(FILE *fp, int to_hist)
 {
 	struct ifstat_ent *n, *h;
 	h = hist_db;
@@ -244,7 +244,8 @@
 static const unsigned long long mega = 1000000;
 static const unsigned long long kilo = 1000;
 
-void format_rate(FILE *fp, unsigned long long *vals, double *rates, int i)
+static void format_rate(FILE *fp, unsigned long long *vals,
+			double *rates, int i)
 {
 	char temp[64];
 	if (vals[i] > giga)
@@ -264,7 +265,7 @@
 		fprintf(fp, "%-6u ", (unsigned)rates[i]);
 }
 
-void format_pair(FILE *fp, unsigned long long *vals, int i, int k)
+static void format_pair(FILE *fp, unsigned long long *vals, int i, int k)
 {
 	char temp[64];
 	if (vals[i] > giga)
@@ -284,7 +285,7 @@
 		fprintf(fp, "%-6u ", (unsigned)vals[k]);
 }
 
-void print_head(FILE *fp)
+static void print_head(FILE *fp)
 {
 	fprintf(fp, "#%s\n", info_source);
 	fprintf(fp, "%-15s ", "Interface");
@@ -327,7 +328,8 @@
 	}
 }
 
-void print_one_if(FILE *fp, struct ifstat_ent *n, unsigned long long *vals)
+static void print_one_if(FILE *fp, struct ifstat_ent *n,
+			 unsigned long long *vals)
 {
 	int i;
 	fprintf(fp, "%-15s ", n->name);
@@ -374,7 +376,7 @@
 }
 
 
-void dump_kern_db(FILE *fp)
+static void dump_kern_db(FILE *fp)
 {
 	struct ifstat_ent *n;
 
@@ -388,7 +390,7 @@
 }
 
 
-void dump_incr_db(FILE *fp)
+static void dump_incr_db(FILE *fp)
 {
 	struct ifstat_ent *n, *h;
 	h = hist_db;
@@ -419,11 +421,11 @@
 
 static int children;
 
-void sigchild(int signo)
+static void sigchild(int signo)
 {
 }
 
-void update_db(int interval)
+static void update_db(int interval)
 {
 	struct ifstat_ent *n, *h;
 
@@ -482,7 +484,7 @@
 #define T_DIFF(a,b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000)
 
 
-void server_loop(int fd)
+static void server_loop(int fd)
 {
 	struct timeval snaptime = { 0 };
 	struct pollfd p;
@@ -534,7 +536,7 @@
 	}
 }
 
-int verify_forging(int fd)
+static int verify_forging(int fd)
 {
 	struct ucred cred;
 	socklen_t olen = sizeof(cred);
diff --git a/misc/nstat.c b/misc/nstat.c
index 2f06ffd..b685978 100644
--- a/misc/nstat.c
+++ b/misc/nstat.c
@@ -55,17 +55,17 @@
 	return open(p, O_RDONLY);
 }
 
-int net_netstat_open(void)
+static int net_netstat_open(void)
 {
 	return generic_proc_open("PROC_NET_NETSTAT", "net/netstat");
 }
 
-int net_snmp_open(void)
+static int net_snmp_open(void)
 {
 	return generic_proc_open("PROC_NET_SNMP", "net/snmp");
 }
 
-int net_snmp6_open(void)
+static int net_snmp6_open(void)
 {
 	return generic_proc_open("PROC_NET_SNMP6", "net/snmp6");
 }
@@ -82,13 +82,13 @@
 struct nstat_ent *kern_db;
 struct nstat_ent *hist_db;
 
-char *useless_numbers[] = {
-"IpForwarding", "IpDefaultTTL",
-"TcpRtoAlgorithm", "TcpRtoMin", "TcpRtoMax",
-"TcpMaxConn", "TcpCurrEstab"
+static const char *useless_numbers[] = {
+	"IpForwarding", "IpDefaultTTL",
+	"TcpRtoAlgorithm", "TcpRtoMin", "TcpRtoMax",
+	"TcpMaxConn", "TcpCurrEstab"
 };
 
-int useless_number(char *id)
+static int useless_number(const char *id)
 {
 	int i;
 	for (i=0; i<sizeof(useless_numbers)/sizeof(*useless_numbers); i++)
@@ -97,7 +97,7 @@
 	return 0;
 }
 
-int match(char *id)
+static int match(const char *id)
 {
 	int i;
 
@@ -111,7 +111,7 @@
 	return 0;
 }
 
-void load_good_table(FILE *fp)
+static void load_good_table(FILE *fp)
 {
 	char buf[4096];
 	struct nstat_ent *db = NULL;
@@ -157,7 +157,7 @@
 }
 
 
-void load_ugly_table(FILE *fp)
+static void load_ugly_table(FILE *fp)
 {
 	char buf[4096];
 	struct nstat_ent *db = NULL;
@@ -228,7 +228,7 @@
 	}
 }
 
-void load_snmp(void)
+static void load_snmp(void)
 {
 	FILE *fp = fdopen(net_snmp_open(), "r");
 	if (fp) {
@@ -237,7 +237,7 @@
 	}
 }
 
-void load_snmp6(void)
+static void load_snmp6(void)
 {
 	FILE *fp = fdopen(net_snmp6_open(), "r");
 	if (fp) {
@@ -246,7 +246,7 @@
 	}
 }
 
-void load_netstat(void)
+static void load_netstat(void)
 {
 	FILE *fp = fdopen(net_netstat_open(), "r");
 	if (fp) {
@@ -255,7 +255,7 @@
 	}
 }
 
-void dump_kern_db(FILE *fp, int to_hist)
+static void dump_kern_db(FILE *fp, int to_hist)
 {
 	struct nstat_ent *n, *h;
 	h = hist_db;
@@ -280,7 +280,7 @@
 	}
 }
 
-void dump_incr_db(FILE *fp)
+static void dump_incr_db(FILE *fp)
 {
 	struct nstat_ent *n, *h;
 	h = hist_db;
@@ -311,11 +311,11 @@
 
 static int children;
 
-void sigchild(int signo)
+static void sigchild(int signo)
 {
 }
 
-void update_db(int interval)
+static void update_db(int interval)
 {
 	struct nstat_ent *n, *h;
 
@@ -367,7 +367,7 @@
 #define T_DIFF(a,b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000)
 
 
-void server_loop(int fd)
+static void server_loop(int fd)
 {
 	struct timeval snaptime = { 0 };
 	struct pollfd p;
@@ -419,7 +419,7 @@
 	}
 }
 
-int verify_forging(int fd)
+static int verify_forging(int fd)
 {
 	struct ucred cred;
 	socklen_t olen = sizeof(cred);
diff --git a/misc/rtacct.c b/misc/rtacct.c
index 49168bd..bb8c90f 100644
--- a/misc/rtacct.c
+++ b/misc/rtacct.c
@@ -55,12 +55,12 @@
 	return open(p, O_RDONLY);
 }
 
-int net_rtacct_open(void)
+static int net_rtacct_open(void)
 {
 	return generic_proc_open("PROC_NET_RTACCT", "net/rt_acct");
 }
 
-__u32 rmap[256/4];
+static __u32 rmap[256/4];
 
 struct rtacct_data
 {
@@ -71,12 +71,12 @@
 	char			signature[128];
 };
 
-struct rtacct_data kern_db_static;
+static struct rtacct_data kern_db_static;
 
-struct rtacct_data *kern_db = &kern_db_static;
-struct rtacct_data *hist_db;
+static struct rtacct_data *kern_db = &kern_db_static;
+static struct rtacct_data *hist_db;
 
-void nread(int fd, char *buf, int tot)
+static void nread(int fd, char *buf, int tot)
 {
 	int count = 0;
 
@@ -93,8 +93,7 @@
 	}
 }
 
-
-__u32 *read_kern_table(__u32 *tbl)
+static __u32 *read_kern_table(__u32 *tbl)
 {
 	static __u32 *tbl_ptr;
 	int fd;
@@ -130,7 +129,7 @@
 	return tbl;
 }
 
-void format_rate(FILE *fp, double rate)
+static void format_rate(FILE *fp, double rate)
 {
 	char temp[64];
 
@@ -144,7 +143,7 @@
 		fprintf(fp, " %-10u", (unsigned)rate);
 }
 
-void format_count(FILE *fp, unsigned long long val)
+static void format_count(FILE *fp, unsigned long long val)
 {
 	if (val > 1024*1024*1024)
 		fprintf(fp, " %10lluM", val/(1024*1024));
@@ -154,7 +153,7 @@
 		fprintf(fp, " %10llu", val);
 }
 
-void dump_abs_db(FILE *fp)
+static void dump_abs_db(FILE *fp)
 {
 	int realm;
 	char b1[16];
@@ -216,7 +215,7 @@
 }
 
 
-void dump_incr_db(FILE *fp)
+static void dump_incr_db(FILE *fp)
 {
 	int k, realm;
 	char b1[16];
@@ -293,13 +292,13 @@
 
 static int children;
 
-void sigchild(int signo)
+static void sigchild(int signo)
 {
 }
 
 /* Server side only: read kernel data, update tables, calculate rates. */
 
-void update_db(int interval)
+static void update_db(int interval)
 {
 	int i;
 	__u32 *ival;
@@ -331,7 +330,7 @@
 	}
 }
 
-void send_db(int fd)
+static void send_db(int fd)
 {
 	int tot = 0;
 
@@ -351,7 +350,7 @@
 #define T_DIFF(a,b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000)
 
 
-void pad_kern_table(struct rtacct_data *dat, __u32 *ival)
+static void pad_kern_table(struct rtacct_data *dat, __u32 *ival)
 {
 	int i;
 	memset(dat->rate, 0, sizeof(dat->rate));
@@ -361,7 +360,7 @@
 		dat->val[i] = ival[i];
 }
 
-void server_loop(int fd)
+static void server_loop(int fd)
 {
 	struct timeval snaptime = { 0 };
 	struct pollfd p;
@@ -410,7 +409,7 @@
 	}
 }
 
-int verify_forging(int fd)
+static int verify_forging(int fd)
 {
 	struct ucred cred;
 	socklen_t olen = sizeof(cred);
diff --git a/misc/ss.c b/misc/ss.c
index b45f5ba..ff7c194 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -105,7 +105,7 @@
 };
 
 struct filter default_filter = {
-	.dbs	=  (1<<TCP_DB),
+	.dbs	=  ~0,
 	.states = SS_ALL & ~((1<<SS_LISTEN)|(1<<SS_CLOSE)|(1<<SS_TIME_WAIT)|(1<<SS_SYN_RECV)),
 	.families= (1<<AF_INET)|(1<<AF_INET6),
 };
@@ -309,7 +309,7 @@
 	closedir(dir);
 }
 
-int find_users(unsigned ino, char *buf, int buflen)
+static int find_users(unsigned ino, char *buf, int buflen)
 {
 	struct user_ent *p;
 	int cnt = 0;
@@ -365,7 +365,7 @@
 	"skbuff_head_cache",
 };
 
-int get_slabstat(struct slabstat *s)
+static int get_slabstat(struct slabstat *s)
 {
 	char buf[256];
 	FILE *fp;
@@ -455,7 +455,7 @@
 	"unknown"
 };
 
-const char *print_ms_timer(int timeout)
+static const char *print_ms_timer(int timeout)
 {
 	static char buf[64];
 	int secs, msecs, minutes;
@@ -482,7 +482,7 @@
 	return buf;
 }
 
-const char *print_hz_timer(int timeout)
+static const char *print_hz_timer(int timeout)
 {
 	int hz = get_user_hz();
 	return print_ms_timer(((timeout*1000) + hz-1)/hz);
@@ -498,7 +498,7 @@
 
 struct scache *rlist;
 
-void init_service_resolver(void)
+static void init_service_resolver(void)
 {
 	char buf[128];
 	FILE *fp = popen("/usr/sbin/rpcinfo -p 2>/dev/null", "r");
@@ -555,7 +555,7 @@
 }
 
 
-const char *__resolve_service(int port)
+static const char *__resolve_service(int port)
 {
 	struct scache *c;
 
@@ -580,7 +580,7 @@
 }
 
 
-const char *resolve_service(int port)
+static const char *resolve_service(int port)
 {
 	static char buf[128];
 	static struct scache cache[256];
@@ -634,7 +634,7 @@
 	return buf;
 }
 
-void formatted_print(const inet_prefix *a, int port)
+static void formatted_print(const inet_prefix *a, int port)
 {
 	char buf[1024];
 	const char *ap = buf;
@@ -667,7 +667,8 @@
 	struct aafilter *next;
 };
 
-int inet2_addr_match(const inet_prefix *a, const inet_prefix *p, int plen)
+static int inet2_addr_match(const inet_prefix *a, const inet_prefix *p,
+			    int plen)
 {
 	if (!inet_addr_match(a, p, plen))
 		return 0;
@@ -686,7 +687,7 @@
 	return 1;
 }
 
-int unix_match(const inet_prefix *a, const inet_prefix *p)
+static int unix_match(const inet_prefix *a, const inet_prefix *p)
 {
 	char *addr, *pattern;
 	memcpy(&addr, a->data, sizeof(addr));
@@ -698,7 +699,7 @@
 	return !fnmatch(pattern, addr, 0);
 }
 
-int run_ssfilter(struct ssfilter *f, struct tcpstat *s)
+static int run_ssfilter(struct ssfilter *f, struct tcpstat *s)
 {
 	switch (f->type) {
 		case SSF_S_AUTO:
@@ -1882,7 +1883,7 @@
 }
 
 
-int dgram_show_line(char *line, const struct filter *f, int family)
+static int dgram_show_line(char *line, const struct filter *f, int family)
 {
 	struct tcpstat s;
 	char *loc, *rem, *data;
@@ -1974,7 +1975,7 @@
 }
 
 
-int udp_show(struct filter *f)
+static int udp_show(struct filter *f)
 {
 	FILE *fp = NULL;
 
@@ -2010,7 +2011,7 @@
 	} while (0);
 }
 
-int raw_show(struct filter *f)
+static int raw_show(struct filter *f)
 {
 	FILE *fp = NULL;
 
@@ -2063,7 +2064,7 @@
 
 #define MAX_UNIX_REMEMBER (1024*1024/sizeof(struct unixstat))
 
-void unix_list_free(struct unixstat *list)
+static void unix_list_free(struct unixstat *list)
 {
 	while (list) {
 		struct unixstat *s = list;
@@ -2074,7 +2075,7 @@
 	}
 }
 
-void unix_list_print(struct unixstat *list, struct filter *f)
+static void unix_list_print(struct unixstat *list, struct filter *f)
 {
 	struct unixstat *s;
 	char *peer;
@@ -2283,7 +2284,7 @@
 	return 0;
 }
 
-int unix_show(struct filter *f)
+static int unix_show(struct filter *f)
 {
 	FILE *fp;
 	char buf[256];
@@ -2368,7 +2369,7 @@
 }
 
 
-int packet_show(struct filter *f)
+static int packet_show(struct filter *f)
 {
 	FILE *fp;
 	char buf[256];
@@ -2445,7 +2446,7 @@
 	return 0;
 }
 
-int netlink_show(struct filter *f)
+static int netlink_show(struct filter *f)
 {
 	FILE *fp;
 	char buf[256];
@@ -2534,7 +2535,7 @@
 	int tcp_estab;
 };
 
-int get_snmp_int(char *proto, char *key, int *result)
+static int get_snmp_int(char *proto, char *key, int *result)
 {
 	char buf[1024];
 	FILE *fp;
@@ -2629,7 +2630,7 @@
 		       &s->tcp_orphans, &s->tcp_tws, &s->tcp_total, &s->tcp_mem);
 }
 
-int get_sockstat(struct sockstat *s)
+static int get_sockstat(struct sockstat *s)
 {
 	char buf[256];
 	FILE *fp;
@@ -2651,7 +2652,7 @@
 	return 0;
 }
 
-int print_summary(void)
+static int print_summary(void)
 {
 	struct sockstat s;
 	struct snmpstat sn;
@@ -2743,7 +2744,7 @@
 }
 
 
-int scan_state(const char *state)
+static int scan_state(const char *state)
 {
 	int i;
 	if (strcasecmp(state, "close") == 0 ||
diff --git a/tc/Makefile b/tc/Makefile
index 696f891..f26e764 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -140,6 +140,8 @@
 m_xt_old.so: m_xt_old.c
 	$(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic -o m_xt_old.so m_xt_old.c $$($(PKG_CONFIG) xtables --cflags --libs)
 
+em_ipset.o: CFLAGS += $$($(PKG_CONFIG) xtables --cflags)
+
 %.yacc.c: %.y
 	$(YACC) $(YACCFLAGS) -o $@ $<
 
diff --git a/tc/f_rsvp.c b/tc/f_rsvp.c
index 8eaf85d..44d3bdf 100644
--- a/tc/f_rsvp.c
+++ b/tc/f_rsvp.c
@@ -37,7 +37,7 @@
 	fprintf(stderr, "\nNOTE: CLASSID is parsed as hexadecimal input.\n");
 }
 
-int get_addr_and_pi(int *argc_p, char ***argv_p, inet_prefix * addr,
+static int get_addr_and_pi(int *argc_p, char ***argv_p, inet_prefix * addr,
 		    struct tc_rsvp_pinfo *pinfo, int dir, int family)
 {
 	int argc = *argc_p;
diff --git a/tc/f_u32.c b/tc/f_u32.c
index 66c3247..f2a862d 100644
--- a/tc/f_u32.c
+++ b/tc/f_u32.c
@@ -45,7 +45,7 @@
 	fprintf(stderr, "\nNOTE: CLASSID is parsed at hexadecimal input.\n");
 }
 
-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, ':');
@@ -80,7 +80,7 @@
 	return 0;
 }
 
-char * sprint_u32_handle(__u32 handle, char *buf)
+static char * sprint_u32_handle(__u32 handle, char *buf)
 {
 	int bsize = SPRINT_BSIZE-1;
 	__u32 htid = TC_U32_HTID(handle);
@@ -194,7 +194,7 @@
 }
 
 
-int parse_at(int *argc_p, char ***argv_p, int *off, int *offmask)
+static int parse_at(int *argc_p, char ***argv_p, int *off, int *offmask)
 {
 	int argc = *argc_p;
 	char **argv = *argv_p;
diff --git a/tc/m_action.c b/tc/m_action.c
index 1fe2431..4acabef 100644
--- a/tc/m_action.c
+++ b/tc/m_action.c
@@ -35,7 +35,7 @@
 int batch_c = 0;
 int tab_flush = 0;
 
-void act_usage(void)
+static void act_usage(void)
 {
 	/*XXX: In the near future add a action->print_help to improve
 	 * usability
@@ -83,7 +83,7 @@
 	return -1;
 }
 
-struct action_util *get_action_kind(char *str)
+static struct action_util *get_action_kind(char *str)
 {
 	static void *aBODY;
 	void *dlh;
@@ -138,7 +138,7 @@
 	return a;
 }
 
-int
+static int
 new_cmd(char **argv)
 {
 	if ((matches(*argv, "change") == 0) ||
@@ -241,7 +241,7 @@
 	return -1;
 }
 
-int
+static int
 tc_print_one_action(FILE * f, struct rtattr *arg)
 {
 
@@ -355,7 +355,7 @@
 	return 0;
 }
 
-int tc_action_gd(int cmd, unsigned flags, int *argc_p, char ***argv_p)
+static int tc_action_gd(int cmd, unsigned flags, int *argc_p, char ***argv_p)
 {
 	char k[16];
 	struct action_util *a = NULL;
@@ -467,7 +467,7 @@
 	return ret;
 }
 
-int tc_action_modify(int cmd, unsigned flags, int *argc_p, char ***argv_p)
+static int tc_action_modify(int cmd, unsigned flags, int *argc_p, char ***argv_p)
 {
 	int argc = *argc_p;
 	char **argv = *argv_p;
@@ -507,7 +507,7 @@
 	return ret;
 }
 
-int tc_act_list_or_flush(int argc, char **argv, int event)
+static int tc_act_list_or_flush(int argc, char **argv, int event)
 {
 	int ret = 0, prio = 0, msg_size = 0;
 	char k[16];
diff --git a/tc/m_ematch.h b/tc/m_ematch.h
index 5036e9b..81456aa 100644
--- a/tc/m_ematch.h
+++ b/tc/m_ematch.h
@@ -4,6 +4,7 @@
 #include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>
 
 #include "utils.h"
 #include "tc_util.h"
diff --git a/tc/m_estimator.c b/tc/m_estimator.c
index a9e5dbc..3dc8624 100644
--- a/tc/m_estimator.c
+++ b/tc/m_estimator.c
@@ -22,6 +22,7 @@
 
 #include "utils.h"
 #include "tc_util.h"
+#include "tc_common.h"
 
 static void est_help(void);
 
@@ -31,7 +32,6 @@
 	fprintf(stderr, "  INTERVAL is interval between measurements\n");
 	fprintf(stderr, "  TIME-CONST is averaging time constant\n");
 	fprintf(stderr, "Example: ... est 1sec 8sec\n");
-	return;
 }
 
 int parse_estimator(int *p_argc, char ***p_argv, struct tc_estimator *est)
diff --git a/tc/m_gact.c b/tc/m_gact.c
index 9f07851..94bd5e7 100644
--- a/tc/m_gact.c
+++ b/tc/m_gact.c
@@ -68,7 +68,7 @@
 	exit(-1);
 }
 
-int
+static int
 get_act(char ***argv_p)
 {
 	char **argv = *argv_p;
@@ -89,8 +89,9 @@
 	}
 }
 
-int
-parse_gact(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
+static int
+parse_gact(struct action_util *a, int *argc_p, char ***argv_p,
+	   int tca_id, struct nlmsghdr *n)
 {
 	int argc = *argc_p;
 	char **argv = *argv_p;
@@ -202,7 +203,7 @@
 	return 0;
 }
 
-int
+static int
 print_gact(struct action_util *au,FILE * f, struct rtattr *arg)
 {
 	SPRINT_BUF(b1);
diff --git a/tc/m_mirred.c b/tc/m_mirred.c
index 0d771bc..1ef9f2b 100644
--- a/tc/m_mirred.c
+++ b/tc/m_mirred.c
@@ -45,7 +45,7 @@
 	exit(-1);
 }
 
-char *mirred_n2a(int action)
+static const char *mirred_n2a(int action)
 {
 	switch (action) {
 	case TCA_EGRESS_REDIR:
@@ -61,8 +61,9 @@
 	}
 }
 
-int
-parse_egress(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
+static int
+parse_egress(struct action_util *a, int *argc_p, char ***argv_p,
+	     int tca_id, struct nlmsghdr *n)
 {
 
 	int argc = *argc_p;
@@ -205,8 +206,9 @@
 }
 
 
-int
-parse_mirred(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
+static int
+parse_mirred(struct action_util *a, int *argc_p, char ***argv_p,
+	     int tca_id, struct nlmsghdr *n)
 {
 
 	int argc = *argc_p;
@@ -245,7 +247,7 @@
 
 }
 
-int
+static int
 print_mirred(struct action_util *au,FILE * f, struct rtattr *arg)
 {
 	struct tc_mirred *p;
diff --git a/tc/m_pedit.c b/tc/m_pedit.c
index 7499846..452d96f 100644
--- a/tc/m_pedit.c
+++ b/tc/m_pedit.c
@@ -73,8 +73,7 @@
 
 }
 
-struct m_pedit_util
-*get_pedit_kind(char *str)
+static struct m_pedit_util *get_pedit_kind(const char *str)
 {
 	static void *pBODY;
 	void *dlh;
@@ -411,7 +410,7 @@
 	return res;
 }
 
-int
+static int
 parse_munge(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel)
 {
 	struct tc_pedit_key tkey;
diff --git a/tc/m_police.c b/tc/m_police.c
index c3869b6..53cbefc 100644
--- a/tc/m_police.c
+++ b/tc/m_police.c
@@ -50,7 +50,7 @@
 	fprintf(stderr, "Illegal \"%s\"\n", arg);
 }
 
-char *police_action_n2a(int action, char *buf, int len)
+static const char *police_action_n2a(int action, char *buf, int len)
 {
 	switch (action) {
 	case -1:
@@ -72,7 +72,7 @@
 	}
 }
 
-int police_action_a2n(char *arg, int *result)
+static int police_action_a2n(const char *arg, int *result)
 {
 	int res;
 
@@ -100,7 +100,7 @@
 }
 
 
-int get_police_result(int *action, int *result, char *arg)
+static int get_police_result(int *action, int *result, char *arg)
 {
 	char *p = strchr(arg, '/');
 
diff --git a/tc/m_xt.c b/tc/m_xt.c
index bcc4d75..3edf520 100644
--- a/tc/m_xt.c
+++ b/tc/m_xt.c
@@ -76,7 +76,7 @@
 /*
  * we may need to check for version mismatch
 */
-int
+static int
 build_st(struct xtables_target *target, struct xt_entry_target *t)
 {
 
@@ -98,7 +98,7 @@
 
 }
 
-inline void set_lib_dir(void)
+static void set_lib_dir(void)
 {
 
 	lib_dir = getenv("XTABLES_LIBDIR");
@@ -118,6 +118,7 @@
 	struct xtables_target *m = NULL;
 	struct ipt_entry fw;
 	struct rtattr *tail;
+
 	int c;
 	int rargc = *argc_p;
 	char **argv = *argv_p;
@@ -126,6 +127,7 @@
 	int size = 0;
 	int iok = 0, ok = 0;
 	__u32 hook = 0, index = 0;
+	struct option *opts = NULL;
 
 	xtables_init_all(&tcipt_globals, NFPROTO_IPV4);
 	set_lib_dir();
@@ -158,14 +160,22 @@
 					printf(" %s error \n", m->name);
 					return -1;
 				}
-				tcipt_globals.opts =
-				    xtables_merge_options(
 #if (XTABLES_VERSION_CODE >= 6)
-				        tcipt_globals.orig_opts,
+			opts = xtables_options_xfrm(tcipt_globals.orig_opts,
+						    tcipt_globals.opts,
+						    m->x6_options,
+						    &m->option_offset);
+#else
+			opts = xtables_merge_options(tcipt_globals.orig_opts,
+						     tcipt_globals.opts,
+						     m->extra_opts,
+						     &m->option_offset);
 #endif
-				        tcipt_globals.opts,
-				        m->extra_opts,
-				        &m->option_offset);
+			if (opts == NULL) {
+				fprintf(stderr, " failed to find aditional options for target %s\n\n", optarg);
+				return -1;
+			} else
+				tcipt_globals.opts = opts;
 			} else {
 				fprintf(stderr," failed to find target %s\n\n", optarg);
 				return -1;
@@ -175,17 +185,21 @@
 
 		default:
 			memset(&fw, 0, sizeof (fw));
-			if (m) {
-				m->parse(c - m->option_offset, argv, 0,
-					 &m->tflags, NULL, &m->t);
+#if (XTABLES_VERSION_CODE >= 6)
+		if (m != NULL && m->x6_parse != NULL ) {
+			xtables_option_tpcall(c, argv, 0 , m, NULL);
+#else
+		if (m != NULL && m->parse != NULL ) {
+			m->parse(c - m->option_offset, argv, 0, &m->tflags,
+				 NULL, &m->t);
+#endif
 			} else {
-				fprintf(stderr," failed to find target %s\n\n", optarg);
+				fprintf(stderr,"failed to find target %s\n\n", optarg);
 				return -1;
 
 			}
 			ok++;
 			break;
-
 		}
 	}
 
@@ -208,8 +222,13 @@
 	}
 
 	/* check that we passed the correct parameters to the target */
+#if (XTABLES_VERSION_CODE >= 6)
+	if (m)
+		xtables_option_tfcall(m);
+#else
 	if (m && m->final_check)
 		m->final_check(m->tflags);
+#endif
 
 	{
 		struct tcmsg *t = NLMSG_DATA(n);
@@ -271,6 +290,7 @@
 {
 	struct rtattr *tb[TCA_IPT_MAX + 1];
 	struct xt_entry_target *t = NULL;
+	struct option *opts = NULL;
 
 	if (arg == NULL)
 		return -1;
@@ -309,14 +329,22 @@
 				return -1;
 			}
 
-			tcipt_globals.opts =
-			    xtables_merge_options(
 #if (XTABLES_VERSION_CODE >= 6)
-				                  tcipt_globals.orig_opts,
+		opts = xtables_options_xfrm(tcipt_globals.orig_opts,
+					    tcipt_globals.opts,
+					    m->x6_options,
+					    &m->option_offset);
+#else
+		opts = xtables_merge_options(tcipt_globals.orig_opts,
+					     tcipt_globals.opts,
+					     m->extra_opts,
+					     &m->option_offset);
 #endif
-				                  tcipt_globals.opts,
-			                          m->extra_opts,
-			                          &m->option_offset);
+	if (opts == NULL) {
+		fprintf(stderr, " failed to find aditional options for target %s\n\n", optarg);
+		return -1;
+	} else
+		tcipt_globals.opts = opts;
 		} else {
 			fprintf(stderr, " failed to find target %s\n\n",
 				t->u.user.name);
@@ -355,4 +383,3 @@
         .parse_aopt = parse_ipt,
         .print_aopt = print_ipt,
 };
-
diff --git a/tc/q_mqprio.c b/tc/q_mqprio.c
index b2d8c12..fa1022b 100644
--- a/tc/q_mqprio.c
+++ b/tc/q_mqprio.c
@@ -104,7 +104,7 @@
 	return 0;
 }
 
-int mqprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+static int mqprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 {
 	int i;
 	struct tc_mqprio_qopt *qopt;
diff --git a/tc/q_multiq.c b/tc/q_multiq.c
index 7ff9e74..546ae01 100644
--- a/tc/q_multiq.c
+++ b/tc/q_multiq.c
@@ -61,7 +61,7 @@
 	return 0;
 }
 
-int multiq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+static int multiq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 {
 	struct tc_multiq_qopt *qopt;
 
diff --git a/tc/q_netem.c b/tc/q_netem.c
index f8489e9..9dd8712 100644
--- a/tc/q_netem.c
+++ b/tc/q_netem.c
@@ -87,12 +87,12 @@
 	return 0;
 }
 
-void print_percent(char *buf, int len, __u32 per)
+static void print_percent(char *buf, int len, __u32 per)
 {
 	snprintf(buf, len, "%g%%", 100. * (double) per / max_percent_value);
 }
 
-char * sprint_percent(__u32 per, char *buf)
+static char * sprint_percent(__u32 per, char *buf)
 {
 	print_percent(buf, SPRINT_BSIZE-1, per);
 	return buf;
@@ -147,6 +147,8 @@
 }
 
 #define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isdigit(argv[1][0]))
+#define NEXT_IS_SIGNED_NUMBER() \
+	(NEXT_ARG_OK() && (isdigit(argv[1][0]) || argv[1][0] == '-'))
 
 /* Adjust for the fact that psched_ticks aren't always usecs
    (based on kernel PSCHED_CLOCK configuration */
@@ -393,7 +395,7 @@
 				explain1("rate");
 				return -1;
 			}
-			if (NEXT_IS_NUMBER()) {
+			if (NEXT_IS_SIGNED_NUMBER()) {
 				NEXT_ARG();
 				if (get_s32(&rate.packet_overhead, *argv, 0)) {
 					explain1("rate");
@@ -407,7 +409,7 @@
 					return -1;
 				}
 			}
-			if (NEXT_IS_NUMBER()) {
+			if (NEXT_IS_SIGNED_NUMBER()) {
 				NEXT_ARG();
 				if (get_s32(&rate.cell_overhead, *argv, 0)) {
 					explain1("rate");
diff --git a/tc/q_rr.c b/tc/q_rr.c
index 79072ad..e8a9165 100644
--- a/tc/q_rr.c
+++ b/tc/q_rr.c
@@ -88,7 +88,7 @@
 	return 0;
 }
 
-int rr_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+static int rr_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 {
 	int i;
 	struct tc_prio_qopt *qopt;
diff --git a/tc/tc_class.c b/tc/tc_class.c
index 95bf615..6c0850d 100644
--- a/tc/tc_class.c
+++ b/tc/tc_class.c
@@ -40,7 +40,7 @@
 	return;
 }
 
-int tc_class_modify(int cmd, unsigned flags, int argc, char **argv)
+static int tc_class_modify(int cmd, unsigned flags, int argc, char **argv)
 {
 	struct {
 		struct nlmsghdr 	n;
@@ -232,7 +232,7 @@
 }
 
 
-int tc_class_list(int argc, char **argv)
+static int tc_class_list(int argc, char **argv)
 {
 	struct tcmsg t;
 	char d[16];
diff --git a/tc/tc_core.c b/tc/tc_core.c
index 9a0ff39..85b072e 100644
--- a/tc/tc_core.c
+++ b/tc/tc_core.c
@@ -76,7 +76,7 @@
  * (as the table will always be aligned for 48 bytes).
  *  --Hawk, d.7/11-2004. <hawk@diku.dk>
  */
-unsigned tc_align_to_atm(unsigned size)
+static unsigned tc_align_to_atm(unsigned size)
 {
 	int linksize, cells;
 	cells = size / ATM_CELL_PAYLOAD;
@@ -87,7 +87,7 @@
 	return linksize;
 }
 
-unsigned tc_adjust_size(unsigned sz, unsigned mpu, enum link_layer linklayer)
+static unsigned tc_adjust_size(unsigned sz, unsigned mpu, enum link_layer linklayer)
 {
 	if (sz < mpu)
 		sz = mpu;
@@ -181,7 +181,7 @@
 	return 0;
 }
 
-int tc_core_init()
+int tc_core_init(void)
 {
 	FILE *fp;
 	__u32 clock_res;
diff --git a/tc/tc_filter.c b/tc/tc_filter.c
index c9e09d8..c3f2d5f 100644
--- a/tc/tc_filter.c
+++ b/tc/tc_filter.c
@@ -45,7 +45,7 @@
 }
 
 
-int tc_filter_modify(int cmd, unsigned flags, int argc, char **argv)
+static int tc_filter_modify(int cmd, unsigned flags, int argc, char **argv)
 {
 	struct {
 		struct nlmsghdr 	n;
@@ -260,7 +260,7 @@
 }
 
 
-int tc_filter_list(int argc, char **argv)
+static int tc_filter_list(int argc, char **argv)
 {
 	struct tcmsg t;
 	char d[16];
diff --git a/tc/tc_monitor.c b/tc/tc_monitor.c
index bf58744..0efe034 100644
--- a/tc/tc_monitor.c
+++ b/tc/tc_monitor.c
@@ -35,7 +35,8 @@
 }
 
 
-int accept_tcmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static int accept_tcmsg(const struct sockaddr_nl *who,
+			struct nlmsghdr *n, void *arg)
 {
 	FILE *fp = (FILE*)arg;
 
diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c
index c8d7335..f3bf5b5 100644
--- a/tc/tc_qdisc.c
+++ b/tc/tc_qdisc.c
@@ -44,7 +44,7 @@
 	return -1;
 }
 
-int tc_qdisc_modify(int cmd, unsigned flags, int argc, char **argv)
+static int tc_qdisc_modify(int cmd, unsigned flags, int argc, char **argv)
 {
 	struct qdisc_util *q = NULL;
 	struct tc_estimator est;
@@ -277,7 +277,7 @@
 }
 
 
-int tc_qdisc_list(int argc, char **argv)
+static int tc_qdisc_list(int argc, char **argv)
 {
 	struct tcmsg t;
 	char d[16];
diff --git a/tc/tc_util.c b/tc/tc_util.c
index ccf8fa4..8e62a01 100644
--- a/tc/tc_util.c
+++ b/tc/tc_util.c
@@ -167,35 +167,6 @@
 	return -1;
 }
 
-int get_rate_and_cell(unsigned *rate, int *cell_log, char *str)
-{
-	char * slash = strchr(str, '/');
-
-	if (slash)
-		*slash = 0;
-
-	if (get_rate(rate, str))
-		return -1;
-
-	if (slash) {
-		int cell;
-		int i;
-
-		if (get_integer(&cell, slash+1, 0))
-			return -1;
-		*slash = '/';
-
-		for (i=0; i<32; i++) {
-			if ((1<<i) == cell) {
-				*cell_log = i;
-				return 0;
-			}
-		}
-		return -1;
-	}
-	return 0;
-}
-
 void print_rate(char *buf, int len, __u32 rate)
 {
 	double tmp = (double)rate*8;
diff --git a/tc/tc_util.h b/tc/tc_util.h
index 6b3ed1e..4f54436 100644
--- a/tc/tc_util.h
+++ b/tc/tc_util.h
@@ -97,5 +97,6 @@
 extern int  tc_print_ipt(FILE *f, const struct rtattr *tb);
 extern int  parse_action(int *, char ***, int, struct nlmsghdr *);
 extern void print_tm(FILE *f, const struct tcf_t *tm);
+extern int prio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt);
 
 #endif