naccyde / rpms / iproute

Forked from rpms/iproute 9 months ago
Clone

Blame SOURCES/iproute2-3.10.0-address.patch

a4b897
commit 9764c8d5063de9d76326f028fce66f6e4e49bb5a
a4b897
Author: Pavel Šimerda <psimerda@redhat.com>
a4b897
Date:   Thu May 28 12:17:39 2015 +0200
a4b897
a4b897
    backport selected ip-link/ip-address features and documentation
a4b897
a4b897
diff --git a/ip/ip.c b/ip/ip.c
a4b897
index 86f8b45..7f2c206 100644
a4b897
--- a/ip/ip.c
a4b897
+++ b/ip/ip.c
a4b897
@@ -22,8 +22,11 @@
a4b897
 #include "SNAPSHOT.h"
a4b897
 #include "utils.h"
a4b897
 #include "ip_common.h"
a4b897
+#include "namespace.h"
a4b897
 
a4b897
 int preferred_family = AF_UNSPEC;
a4b897
+int human_readable = 0;
a4b897
+int use_iec = 0;
a4b897
 int show_stats = 0;
a4b897
 int show_details = 0;
a4b897
 int resolve_hosts = 0;
a4b897
@@ -33,6 +36,7 @@ char * _SL_ = NULL;
a4b897
 int force = 0;
a4b897
 int max_flush_loops = 10;
a4b897
 int batch_mode = 0;
a4b897
+bool do_all = false;
a4b897
 
a4b897
 struct rtnl_handle rth = { .fd = -1 };
a4b897
 
a4b897
@@ -47,11 +51,12 @@ static void usage(void)
a4b897
 "                   tunnel | tuntap | maddr | mroute | mrule | monitor | xfrm |\n"
a4b897
 "                   netns | l2tp | tcp_metrics | token }\n"
a4b897
 "       OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
a4b897
+"                    -h[uman-readable] | -iec |\n"
a4b897
 "                    -f[amily] { inet | inet6 | ipx | dnet | bridge | link } |\n"
a4b897
 "                    -4 | -6 | -I | -D | -B | -0 |\n"
a4b897
 "                    -l[oops] { maximum-addr-flush-attempts } |\n"
a4b897
 "                    -o[neline] | -t[imestamp] | -b[atch] [filename] |\n"
a4b897
-"                    -rc[vbuf] [size]}\n");
a4b897
+"                    -rc[vbuf] [size] | -n[etns] name | -a[ll] }\n");
a4b897
 	exit(-1);
a4b897
 }
a4b897
 
a4b897
@@ -65,7 +70,7 @@ static const struct cmd {
a4b897
 	const char *cmd;
a4b897
 	int (*func)(int argc, char **argv);
a4b897
 } cmds[] = {
a4b897
-	{ "address", 	do_ipaddr },
a4b897
+	{ "address",	do_ipaddr },
a4b897
 	{ "addrlabel",	do_ipaddrlabel },
a4b897
 	{ "maddress",	do_multiaddr },
a4b897
 	{ "route",	do_iproute },
a4b897
@@ -212,6 +217,11 @@ int main(int argc, char **argv)
a4b897
 			preferred_family = AF_DECnet;
a4b897
 		} else if (strcmp(opt, "-B") == 0) {
a4b897
 			preferred_family = AF_BRIDGE;
a4b897
+		} else if (matches(opt, "-human") == 0 ||
a4b897
+			   matches(opt, "-human-readable") == 0) {
a4b897
+			++human_readable;
a4b897
+		} else if (matches(opt, "-iec") == 0) {
a4b897
+			++use_iec;
a4b897
 		} else if (matches(opt, "-stats") == 0 ||
a4b897
 			   matches(opt, "-statistics") == 0) {
a4b897
 			++show_stats;
a4b897
@@ -253,6 +263,12 @@ int main(int argc, char **argv)
a4b897
 			rcvbuf = size;
a4b897
 		} else if (matches(opt, "-help") == 0) {
a4b897
 			usage();
a4b897
+		} else if (matches(opt, "-netns") == 0) {
a4b897
+			NEXT_ARG();
a4b897
+			if (netns_switch(argv[1]))
a4b897
+				exit(-1);
a4b897
+		} else if (matches(opt, "-all") == 0) {
a4b897
+			do_all = true;
a4b897
 		} else {
a4b897
 			fprintf(stderr, "Option \"%s\" is unknown, try \"ip -help\".\n", opt);
a4b897
 			exit(-1);
a4b897
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
a4b897
index 5b9a438..c99a078 100644
a4b897
--- a/ip/ipaddress.c
a4b897
+++ b/ip/ipaddress.c
a4b897
@@ -28,6 +28,7 @@
a4b897
 #include <linux/netdevice.h>
a4b897
 #include <linux/if_arp.h>
a4b897
 #include <linux/sockios.h>
a4b897
+#include <linux/net_namespace.h>
a4b897
 
a4b897
 #include "rt_names.h"
a4b897
 #include "utils.h"
a4b897
@@ -69,7 +70,7 @@ static void usage(void)
a4b897
 	}
a4b897
 	fprintf(stderr, "Usage: ip addr {add|change|replace} IFADDR dev STRING [ LIFETIME ]\n");
a4b897
 	fprintf(stderr, "                                                      [ CONFFLAG-LIST ]\n");
a4b897
-	fprintf(stderr, "       ip addr del IFADDR dev STRING\n");
a4b897
+	fprintf(stderr, "       ip addr del IFADDR dev STRING [mngtmpaddr]\n");
a4b897
 	fprintf(stderr, "       ip addr {show|save|flush} [ dev STRING ] [ scope SCOPE-ID ]\n");
a4b897
 	fprintf(stderr, "                            [ to PREFIX ] [ FLAG-LIST ] [ label PATTERN ] [up]\n");
a4b897
 	fprintf(stderr, "       ip addr {showdump|restore}\n");
a4b897
@@ -82,7 +83,7 @@ static void usage(void)
a4b897
 	fprintf(stderr, "           tentative | deprecated | dadfailed | temporary |\n");
a4b897
 	fprintf(stderr, "           CONFFLAG-LIST ]\n");
a4b897
 	fprintf(stderr, "CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG\n");
a4b897
-	fprintf(stderr, "CONFFLAG  := [ home | nodad ]\n");
a4b897
+	fprintf(stderr, "CONFFLAG  := [ home | nodad | mngtmpaddr | noprefixroute ]\n");
a4b897
 	fprintf(stderr, "LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n");
a4b897
 	fprintf(stderr, "LFT := forever | SECONDS\n");
a4b897
 
a4b897
@@ -223,12 +224,36 @@ static void print_linktype(FILE *fp, struct rtattr *tb)
a4b897
 	}
a4b897
 }
a4b897
 
a4b897
+static void print_af_spec(FILE *fp, struct rtattr *af_spec_attr)
a4b897
+{
a4b897
+	struct rtattr *inet6_attr;
a4b897
+	struct rtattr *tb[IFLA_INET6_MAX + 1];
a4b897
+
a4b897
+	inet6_attr = parse_rtattr_one_nested(AF_INET6, af_spec_attr);
a4b897
+	if (!inet6_attr)
a4b897
+		return;
a4b897
+
a4b897
+	parse_rtattr_nested(tb, IFLA_INET6_MAX, inet6_attr);
a4b897
+
a4b897
+	if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
a4b897
+		switch (rta_getattr_u8(tb[IFLA_INET6_ADDR_GEN_MODE])) {
a4b897
+		case IN6_ADDR_GEN_MODE_EUI64:
a4b897
+			fprintf(fp, "addrgenmode eui64 ");
a4b897
+			break;
a4b897
+		case IN6_ADDR_GEN_MODE_NONE:
a4b897
+			fprintf(fp, "addrgenmode none ");
a4b897
+			break;
a4b897
+		}
a4b897
+	}
a4b897
+}
a4b897
+
a4b897
 static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
a4b897
 {
a4b897
 	struct ifla_vf_mac *vf_mac;
a4b897
 	struct ifla_vf_vlan *vf_vlan;
a4b897
 	struct ifla_vf_tx_rate *vf_tx_rate;
a4b897
 	struct ifla_vf_spoofchk *vf_spoofchk;
a4b897
+	struct ifla_vf_link_state *vf_linkstate;
a4b897
 	struct rtattr *vf[IFLA_VF_MAX+1];
a4b897
 	struct rtattr *tmp;
a4b897
 	SPRINT_BUF(b1);
a4b897
@@ -255,6 +280,20 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
a4b897
 	else
a4b897
 		vf_spoofchk = RTA_DATA(vf[IFLA_VF_SPOOFCHK]);
a4b897
 
a4b897
+	if (vf_spoofchk) {
a4b897
+		/* Check if the link state vf info type is supported by
a4b897
+		 * this kernel.
a4b897
+		 */
a4b897
+		tmp = (struct rtattr *)((char *)vf[IFLA_VF_SPOOFCHK] +
a4b897
+				vf[IFLA_VF_SPOOFCHK]->rta_len);
a4b897
+
a4b897
+		if (tmp->rta_type != IFLA_VF_LINK_STATE)
a4b897
+			vf_linkstate = NULL;
a4b897
+		else
a4b897
+			vf_linkstate = RTA_DATA(vf[IFLA_VF_LINK_STATE]);
a4b897
+	} else
a4b897
+		vf_linkstate = NULL;
a4b897
+
a4b897
 	fprintf(fp, "\n    vf %d MAC %s", vf_mac->vf,
a4b897
 		ll_addr_n2a((unsigned char *)&vf_mac->mac,
a4b897
 		ETH_ALEN, 0, b1, sizeof(b1)));
a4b897
@@ -270,99 +309,203 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
a4b897
 		else
a4b897
 			fprintf(fp, ", spoof checking off");
a4b897
 	}
a4b897
+	if (vf_linkstate) {
a4b897
+		if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_AUTO)
a4b897
+			fprintf(fp, ", link-state auto");
a4b897
+		else if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_ENABLE)
a4b897
+			fprintf(fp, ", link-state enable");
a4b897
+		else
a4b897
+			fprintf(fp, ", link-state disable");
a4b897
+	}
a4b897
 }
a4b897
 
a4b897
-static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s) {
a4b897
-	fprintf(fp, "%s", _SL_);
a4b897
+static void print_num(FILE *fp, unsigned width, uint64_t count)
a4b897
+{
a4b897
+	const char *prefix = "kMGTPE";
a4b897
+	const unsigned int base = use_iec ? 1024 : 1000;
a4b897
+	uint64_t powi = 1;
a4b897
+	uint16_t powj = 1;
a4b897
+	uint8_t precision = 2;
a4b897
+	char buf[64];
a4b897
+
a4b897
+	if (!human_readable || count < base) {
a4b897
+		fprintf(fp, "%-*"PRIu64" ", width, count);
a4b897
+		return;
a4b897
+	}
a4b897
+
a4b897
+	/* increase value by a factor of 1000/1024 and print
a4b897
+	 * if result is something a human can read */
a4b897
+	for(;;) {
a4b897
+		powi *= base;
a4b897
+		if (count / base < powi)
a4b897
+			break;
a4b897
+
a4b897
+		if (!prefix[1])
a4b897
+			break;
a4b897
+		++prefix;
a4b897
+	}
a4b897
+
a4b897
+	/* try to guess a good number of digits for precision */
a4b897
+	for (; precision > 0; precision--) {
a4b897
+		powj *= 10;
a4b897
+		if (count / powi < powj)
a4b897
+			break;
a4b897
+	}
a4b897
+
a4b897
+	snprintf(buf, sizeof(buf), "%.*f%c%s", precision,
a4b897
+		(double) count / powi, *prefix, use_iec ? "i" : "");
a4b897
+
a4b897
+	fprintf(fp, "%-*s ", width, buf);
a4b897
+}
a4b897
+
a4b897
+static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s,
a4b897
+                               const struct rtattr *carrier_changes)
a4b897
+{
a4b897
+	/* RX stats */
a4b897
 	fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
a4b897
 		s->rx_compressed ? "compressed" : "", _SL_);
a4b897
-	fprintf(fp, "    %-10"PRIu64" %-8"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
a4b897
-		(uint64_t)s->rx_bytes,
a4b897
-		(uint64_t)s->rx_packets,
a4b897
-		(uint64_t)s->rx_errors,
a4b897
-		(uint64_t)s->rx_dropped,
a4b897
-		(uint64_t)s->rx_over_errors,
a4b897
-		(uint64_t)s->multicast);
a4b897
+
a4b897
+	fprintf(fp, "    ");
a4b897
+	print_num(fp, 10, s->rx_bytes);
a4b897
+	print_num(fp, 8, s->rx_packets);
a4b897
+	print_num(fp, 7, s->rx_errors);
a4b897
+	print_num(fp, 7, s->rx_dropped);
a4b897
+	print_num(fp, 7, s->rx_over_errors);
a4b897
+	print_num(fp, 7, s->multicast);
a4b897
 	if (s->rx_compressed)
a4b897
-		fprintf(fp, " %-7"PRIu64"",
a4b897
-			(uint64_t)s->rx_compressed);
a4b897
+		print_num(fp, 7, s->rx_compressed);
a4b897
+
a4b897
+	/* RX error stats */
a4b897
 	if (show_stats > 1) {
a4b897
 		fprintf(fp, "%s", _SL_);
a4b897
-		fprintf(fp, "    RX errors: length  crc     frame   fifo    missed%s", _SL_);
a4b897
-		fprintf(fp, "               %-7"PRIu64"  %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
a4b897
-			(uint64_t)s->rx_length_errors,
a4b897
-			(uint64_t)s->rx_crc_errors,
a4b897
-			(uint64_t)s->rx_frame_errors,
a4b897
-			(uint64_t)s->rx_fifo_errors,
a4b897
-			(uint64_t)s->rx_missed_errors);
a4b897
+		fprintf(fp, "    RX errors: length   crc     frame   fifo    missed%s", _SL_);
a4b897
+
a4b897
+		fprintf(fp, "               ");
a4b897
+		print_num(fp, 8, s->rx_length_errors);
a4b897
+		print_num(fp, 7, s->rx_crc_errors);
a4b897
+		print_num(fp, 7, s->rx_frame_errors);
a4b897
+		print_num(fp, 7, s->rx_fifo_errors);
a4b897
+		print_num(fp, 7, s->rx_missed_errors);
a4b897
 	}
a4b897
 	fprintf(fp, "%s", _SL_);
a4b897
+
a4b897
+	/* TX stats */
a4b897
 	fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
a4b897
-		(uint64_t)s->tx_compressed ? "compressed" : "", _SL_);
a4b897
-	fprintf(fp, "    %-10"PRIu64" %-8"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
a4b897
-		(uint64_t)s->tx_bytes,
a4b897
-		(uint64_t)s->tx_packets,
a4b897
-		(uint64_t)s->tx_errors,
a4b897
-		(uint64_t)s->tx_dropped,
a4b897
-		(uint64_t)s->tx_carrier_errors,
a4b897
-		(uint64_t)s->collisions);
a4b897
+		s->tx_compressed ? "compressed" : "", _SL_);
a4b897
+
a4b897
+
a4b897
+	fprintf(fp, "    ");
a4b897
+	print_num(fp, 10, s->tx_bytes);
a4b897
+	print_num(fp, 8, s->tx_packets);
a4b897
+	print_num(fp, 7, s->tx_errors);
a4b897
+	print_num(fp, 7, s->tx_dropped);
a4b897
+	print_num(fp, 7, s->tx_carrier_errors);
a4b897
+	print_num(fp, 7, s->collisions);
a4b897
 	if (s->tx_compressed)
a4b897
-		fprintf(fp, " %-7"PRIu64"",
a4b897
-			(uint64_t)s->tx_compressed);
a4b897
+		print_num(fp, 7, s->tx_compressed);
a4b897
+
a4b897
+	/* TX error stats */
a4b897
 	if (show_stats > 1) {
a4b897
 		fprintf(fp, "%s", _SL_);
a4b897
-		fprintf(fp, "    TX errors: aborted fifo    window  heartbeat%s", _SL_);
a4b897
-		fprintf(fp, "               %-7"PRIu64"  %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"",
a4b897
-			(uint64_t)s->tx_aborted_errors,
a4b897
-			(uint64_t)s->tx_fifo_errors,
a4b897
-			(uint64_t)s->tx_window_errors,
a4b897
-			(uint64_t)s->tx_heartbeat_errors);
a4b897
+		fprintf(fp, "    TX errors: aborted  fifo   window heartbeat");
a4b897
+                if (carrier_changes)
a4b897
+			fprintf(fp, " transns");
a4b897
+		fprintf(fp, "%s", _SL_);
a4b897
+
a4b897
+		fprintf(fp, "               ");
a4b897
+		print_num(fp, 8, s->tx_aborted_errors);
a4b897
+		print_num(fp, 7, s->tx_fifo_errors);
a4b897
+		print_num(fp, 7, s->tx_window_errors);
a4b897
+		print_num(fp, 7, s->tx_heartbeat_errors);
a4b897
+		if (carrier_changes)
a4b897
+			print_num(fp, 7, *(uint32_t*)RTA_DATA(carrier_changes));
a4b897
 	}
a4b897
 }
a4b897
 
a4b897
-static void print_link_stats(FILE *fp, const struct rtnl_link_stats *s)
a4b897
+static void print_link_stats32(FILE *fp, const struct rtnl_link_stats *s,
a4b897
+			       const struct rtattr *carrier_changes)
a4b897
 {
a4b897
-	fprintf(fp, "%s", _SL_);
a4b897
+	/* RX stats */
a4b897
 	fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
a4b897
 		s->rx_compressed ? "compressed" : "", _SL_);
a4b897
-	fprintf(fp, "    %-10u %-8u %-7u %-7u %-7u %-7u",
a4b897
-		s->rx_bytes, s->rx_packets, s->rx_errors,
a4b897
-		s->rx_dropped, s->rx_over_errors,
a4b897
-		s->multicast
a4b897
-		);
a4b897
+
a4b897
+
a4b897
+	fprintf(fp, "    ");
a4b897
+	print_num(fp, 10, s->rx_bytes);
a4b897
+	print_num(fp, 8, s->rx_packets);
a4b897
+	print_num(fp, 7, s->rx_errors);
a4b897
+	print_num(fp, 7, s->rx_dropped);
a4b897
+	print_num(fp, 7, s->rx_over_errors);
a4b897
+	print_num(fp, 7, s->multicast);
a4b897
 	if (s->rx_compressed)
a4b897
-		fprintf(fp, " %-7u", s->rx_compressed);
a4b897
+		print_num(fp, 7, s->rx_compressed);
a4b897
+
a4b897
+	/* RX error stats */
a4b897
 	if (show_stats > 1) {
a4b897
 		fprintf(fp, "%s", _SL_);
a4b897
-		fprintf(fp, "    RX errors: length  crc     frame   fifo    missed%s", _SL_);
a4b897
-		fprintf(fp, "               %-7u  %-7u %-7u %-7u %-7u",
a4b897
-			s->rx_length_errors,
a4b897
-			s->rx_crc_errors,
a4b897
-			s->rx_frame_errors,
a4b897
-			s->rx_fifo_errors,
a4b897
-			s->rx_missed_errors
a4b897
-			);
a4b897
+		fprintf(fp, "    RX errors: length   crc     frame   fifo    missed%s", _SL_);
a4b897
+		fprintf(fp, "               ");
a4b897
+		print_num(fp, 8, s->rx_length_errors);
a4b897
+		print_num(fp, 7, s->rx_crc_errors);
a4b897
+		print_num(fp, 7, s->rx_frame_errors);
a4b897
+		print_num(fp, 7, s->rx_fifo_errors);
a4b897
+		print_num(fp, 7, s->rx_missed_errors);
a4b897
 	}
a4b897
 	fprintf(fp, "%s", _SL_);
a4b897
+
a4b897
+	/* TX stats */
a4b897
 	fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
a4b897
 		s->tx_compressed ? "compressed" : "", _SL_);
a4b897
-	fprintf(fp, "    %-10u %-8u %-7u %-7u %-7u %-7u",
a4b897
-		s->tx_bytes, s->tx_packets, s->tx_errors,
a4b897
-		s->tx_dropped, s->tx_carrier_errors, s->collisions);
a4b897
+
a4b897
+	fprintf(fp, "    ");
a4b897
+	print_num(fp, 10, s->tx_bytes);
a4b897
+	print_num(fp, 8, s->tx_packets);
a4b897
+	print_num(fp, 7, s->tx_errors);
a4b897
+	print_num(fp, 7, s->tx_dropped);
a4b897
+	print_num(fp, 7, s->tx_carrier_errors);
a4b897
+	print_num(fp, 7, s->collisions);
a4b897
 	if (s->tx_compressed)
a4b897
-		fprintf(fp, " %-7u", s->tx_compressed);
a4b897
+		print_num(fp, 7, s->tx_compressed);
a4b897
+
a4b897
+	/* TX error stats */
a4b897
 	if (show_stats > 1) {
a4b897
 		fprintf(fp, "%s", _SL_);
a4b897
-		fprintf(fp, "    TX errors: aborted fifo    window  heartbeat%s", _SL_);
a4b897
-		fprintf(fp, "               %-7u  %-7u %-7u %-7u",
a4b897
-			s->tx_aborted_errors,
a4b897
-			s->tx_fifo_errors,
a4b897
-			s->tx_window_errors,
a4b897
-			s->tx_heartbeat_errors
a4b897
-			);
a4b897
+		fprintf(fp, "    TX errors: aborted  fifo   window heartbeat");
a4b897
+                if (carrier_changes)
a4b897
+			fprintf(fp, " transns");
a4b897
+		fprintf(fp, "%s", _SL_);
a4b897
+
a4b897
+		fprintf(fp, "               ");
a4b897
+		print_num(fp, 8, s->tx_aborted_errors);
a4b897
+		print_num(fp, 7, s->tx_fifo_errors);
a4b897
+		print_num(fp, 7, s->tx_window_errors);
a4b897
+		print_num(fp, 7, s->tx_heartbeat_errors);
a4b897
+		if (carrier_changes)
a4b897
+			print_num(fp, 7, *(uint32_t*)RTA_DATA(carrier_changes));
a4b897
 	}
a4b897
 }
a4b897
 
a4b897
+static void __print_link_stats(FILE *fp, struct rtattr **tb)
a4b897
+{
a4b897
+	if (tb[IFLA_STATS64])
a4b897
+		print_link_stats64(fp, RTA_DATA(tb[IFLA_STATS64]),
a4b897
+					tb[IFLA_CARRIER_CHANGES]);
a4b897
+	else if (tb[IFLA_STATS])
a4b897
+		print_link_stats32(fp, RTA_DATA(tb[IFLA_STATS]),
a4b897
+					tb[IFLA_CARRIER_CHANGES]);
a4b897
+}
a4b897
+
a4b897
+static void print_link_stats(FILE *fp, struct nlmsghdr *n)
a4b897
+{
a4b897
+	struct ifinfomsg *ifi = NLMSG_DATA(n);
a4b897
+	struct rtattr * tb[IFLA_MAX+1];
a4b897
+
a4b897
+	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi),
a4b897
+		     n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)));
a4b897
+	__print_link_stats(fp, tb);
a4b897
+	fprintf(fp, "%s", _SL_);
a4b897
+}
a4b897
+
a4b897
 int print_linkinfo(const struct sockaddr_nl *who,
a4b897
 		   struct nlmsghdr *n, void *arg)
a4b897
 {
a4b897
@@ -411,9 +554,13 @@ int print_linkinfo(const struct sockaddr_nl *who,
a4b897
 		if (iflink == 0)
a4b897
 			fprintf(fp, "@NONE: ");
a4b897
 		else {
a4b897
-			fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1));
a4b897
-			m_flag = ll_index_to_flags(iflink);
a4b897
-			m_flag = !(m_flag & IFF_UP);
a4b897
+			if (tb[IFLA_LINK_NETNSID])
a4b897
+				fprintf(fp, "@if%d: ", iflink);
a4b897
+			else {
a4b897
+				fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1));
a4b897
+				m_flag = ll_index_to_flags(iflink);
a4b897
+				m_flag = !(m_flag & IFF_UP);
a4b897
+			}
a4b897
 		}
a4b897
 	} else {
a4b897
 		fprintf(fp, ": ");
a4b897
@@ -461,26 +608,36 @@ int print_linkinfo(const struct sockaddr_nl *who,
a4b897
 		}
a4b897
 	}
a4b897
 
a4b897
-	if (do_link && tb[IFLA_PROMISCUITY] && show_details)
a4b897
+	if (tb[IFLA_LINK_NETNSID]) {
a4b897
+		int id = *(int*)RTA_DATA(tb[IFLA_LINK_NETNSID]);
a4b897
+
a4b897
+		if (id >= 0)
a4b897
+			fprintf(fp, " link-netnsid %d", id);
a4b897
+		else
a4b897
+			fprintf(fp, " link-netnsid unknown");
a4b897
+	}
a4b897
+
a4b897
+	if (tb[IFLA_PROMISCUITY] && show_details)
a4b897
 		fprintf(fp, " promiscuity %u ",
a4b897
 			*(int*)RTA_DATA(tb[IFLA_PROMISCUITY]));
a4b897
 
a4b897
-	if (do_link && tb[IFLA_LINKINFO] && show_details)
a4b897
+	if (tb[IFLA_LINKINFO] && show_details)
a4b897
 		print_linktype(fp, tb[IFLA_LINKINFO]);
a4b897
 
a4b897
-	if (do_link && tb[IFLA_IFALIAS]) {
a4b897
+	if (do_link && tb[IFLA_AF_SPEC] && show_details)
a4b897
+		print_af_spec(fp, tb[IFLA_AF_SPEC]);
a4b897
+
a4b897
+	if ((do_link || show_details) && tb[IFLA_IFALIAS]) {
a4b897
 		fprintf(fp, "%s    alias %s", _SL_,
a4b897
 			rta_getattr_str(tb[IFLA_IFALIAS]));
a4b897
 	}
a4b897
 
a4b897
 	if (do_link && show_stats) {
a4b897
-		if (tb[IFLA_STATS64])
a4b897
-			print_link_stats64(fp, RTA_DATA(tb[IFLA_STATS64]));
a4b897
-		else if (tb[IFLA_STATS])
a4b897
-			print_link_stats(fp, RTA_DATA(tb[IFLA_STATS]));
a4b897
+		fprintf(fp, "%s", _SL_);
a4b897
+		__print_link_stats(fp, tb);
a4b897
 	}
a4b897
 
a4b897
-	if (do_link && tb[IFLA_VFINFO_LIST] && tb[IFLA_NUM_VF]) {
a4b897
+	if ((do_link || show_details) && tb[IFLA_VFINFO_LIST] && tb[IFLA_NUM_VF]) {
a4b897
 		struct rtattr *i, *vflist = tb[IFLA_VFINFO_LIST];
a4b897
 		int rem = RTA_PAYLOAD(vflist);
a4b897
 		for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
a4b897
@@ -489,7 +646,7 @@ int print_linkinfo(const struct sockaddr_nl *who,
a4b897
 
a4b897
 	fprintf(fp, "\n");
a4b897
 	fflush(fp);
a4b897
-	return 0;
a4b897
+	return 1;
a4b897
 }
a4b897
 
a4b897
 static int flush_update(void)
a4b897
@@ -512,10 +669,17 @@ static int set_lifetime(unsigned int *lifetime, char *argv)
a4b897
 	return 0;
a4b897
 }
a4b897
 
a4b897
+static unsigned int get_ifa_flags(struct ifaddrmsg *ifa,
a4b897
+				  struct rtattr *ifa_flags_attr)
a4b897
+{
a4b897
+	return ifa_flags_attr ? rta_getattr_u32(ifa_flags_attr) :
a4b897
+				ifa->ifa_flags;
a4b897
+}
a4b897
+
a4b897
 int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
a4b897
 		   void *arg)
a4b897
 {
a4b897
-	FILE *fp = (FILE*)arg;
a4b897
+	FILE *fp = arg;
a4b897
 	struct ifaddrmsg *ifa = NLMSG_DATA(n);
a4b897
 	int len = n->nlmsg_len;
a4b897
 	int deprecated = 0;
a4b897
@@ -536,7 +700,10 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
a4b897
 	if (filter.flushb && n->nlmsg_type != RTM_NEWADDR)
a4b897
 		return 0;
a4b897
 
a4b897
-	parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
a4b897
+	parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa),
a4b897
+		     n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
a4b897
+
a4b897
+	ifa_flags = get_ifa_flags(ifa, rta_tb[IFA_FLAGS]);
a4b897
 
a4b897
 	if (!rta_tb[IFA_LOCAL])
a4b897
 		rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
a4b897
@@ -547,7 +714,7 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
a4b897
 		return 0;
a4b897
 	if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
a4b897
 		return 0;
a4b897
-	if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
a4b897
+	if ((filter.flags ^ ifa_flags) & filter.flagmask)
a4b897
 		return 0;
a4b897
 	if (filter.label) {
a4b897
 		SPRINT_BUF(b1);
a4b897
@@ -613,7 +780,8 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
a4b897
 					      abuf, sizeof(abuf)));
a4b897
 
a4b897
 		if (rta_tb[IFA_ADDRESS] == NULL ||
a4b897
-		    memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) {
a4b897
+		    memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]),
a4b897
+			   ifa->ifa_family == AF_INET ? 4 : 16) == 0) {
a4b897
 			fprintf(fp, "/%d ", ifa->ifa_prefixlen);
a4b897
 		} else {
a4b897
 			fprintf(fp, " peer %s/%d ",
a4b897
@@ -640,36 +808,43 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
a4b897
 				    abuf, sizeof(abuf)));
a4b897
 	}
a4b897
 	fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
a4b897
-	ifa_flags = ifa->ifa_flags;
a4b897
-	if (ifa->ifa_flags&IFA_F_SECONDARY) {
a4b897
+	if (ifa_flags & IFA_F_SECONDARY) {
a4b897
 		ifa_flags &= ~IFA_F_SECONDARY;
a4b897
 		if (ifa->ifa_family == AF_INET6)
a4b897
 			fprintf(fp, "temporary ");
a4b897
 		else
a4b897
 			fprintf(fp, "secondary ");
a4b897
 	}
a4b897
-	if (ifa->ifa_flags&IFA_F_TENTATIVE) {
a4b897
+	if (ifa_flags & IFA_F_TENTATIVE) {
a4b897
 		ifa_flags &= ~IFA_F_TENTATIVE;
a4b897
 		fprintf(fp, "tentative ");
a4b897
 	}
a4b897
-	if (ifa->ifa_flags&IFA_F_DEPRECATED) {
a4b897
+	if (ifa_flags & IFA_F_DEPRECATED) {
a4b897
 		ifa_flags &= ~IFA_F_DEPRECATED;
a4b897
 		deprecated = 1;
a4b897
 		fprintf(fp, "deprecated ");
a4b897
 	}
a4b897
-	if (ifa->ifa_flags&IFA_F_HOMEADDRESS) {
a4b897
+	if (ifa_flags & IFA_F_HOMEADDRESS) {
a4b897
 		ifa_flags &= ~IFA_F_HOMEADDRESS;
a4b897
 		fprintf(fp, "home ");
a4b897
 	}
a4b897
-	if (ifa->ifa_flags&IFA_F_NODAD) {
a4b897
+	if (ifa_flags & IFA_F_NODAD) {
a4b897
 		ifa_flags &= ~IFA_F_NODAD;
a4b897
 		fprintf(fp, "nodad ");
a4b897
 	}
a4b897
-	if (!(ifa->ifa_flags&IFA_F_PERMANENT)) {
a4b897
+	if (ifa_flags & IFA_F_MANAGETEMPADDR) {
a4b897
+		ifa_flags &= ~IFA_F_MANAGETEMPADDR;
a4b897
+		fprintf(fp, "mngtmpaddr ");
a4b897
+	}
a4b897
+	if (ifa_flags & IFA_F_NOPREFIXROUTE) {
a4b897
+		ifa_flags &= ~IFA_F_NOPREFIXROUTE;
a4b897
+		fprintf(fp, "noprefixroute ");
a4b897
+	}
a4b897
+	if (!(ifa_flags & IFA_F_PERMANENT)) {
a4b897
 		fprintf(fp, "dynamic ");
a4b897
 	} else
a4b897
 		ifa_flags &= ~IFA_F_PERMANENT;
a4b897
-	if (ifa->ifa_flags&IFA_F_DADFAILED) {
a4b897
+	if (ifa_flags & IFA_F_DADFAILED) {
a4b897
 		ifa_flags &= ~IFA_F_DADFAILED;
a4b897
 		fprintf(fp, "dadfailed ");
a4b897
 	}
a4b897
@@ -896,6 +1071,8 @@ static void ipaddr_filter(struct nlmsg_chain *linfo, struct nlmsg_chain *ainfo)
a4b897
 		for (a = ainfo->head; a; a = a->next) {
a4b897
 			struct nlmsghdr *n = &a->h;
a4b897
 			struct ifaddrmsg *ifa = NLMSG_DATA(n);
a4b897
+			struct rtattr *tb[IFA_MAX + 1];
a4b897
+			unsigned int ifa_flags;
a4b897
 
a4b897
 			if (ifa->ifa_index != ifi->ifi_index)
a4b897
 				continue;
a4b897
@@ -904,11 +1081,13 @@ static void ipaddr_filter(struct nlmsg_chain *linfo, struct nlmsg_chain *ainfo)
a4b897
 				continue;
a4b897
 			if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
a4b897
 				continue;
a4b897
-			if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
a4b897
+
a4b897
+			parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
a4b897
+			ifa_flags = get_ifa_flags(ifa, tb[IFA_FLAGS]);
a4b897
+
a4b897
+			if ((filter.flags ^ ifa_flags) & filter.flagmask)
a4b897
 				continue;
a4b897
 			if (filter.pfx.family || filter.label) {
a4b897
-				struct rtattr *tb[IFA_MAX+1];
a4b897
-				parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
a4b897
 				if (!tb[IFA_LOCAL])
a4b897
 					tb[IFA_LOCAL] = tb[IFA_ADDRESS];
a4b897
 
a4b897
@@ -1084,6 +1263,12 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
a4b897
 		} else if (strcmp(*argv, "nodad") == 0) {
a4b897
 			filter.flags |= IFA_F_NODAD;
a4b897
 			filter.flagmask |= IFA_F_NODAD;
a4b897
+		} else if (strcmp(*argv, "mngtmpaddr") == 0) {
a4b897
+			filter.flags |= IFA_F_MANAGETEMPADDR;
a4b897
+			filter.flagmask |= IFA_F_MANAGETEMPADDR;
a4b897
+		} else if (strcmp(*argv, "noprefixroute") == 0) {
a4b897
+			filter.flags |= IFA_F_NOPREFIXROUTE;
a4b897
+			filter.flagmask |= IFA_F_NOPREFIXROUTE;
a4b897
 		} else if (strcmp(*argv, "dadfailed") == 0) {
a4b897
 			filter.flags |= IFA_F_DADFAILED;
a4b897
 			filter.flagmask |= IFA_F_DADFAILED;
a4b897
@@ -1163,11 +1348,15 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
a4b897
 	}
a4b897
 
a4b897
 	for (l = linfo.head; l; l = l->next) {
a4b897
-		if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
a4b897
+		int res = 0;
a4b897
+
a4b897
+		if (no_link || (res = print_linkinfo(NULL, &l->h, stdout)) >= 0) {
a4b897
 			struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
a4b897
 			if (filter.family != AF_PACKET)
a4b897
 				print_selected_addrinfo(ifi->ifi_index,
a4b897
 							ainfo.head, stdout);
a4b897
+			if (res > 0 && !do_link && show_stats)
a4b897
+				print_link_stats(stdout, &l->h);
a4b897
 		}
a4b897
 	}
a4b897
 	fflush(stdout);
a4b897
@@ -1222,6 +1411,7 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
a4b897
 	__u32 preferred_lft = INFINITY_LIFE_TIME;
a4b897
 	__u32 valid_lft = INFINITY_LIFE_TIME;
a4b897
 	struct ifa_cacheinfo cinfo;
a4b897
+	unsigned int ifa_flags = 0;
a4b897
 
a4b897
 	memset(&req, 0, sizeof(req));
a4b897
 
a4b897
@@ -1299,9 +1489,13 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
a4b897
 			if (set_lifetime(&preferred_lft, *argv))
a4b897
 				invarg("preferred_lft value", *argv);
a4b897
 		} else if (strcmp(*argv, "home") == 0) {
a4b897
-			req.ifa.ifa_flags |= IFA_F_HOMEADDRESS;
a4b897
+			ifa_flags |= IFA_F_HOMEADDRESS;
a4b897
 		} else if (strcmp(*argv, "nodad") == 0) {
a4b897
-			req.ifa.ifa_flags |= IFA_F_NODAD;
a4b897
+			ifa_flags |= IFA_F_NODAD;
a4b897
+		} else if (strcmp(*argv, "mngtmpaddr") == 0) {
a4b897
+			ifa_flags |= IFA_F_MANAGETEMPADDR;
a4b897
+		} else if (strcmp(*argv, "noprefixroute") == 0) {
a4b897
+			ifa_flags |= IFA_F_NOPREFIXROUTE;
a4b897
 		} else {
a4b897
 			if (strcmp(*argv, "local") == 0) {
a4b897
 				NEXT_ARG();
a4b897
@@ -1319,6 +1513,11 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
a4b897
 		}
a4b897
 		argc--; argv++;
a4b897
 	}
a4b897
+	if (ifa_flags <= 0xff)
a4b897
+		req.ifa.ifa_flags = ifa_flags;
a4b897
+	else
a4b897
+		addattr32(&req.n, sizeof(req), IFA_FLAGS, ifa_flags);
a4b897
+
a4b897
 	if (d == NULL) {
a4b897
 		fprintf(stderr, "Not enough information: \"dev\" argument is required.\n");
a4b897
 		return -1;
a4b897
diff --git a/ip/iplink.c b/ip/iplink.c
a4b897
index dc98019..e17d5df 100644
a4b897
--- a/ip/iplink.c
a4b897
+++ b/ip/iplink.c
a4b897
@@ -31,6 +31,7 @@
a4b897
 #include "rt_names.h"
a4b897
 #include "utils.h"
a4b897
 #include "ip_common.h"
a4b897
+#include "namespace.h"
a4b897
 
a4b897
 #define IPLINK_IOCTL_COMPAT	1
a4b897
 #ifndef LIBDIR
a4b897
@@ -70,6 +71,7 @@ void iplink_usage(void)
a4b897
 	fprintf(stderr, "	                  [ mtu MTU ]\n");
a4b897
 	fprintf(stderr, "	                  [ netns PID ]\n");
a4b897
 	fprintf(stderr, "	                  [ netns NAME ]\n");
a4b897
+	fprintf(stderr, "	                  [ link-netnsid ID ]\n");
a4b897
 	fprintf(stderr, "			  [ alias NAME ]\n");
a4b897
 	fprintf(stderr, "	                  [ vf NUM [ mac LLADDR ]\n");
a4b897
 	fprintf(stderr, "				   [ vlan VLANID [ qos VLAN-QOS ] ]\n");
a4b897
@@ -77,8 +79,11 @@ void iplink_usage(void)
a4b897
 	fprintf(stderr, "				   [ rate TXRATE ] ] \n");
a4b897
 
a4b897
 	fprintf(stderr, "				   [ spoofchk { on | off} ] ] \n");
a4b897
+	fprintf(stderr, "				   [ query_rss { on | off} ] ] \n");
a4b897
+	fprintf(stderr, "				   [ state { auto | enable | disable} ] ]\n");
a4b897
 	fprintf(stderr, "			  [ master DEVICE ]\n");
a4b897
 	fprintf(stderr, "			  [ nomaster ]\n");
a4b897
+	fprintf(stderr, "			  [ addrgenmode { eui64 | none } ]\n");
a4b897
 	fprintf(stderr, "       ip link show [ DEVICE | group GROUP ] [up]\n");
a4b897
 
a4b897
 	if (iplink_have_newlink()) {
a4b897
@@ -144,6 +149,15 @@ static int get_link_mode(const char *mode)
a4b897
 	return -1;
a4b897
 }
a4b897
 
a4b897
+static int get_addr_gen_mode(const char *mode)
a4b897
+{
a4b897
+	if (strcasecmp(mode, "eui64") == 0)
a4b897
+		return IN6_ADDR_GEN_MODE_EUI64;
a4b897
+	if (strcasecmp(mode, "none") == 0)
a4b897
+		return IN6_ADDR_GEN_MODE_NONE;
a4b897
+	return -1;
a4b897
+}
a4b897
+
a4b897
 #if IPLINK_IOCTL_COMPAT
a4b897
 static int have_rtnl_newlink = -1;
a4b897
 
a4b897
@@ -176,8 +190,13 @@ static int iplink_have_newlink(void)
a4b897
 		req.n.nlmsg_type = RTM_NEWLINK;
a4b897
 		req.i.ifi_family = AF_UNSPEC;
a4b897
 
a4b897
-		rtnl_send(&rth, &req.n, req.n.nlmsg_len);
a4b897
-		rtnl_listen(&rth, accept_msg, NULL);
a4b897
+		if (rtnl_send(&rth, &req.n, req.n.nlmsg_len) < 0) {
a4b897
+			perror("Could not check for "
a4b897
+				"link configuration over netlink support");
a4b897
+			have_rtnl_newlink = 0;
a4b897
+		} else {
a4b897
+			rtnl_listen(&rth, accept_msg, NULL);
a4b897
+		}
a4b897
 	}
a4b897
 	return have_rtnl_newlink;
a4b897
 }
a4b897
@@ -254,7 +273,44 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
a4b897
 				invarg("Invalid \"spoofchk\" value\n", *argv);
a4b897
 			ivs.vf = vf;
a4b897
 			addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK, &ivs, sizeof(ivs));
a4b897
+		} else if (matches(*argv, "state") == 0) {
a4b897
+			struct ifla_vf_link_state ivl;
a4b897
+			NEXT_ARG();
a4b897
+			if (matches(*argv, "auto") == 0)
a4b897
+				ivl.link_state = IFLA_VF_LINK_STATE_AUTO;
a4b897
+			else if (matches(*argv, "enable") == 0)
a4b897
+				ivl.link_state = IFLA_VF_LINK_STATE_ENABLE;
a4b897
+			else if (matches(*argv, "disable") == 0)
a4b897
+				ivl.link_state = IFLA_VF_LINK_STATE_DISABLE;
a4b897
+			else
a4b897
+				invarg("Invalid \"state\" value\n", *argv);
a4b897
+			ivl.vf = vf;
a4b897
+			addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE, &ivl, sizeof(ivl));
a4b897
+		} else if (matches(*argv, "query_rss") == 0) {
a4b897
+			struct ifla_vf_rss_query_en ivs;
a4b897
+			NEXT_ARG();
a4b897
+			if (matches(*argv, "on") == 0)
a4b897
+				ivs.setting = 1;
a4b897
+			else if (matches(*argv, "off") == 0)
a4b897
+				ivs.setting = 0;
a4b897
+			else
a4b897
+				invarg("Invalid \"query_rss\" value\n", *argv);
a4b897
+			ivs.vf = vf;
a4b897
+			addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(ivs));
a4b897
 
a4b897
+		} else if (matches(*argv, "state") == 0) {
a4b897
+			struct ifla_vf_link_state ivl;
a4b897
+			NEXT_ARG();
a4b897
+			if (matches(*argv, "auto") == 0)
a4b897
+				ivl.link_state = IFLA_VF_LINK_STATE_AUTO;
a4b897
+			else if (matches(*argv, "enable") == 0)
a4b897
+				ivl.link_state = IFLA_VF_LINK_STATE_ENABLE;
a4b897
+			else if (matches(*argv, "disable") == 0)
a4b897
+				ivl.link_state = IFLA_VF_LINK_STATE_DISABLE;
a4b897
+			else
a4b897
+				invarg("Invalid \"state\" value\n", *argv);
a4b897
+			ivl.vf = vf;
a4b897
+			addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE, &ivl, sizeof(ivl));
a4b897
 		} else {
a4b897
 			/* rewind arg */
a4b897
 			PREV_ARG();
a4b897
@@ -284,6 +340,7 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
a4b897
 	int vf = -1;
a4b897
 	int numtxqueues = -1;
a4b897
 	int numrxqueues = -1;
a4b897
+	int link_netnsid = -1;
a4b897
 
a4b897
 	*group = -1;
a4b897
 	ret = argc;
a4b897
@@ -334,7 +391,7 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
a4b897
 			NEXT_ARG();
a4b897
 			if (netns != -1)
a4b897
 				duparg("netns", *argv);
a4b897
-			if ((netns = get_netns_fd(*argv)) >= 0)
a4b897
+			if ((netns = netns_get_fd(*argv)) >= 0)
a4b897
 				addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, &netns, 4);
a4b897
 			else if (get_integer(&netns, *argv, 0) == 0)
a4b897
 				addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4);
a4b897
@@ -466,6 +523,26 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
a4b897
 				invarg("Invalid \"numrxqueues\" value\n", *argv);
a4b897
 			addattr_l(&req->n, sizeof(*req), IFLA_NUM_RX_QUEUES,
a4b897
 				  &numrxqueues, 4);
a4b897
+		} else if (matches(*argv, "addrgenmode") == 0) {
a4b897
+			struct rtattr *afs, *afs6;
a4b897
+			int mode;
a4b897
+			NEXT_ARG();
a4b897
+			mode = get_addr_gen_mode(*argv);
a4b897
+			if (mode < 0)
a4b897
+				invarg("Invalid address generation mode\n", *argv);
a4b897
+			afs = addattr_nest(&req->n, sizeof(*req), IFLA_AF_SPEC);
a4b897
+			afs6 = addattr_nest(&req->n, sizeof(*req), AF_INET6);
a4b897
+			addattr8(&req->n, sizeof(*req), IFLA_INET6_ADDR_GEN_MODE, mode);
a4b897
+			addattr_nest_end(&req->n, afs6);
a4b897
+			addattr_nest_end(&req->n, afs);
a4b897
+		} else if (matches(*argv, "link-netnsid") == 0) {
a4b897
+			NEXT_ARG();
a4b897
+			if (link_netnsid != -1)
a4b897
+				duparg("link-netnsid", *argv);
a4b897
+			if (get_integer(&link_netnsid, *argv, 0))
a4b897
+				invarg("Invalid \"link-netnsid\" value\n", *argv);
a4b897
+			addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID,
a4b897
+				  link_netnsid);
a4b897
 		} else {
a4b897
 			if (strcmp(*argv, "dev") == 0) {
a4b897
 				NEXT_ARG();
a4b897
diff --git a/ip/iplink_vxlan.c b/ip/iplink_vxlan.c
a4b897
index 1025326..43b8abc 100644
a4b897
--- a/ip/iplink_vxlan.c
a4b897
+++ b/ip/iplink_vxlan.c
a4b897
@@ -23,11 +23,12 @@
a4b897
 
a4b897
 static void explain(void)
a4b897
 {
a4b897
-	fprintf(stderr, "Usage: ... vxlan id VNI [ group ADDR ] [ local ADDR ]\n");
a4b897
+	fprintf(stderr, "Usage: ... vxlan id VNI [ { group | remote } ADDR ] [ local ADDR ]\n");
a4b897
 	fprintf(stderr, "                 [ ttl TTL ] [ tos TOS ] [ dev PHYS_DEV ]\n");
a4b897
-	fprintf(stderr, "                 [ port MIN MAX ] [ [no]learning ]\n");
a4b897
-	fprintf(stderr, "                 [ [no]proxy ] [ [no]rsc ]\n");
a4b897
+	fprintf(stderr, "                 [ dstport PORT ] [ srcport MIN MAX ]\n");
a4b897
+	fprintf(stderr, "                 [ [no]learning ] [ [no]proxy ] [ [no]rsc ]\n");
a4b897
 	fprintf(stderr, "                 [ [no]l2miss ] [ [no]l3miss ]\n");
a4b897
+	fprintf(stderr, "                 [ gbp ]\n");
a4b897
 	fprintf(stderr, "\n");
a4b897
 	fprintf(stderr, "Where: VNI := 0-16777215\n");
a4b897
 	fprintf(stderr, "       ADDR := { IP_ADDRESS | any }\n");
a4b897
@@ -42,6 +43,10 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
a4b897
 	int vni_set = 0;
a4b897
 	__u32 saddr = 0;
a4b897
 	__u32 gaddr = 0;
a4b897
+	__u32 daddr = 0;
a4b897
+	struct in6_addr saddr6 = IN6ADDR_ANY_INIT;
a4b897
+	struct in6_addr gaddr6 = IN6ADDR_ANY_INIT;
a4b897
+	struct in6_addr daddr6 = IN6ADDR_ANY_INIT;
a4b897
 	unsigned link = 0;
a4b897
 	__u8 tos = 0;
a4b897
 	__u8 ttl = 0;
a4b897
@@ -53,6 +58,12 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
a4b897
 	__u8 noage = 0;
a4b897
 	__u32 age = 0;
a4b897
 	__u32 maxaddr = 0;
a4b897
+	__u16 dstport = 0;
a4b897
+	__u8 gbp = 0;
a4b897
+	__u8 udpcsum = 0;
a4b897
+	__u8 udp6zerocsumtx = 0;
a4b897
+	__u8 udp6zerocsumrx = 0;
a4b897
+	int dst_port_set = 0;
a4b897
 	struct ifla_vxlan_port_range range = { 0, 0 };
a4b897
 
a4b897
 	while (argc > 0) {
a4b897
@@ -65,15 +76,30 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
a4b897
 			vni_set = 1;
a4b897
 		} else if (!matches(*argv, "group")) {
a4b897
 			NEXT_ARG();
a4b897
-			gaddr = get_addr32(*argv);
a4b897
-
a4b897
-			if (!IN_MULTICAST(ntohl(gaddr)))
a4b897
-				invarg("invald group address", *argv);
a4b897
+			if (!inet_get_addr(*argv, &gaddr, &gaddr6)) {
a4b897
+				fprintf(stderr, "Invalid address \"%s\"\n", *argv);
a4b897
+				return -1;
a4b897
+			}
a4b897
+			if (!IN6_IS_ADDR_MULTICAST(&gaddr6) && !IN_MULTICAST(ntohl(gaddr)))
a4b897
+				invarg("invalid group address", *argv);
a4b897
+		} else if (!matches(*argv, "remote")) {
a4b897
+			NEXT_ARG();
a4b897
+			if (!inet_get_addr(*argv, &daddr, &daddr6)) {
a4b897
+				fprintf(stderr, "Invalid address \"%s\"\n", *argv);
a4b897
+				return -1;
a4b897
+			}
a4b897
+			if (IN6_IS_ADDR_MULTICAST(&daddr6) || IN_MULTICAST(ntohl(daddr)))
a4b897
+				invarg("invalid remote address", *argv);
a4b897
 		} else if (!matches(*argv, "local")) {
a4b897
 			NEXT_ARG();
a4b897
-			if (strcmp(*argv, "any"))
a4b897
-				saddr = get_addr32(*argv);
a4b897
-			if (IN_MULTICAST(ntohl(saddr)))
a4b897
+			if (strcmp(*argv, "any")) {
a4b897
+				if (!inet_get_addr(*argv, &saddr, &saddr6)) {
a4b897
+					fprintf(stderr, "Invalid address \"%s\"\n", *argv);
a4b897
+					return -1;
a4b897
+				}
a4b897
+			}
a4b897
+
a4b897
+			if (IN_MULTICAST(ntohl(saddr)) || IN6_IS_ADDR_MULTICAST(&saddr6))
a4b897
 				invarg("invalid local address", *argv);
a4b897
 		} else if (!matches(*argv, "dev")) {
a4b897
 			NEXT_ARG();
a4b897
@@ -115,7 +141,8 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
a4b897
 				maxaddr = 0;
a4b897
 			else if (get_u32(&maxaddr, *argv, 0))
a4b897
 				invarg("max addresses", *argv);
a4b897
-		} else if (!matches(*argv, "port")) {
a4b897
+		} else if (!matches(*argv, "port") ||
a4b897
+			   !matches(*argv, "srcport")) {
a4b897
 			__u16 minport, maxport;
a4b897
 			NEXT_ARG();
a4b897
 			if (get_u16(&minport, *argv, 0))
a4b897
@@ -125,6 +152,11 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
a4b897
 				invarg("max port", *argv);
a4b897
 			range.low = htons(minport);
a4b897
 			range.high = htons(maxport);
a4b897
+		} else if (!matches(*argv, "dstport")){
a4b897
+			NEXT_ARG();
a4b897
+			if (get_u16(&dstport, *argv, 0))
a4b897
+				invarg("dst port", *argv);
a4b897
+			dst_port_set = 1;
a4b897
 		} else if (!matches(*argv, "nolearning")) {
a4b897
 			learning = 0;
a4b897
 		} else if (!matches(*argv, "learning")) {
a4b897
@@ -145,6 +177,20 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
a4b897
 			l3miss = 0;
a4b897
 		} else if (!matches(*argv, "l3miss")) {
a4b897
 			l3miss = 1;
a4b897
+		} else if (!matches(*argv, "gbp")) {
a4b897
+			gbp = 1;
a4b897
+		} else if (!matches(*argv, "udpcsum")) {
a4b897
+			udpcsum = 1;
a4b897
+		} else if (!matches(*argv, "noudpcsum")) {
a4b897
+			udpcsum = 0;
a4b897
+		} else if (!matches(*argv, "udp6zerocsumtx")) {
a4b897
+			udp6zerocsumtx = 1;
a4b897
+		} else if (!matches(*argv, "noudp6zerocsumtx")) {
a4b897
+			udp6zerocsumtx = 0;
a4b897
+		} else if (!matches(*argv, "udp6zerocsumrx")) {
a4b897
+			udp6zerocsumrx = 1;
a4b897
+		} else if (!matches(*argv, "noudp6zerocsumrx")) {
a4b897
+			udp6zerocsumrx = 0;
a4b897
 		} else if (matches(*argv, "help") == 0) {
a4b897
 			explain();
a4b897
 			return -1;
a4b897
@@ -160,11 +206,35 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
a4b897
 		fprintf(stderr, "vxlan: missing virtual network identifier\n");
a4b897
 		return -1;
a4b897
 	}
a4b897
+
a4b897
+	if (!dst_port_set) {
a4b897
+		fprintf(stderr, "vxlan: destination port not specified\n"
a4b897
+			"Will use Linux kernel default (non-standard value)\n");
a4b897
+		fprintf(stderr,
a4b897
+			"Use 'dstport 4789' to get the IANA assigned value\n"
a4b897
+			"Use 'dstport 0' to get default and quiet this message\n");
a4b897
+	}
a4b897
+	if ((gaddr && daddr) ||
a4b897
+		(memcmp(&gaddr6, &in6addr_any, sizeof(gaddr6)) &&
a4b897
+		 memcmp(&daddr6, &in6addr_any, sizeof(daddr6)))) {
a4b897
+		fprintf(stderr, "vxlan: both group and remote cannot be specified\n");
a4b897
+		return -1;
a4b897
+	}
a4b897
 	addattr32(n, 1024, IFLA_VXLAN_ID, vni);
a4b897
 	if (gaddr)
a4b897
 		addattr_l(n, 1024, IFLA_VXLAN_GROUP, &gaddr, 4);
a4b897
+	else if (daddr)
a4b897
+		addattr_l(n, 1024, IFLA_VXLAN_GROUP, &daddr, 4);
a4b897
+	if (memcmp(&gaddr6, &in6addr_any, sizeof(gaddr6)) != 0)
a4b897
+		addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &gaddr6, sizeof(struct in6_addr));
a4b897
+	else if (memcmp(&daddr6, &in6addr_any, sizeof(daddr6)) != 0)
a4b897
+		addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &daddr6, sizeof(struct in6_addr));
a4b897
+
a4b897
 	if (saddr)
a4b897
 		addattr_l(n, 1024, IFLA_VXLAN_LOCAL, &saddr, 4);
a4b897
+	else if (memcmp(&saddr6, &in6addr_any, sizeof(saddr6)) != 0)
a4b897
+		addattr_l(n, 1024, IFLA_VXLAN_LOCAL6, &saddr6, sizeof(struct in6_addr));
a4b897
+
a4b897
 	if (link)
a4b897
 		addattr32(n, 1024, IFLA_VXLAN_LINK, link);
a4b897
 	addattr8(n, 1024, IFLA_VXLAN_TTL, ttl);
a4b897
@@ -174,6 +244,10 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
a4b897
 	addattr8(n, 1024, IFLA_VXLAN_RSC, rsc);
a4b897
 	addattr8(n, 1024, IFLA_VXLAN_L2MISS, l2miss);
a4b897
 	addattr8(n, 1024, IFLA_VXLAN_L3MISS, l3miss);
a4b897
+	addattr8(n, 1024, IFLA_VXLAN_UDP_CSUM, udpcsum);
a4b897
+	addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, udp6zerocsumtx);
a4b897
+	addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, udp6zerocsumrx);
a4b897
+
a4b897
 	if (noage)
a4b897
 		addattr32(n, 1024, IFLA_VXLAN_AGEING, 0);
a4b897
 	else if (age)
a4b897
@@ -184,6 +258,13 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
a4b897
 		addattr_l(n, 1024, IFLA_VXLAN_PORT_RANGE,
a4b897
 			  &range, sizeof(range));
a4b897
 
a4b897
+	if (dstport)
a4b897
+		addattr16(n, 1024, IFLA_VXLAN_PORT, htons(dstport));
a4b897
+
a4b897
+	if (gbp)
a4b897
+		addattr_l(n, 1024, IFLA_VXLAN_GBP, NULL, 0);
a4b897
+
a4b897
+
a4b897
 	return 0;
a4b897
 }
a4b897
 
a4b897
@@ -208,9 +289,25 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
a4b897
 
a4b897
 	if (tb[IFLA_VXLAN_GROUP]) {
a4b897
 		__be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_GROUP]);
a4b897
-		if (addr)
a4b897
-			fprintf(f, "group %s ",
a4b897
-				format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
a4b897
+		if (addr) {
a4b897
+			if (IN_MULTICAST(ntohl(addr)))
a4b897
+				fprintf(f, "group %s ",
a4b897
+					format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
a4b897
+			else
a4b897
+				fprintf(f, "remote %s ",
a4b897
+					format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
a4b897
+		}
a4b897
+	} else if (tb[IFLA_VXLAN_GROUP6]) {
a4b897
+		struct in6_addr addr;
a4b897
+		memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_GROUP6]), sizeof(struct in6_addr));
a4b897
+		if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0) {
a4b897
+			if (IN6_IS_ADDR_MULTICAST(&addr))
a4b897
+				fprintf(f, "group %s ",
a4b897
+					format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1)));
a4b897
+			else
a4b897
+				fprintf(f, "remote %s ",
a4b897
+					format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1)));
a4b897
+		}
a4b897
 	}
a4b897
 
a4b897
 	if (tb[IFLA_VXLAN_LOCAL]) {
a4b897
@@ -218,6 +315,12 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
a4b897
 		if (addr)
a4b897
 			fprintf(f, "local %s ",
a4b897
 				format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
a4b897
+	} else if (tb[IFLA_VXLAN_LOCAL6]) {
a4b897
+		struct in6_addr addr;
a4b897
+		memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_LOCAL6]), sizeof(struct in6_addr));
a4b897
+		if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0)
a4b897
+			fprintf(f, "local %s ",
a4b897
+				format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1)));
a4b897
 	}
a4b897
 
a4b897
 	if (tb[IFLA_VXLAN_LINK] &&
a4b897
@@ -233,9 +336,13 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
a4b897
 	if (tb[IFLA_VXLAN_PORT_RANGE]) {
a4b897
 		const struct ifla_vxlan_port_range *r
a4b897
 			= RTA_DATA(tb[IFLA_VXLAN_PORT_RANGE]);
a4b897
-		fprintf(f, "port %u %u ", ntohs(r->low), ntohs(r->high));
a4b897
+		fprintf(f, "srcport %u %u ", ntohs(r->low), ntohs(r->high));
a4b897
 	}
a4b897
 
a4b897
+	if (tb[IFLA_VXLAN_PORT])
a4b897
+		fprintf(f, "dstport %u ",
a4b897
+			ntohs(rta_getattr_u16(tb[IFLA_VXLAN_PORT])));
a4b897
+
a4b897
 	if (tb[IFLA_VXLAN_LEARNING] &&
a4b897
 	    !rta_getattr_u8(tb[IFLA_VXLAN_LEARNING]))
a4b897
 		fputs("nolearning ", f);
a4b897
@@ -277,6 +384,20 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
a4b897
 	if (tb[IFLA_VXLAN_LIMIT] &&
a4b897
 	    (maxaddr = rta_getattr_u32(tb[IFLA_VXLAN_LIMIT]) != 0))
a4b897
 		    fprintf(f, "maxaddr %u ", maxaddr);
a4b897
+
a4b897
+	if (tb[IFLA_VXLAN_UDP_CSUM] && rta_getattr_u8(tb[IFLA_VXLAN_UDP_CSUM]))
a4b897
+		fputs("udpcsum ", f);
a4b897
+
a4b897
+	if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX] &&
a4b897
+	    rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]))
a4b897
+		fputs("udp6zerocsumtx ", f);
a4b897
+
a4b897
+	if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX] &&
a4b897
+	    rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]))
a4b897
+		fputs("udp6zerocsumrx ", f);
a4b897
+
a4b897
+	if (tb[IFLA_VXLAN_GBP])
a4b897
+		fputs("gbp ", f);
a4b897
 }
a4b897
 
a4b897
 struct link_util vxlan_link_util = {
a4b897
diff --git a/lib/libnetlink.c b/lib/libnetlink.c
a4b897
index 9e2a795..baac6ae 100644
a4b897
--- a/lib/libnetlink.c
a4b897
+++ b/lib/libnetlink.c
a4b897
@@ -701,6 +701,18 @@ int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int l
a4b897
 	return i;
a4b897
 }
a4b897
 
a4b897
+struct rtattr *parse_rtattr_one(int type, struct rtattr *rta, int len)
a4b897
+{
a4b897
+	while (RTA_OK(rta, len)) {
a4b897
+		if (rta->rta_type == type)
a4b897
+			return rta;
a4b897
+		rta = RTA_NEXT(rta, len);
a4b897
+	}
a4b897
+	if (len)
a4b897
+		fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
a4b897
+	return NULL;
a4b897
+}
a4b897
+
a4b897
 int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta,
a4b897
 			         int len)
a4b897
 {
a4b897
diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in
a4b897
index 86e0bc9..a015b82 100644
a4b897
--- a/man/man8/ip-link.8.in
a4b897
+++ b/man/man8/ip-link.8.in
a4b897
@@ -16,6 +16,7 @@ ip-link \- network device configuration
a4b897
 .ti -8
a4b897
 .IR OPTIONS " := { "
a4b897
 \fB\-V\fR[\fIersion\fR] |
a4b897
+\fB\-h\fR[\fIuman-readable\fR] |
a4b897
 \fB\-s\fR[\fItatistics\fR] |
a4b897
 \fB\-r\fR[\fIesolve\fR] |
a4b897
 \fB\-f\fR[\fIamily\fR] {
a4b897
@@ -120,9 +121,11 @@ ip-link \- network device configuration
a4b897
 ] |
a4b897
 .br
a4b897
 .B master
a4b897
-.IR DEVICE
a4b897
+.IR DEVICE " |"
a4b897
 .br
a4b897
-.B nomaster
a4b897
+.B nomaster " |"
a4b897
+.br
a4b897
+.B addrgenmode { eui64 | none }
a4b897
 .BR " }"
a4b897
 
a4b897
 
a4b897
@@ -197,6 +200,65 @@ specifies the number of transmit queues for new device.
a4b897
 specifies the number of receive queues for new device.
a4b897
 
a4b897
 .TP
a4b897
+VLAN Type Support
a4b897
+For a link of type
a4b897
+.I VLAN
a4b897
+the following additional arguments are supported:
a4b897
+
a4b897
+.BI "ip link add link " DEVICE
a4b897
+.BI name " NAME "
a4b897
+.BI type " vlan "
a4b897
+.R " [ "
a4b897
+.BI protocol " VLAN_PROTO "
a4b897
+.R " ] "
a4b897
+.BI id " VLANID "
a4b897
+.R " [ "
a4b897
+.BR reorder_hdr " { " on " | " off " } "
a4b897
+.R " ] "
a4b897
+.R " [ "
a4b897
+.BR gvrp " { " on " | " off " } "
a4b897
+.R " ] "
a4b897
+.R " [ "
a4b897
+.BR mvrp " { " on " | " off " } "
a4b897
+.R " ] "
a4b897
+.R " [ "
a4b897
+.BR loose_binding " { " on " | " off " } "
a4b897
+.R " ] "
a4b897
+.R " [ "
a4b897
+.BI ingress-qos-map " QOS-MAP "
a4b897
+.R " ] "
a4b897
+.R " [ "
a4b897
+.BI egress-qos-map " QOS-MAP "
a4b897
+.R " ] "
a4b897
+
a4b897
+.in +8
a4b897
+.sp
a4b897
+.BI protocol " VLAN_PROTO "
a4b897
+- either 802.1Q or 802.1ad.
a4b897
+
a4b897
+.BI id " VLANID "
a4b897
+- specifies the VID.
a4b897
+
a4b897
+.BR reorder_hdr " { " on " | " off " } "
a4b897
+- specifies whether ethernet headers are reordered or not.
a4b897
+
a4b897
+.BR gvrp " { " on " | " off " } "
a4b897
+- specifies whether this VLAN should be registered using GARP VLAN Registration Protocol.
a4b897
+
a4b897
+.BR mvrp " { " on " | " off " } "
a4b897
+- specifies whether this VLAN should be registered using Multiple VLAN Registration Protocol.
a4b897
+
a4b897
+.BR loose_binding " { " on " | " off " } "
a4b897
+- specifies whether the VLAN device state is bound to the physical device state.
a4b897
+
a4b897
+.BI ingress-qos-map " QOS-MAP "
a4b897
+- defines a mapping between priority code points on incoming frames.  The format is FROM:TO with multiple mappings separated by spaces.
a4b897
+
a4b897
+.BI egress-qos-map " QOS-MAP "
a4b897
+- the same as ingress-qos-map but for outgoing frames.
a4b897
+.in -8
a4b897
+
a4b897
+.TP
a4b897
 VXLAN Type Support
a4b897
 For a link of type 
a4b897
 .I VXLAN
a4b897
@@ -206,8 +268,8 @@ the following additional arguments are supported:
a4b897
 .BI type " vxlan " id " ID
a4b897
 .R " [ "
a4b897
 .BI dev " PHYS_DEV "
a4b897
-.R " ] [ "
a4b897
-.BI group " IPADDR "
a4b897
+.RB " ] [ { " group " | " remote " } "
a4b897
+.I IPADDR
a4b897
 .R " ] [ "
a4b897
 .BI local " IPADDR "
a4b897
 .R " ] [ "
a4b897
@@ -226,6 +288,8 @@ the following additional arguments are supported:
a4b897
 .I "[no]l2miss "
a4b897
 .R " ] [ "
a4b897
 .I "[no]l3miss "
a4b897
+.R " ] [ "
a4b897
+.B gbp
a4b897
 .R " ]"
a4b897
 
a4b897
 .in +8
a4b897
@@ -240,6 +304,17 @@ Identifier) to use.
a4b897
 .sp
a4b897
 .BI group " IPADDR"
a4b897
 - specifies the multicast IP address to join.
a4b897
+This parameter cannot be specified with the
a4b897
+.B remote
a4b897
+parameter.
a4b897
+
a4b897
+.sp
a4b897
+.BI remote " IPADDR"
a4b897
+- specifies the unicast destination IP address to use in outgoing packets
a4b897
+when the destination link layer address is not known in the VXLAN device
a4b897
+forwarding database.  This parameter cannot be specified with the
a4b897
+.B group
a4b897
+parameter.
a4b897
 
a4b897
 .sp
a4b897
 .BI local " IPADDR"
a4b897
@@ -279,6 +354,49 @@ are entered into the VXLAN device forwarding database.
a4b897
 .I [no]l3miss
a4b897
 - specifies if netlink IP ADDR miss notifications are generated.
a4b897
 
a4b897
+.sp
a4b897
+.B gbp
a4b897
+- enables the Group Policy extension (VXLAN-GBP).
a4b897
+
a4b897
+.in +4
a4b897
+Allows to transport group policy context across VXLAN network peers.
a4b897
+If enabled, includes the mark of a packet in the VXLAN header for outgoing
a4b897
+packets and fills the packet mark based on the information found in the
a4b897
+VXLAN header for incomming packets.
a4b897
+
a4b897
+Format of upper 16 bits of packet mark (flags);
a4b897
+
a4b897
+.in +2
a4b897
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
a4b897
+.br
a4b897
+|-|-|-|-|-|-|-|-|-|D|-|-|A|-|-|-|
a4b897
+.br
a4b897
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
a4b897
+
a4b897
+.B D :=
a4b897
+Don't Learn bit. When set, this bit indicates that the egress
a4b897
+VTEP MUST NOT learn the source address of the encapsulated frame.
a4b897
+
a4b897
+.B A :=
a4b897
+Indicates that the group policy has already been applied to
a4b897
+this packet. Policies MUST NOT be applied by devices when the A bit is set.
a4b897
+.in -2
a4b897
+
a4b897
+Format of lower 16 bits of packet mark (policy ID):
a4b897
+
a4b897
+.in +2
a4b897
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
a4b897
+.br
a4b897
+|        Group Policy ID        |
a4b897
+.br
a4b897
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
a4b897
+.in -2
a4b897
+
a4b897
+Example:
a4b897
+  iptables -A OUTPUT [...] -j MARK --set-mark 0x800FF
a4b897
+
a4b897
+.in -4
a4b897
+
a4b897
 .in -8
a4b897
 
a4b897
 .SS ip link delete - delete virtual link
a4b897
@@ -366,14 +484,29 @@ the interface is
a4b897
 .IR "POINTOPOINT" .
a4b897
 
a4b897
 .TP
a4b897
-.BI netns " PID"
a4b897
-move the device to the network namespace associated with the process
a4b897
-.IR "PID".
a4b897
-
a4b897
-.TP
a4b897
-.BI netns " NETNSNAME"
a4b897
+.BI netns " NETNSNAME " \fR| " PID"
a4b897
 move the device to the network namespace associated with name
a4b897
-.IR "NETNSNAME".
a4b897
+.IR "NETNSNAME " or
a4b897
+.RI process " PID".
a4b897
+
a4b897
+Some devices are not allowed to change network namespace: loopback, bridge,
a4b897
+ppp, wireless. These are network namespace local devices. In such case
a4b897
+.B ip
a4b897
+tool will return "Invalid argument" error. It is possible to find out if device is local
a4b897
+to a single network namespace by checking
a4b897
+.B netns-local
a4b897
+flag in the output of the
a4b897
+.BR ethtool ":"
a4b897
+
a4b897
+.in +8
a4b897
+.B ethtool -k
a4b897
+.I DEVICE
a4b897
+.in -8
a4b897
+
a4b897
+To change network namespace for wireless devices the
a4b897
+.B iw
a4b897
+tool can be used. But it allows to change network namespace only for physical devices and by process
a4b897
+.IR PID .
a4b897
 
a4b897
 .TP
a4b897
 .BI alias " NAME"
a4b897
@@ -442,6 +575,10 @@ set master device of the device (enslave device).
a4b897
 .BI nomaster
a4b897
 unset master device of the device (release device).
a4b897
 
a4b897
+.TP
a4b897
+.BR "addrgenmode eui64 " or " addrgenmode none"
a4b897
+allow to ipv6 set address generation mode
a4b897
+
a4b897
 .PP
a4b897
 .B Warning:
a4b897
 If multiple parameter changes are requested,
a4b897
@@ -471,6 +608,27 @@ specifies what group of devices to show.
a4b897
 .B up
a4b897
 only display running interfaces.
a4b897
 
a4b897
+.TP
a4b897
+The show command has additional formatting options:
a4b897
+
a4b897
+.RS
a4b897
+.TP
a4b897
+.BR "\-s" , " \-stats", " \-statistics"
a4b897
+output more statistics about packet usage.
a4b897
+
a4b897
+.TP
a4b897
+.BR "\-d", " \-details"
a4b897
+output more detailed information.
a4b897
+
a4b897
+.TP
a4b897
+.BR "\-h", " \-human", " \-human-readble"
a4b897
+output statistics with human readable values number followed by suffix
a4b897
+
a4b897
+.TP
a4b897
+.BR "\-iec"
a4b897
+print human readable rates in IEC units (ie. 1K = 1024).
a4b897
+.RE
a4b897
+
a4b897
 .SH "EXAMPLES"
a4b897
 .PP
a4b897
 ip link show
a4b897
@@ -495,7 +653,8 @@ Removes vlan device.
a4b897
 
a4b897
 .SH SEE ALSO
a4b897
 .br
a4b897
-.BR ip (8)
a4b897
+.BR ip (8),
a4b897
+.BR ip-netns (8)
a4b897
 
a4b897
 .SH AUTHOR
a4b897
 Original Manpage by Michail Litvak <mci@owl.openwall.com>
a4b897
diff --git a/man/man8/ip.8 b/man/man8/ip.8
a4b897
index 9065b3a..0713756 100644
a4b897
--- a/man/man8/ip.8
a4b897
+++ b/man/man8/ip.8
a4b897
@@ -31,7 +31,8 @@ ip \- show / manipulate routing, devices, policy routing and tunnels
a4b897
 \fB\-r\fR[\fIesolve\fR] |
a4b897
 \fB\-f\fR[\fIamily\fR] {
a4b897
 .BR inet " | " inet6 " | " ipx " | " dnet " | " link " } | "
a4b897
-\fB\-o\fR[\fIneline\fR] }
a4b897
+\fB\-o\fR[\fIneline\fR] |
a4b897
+\fB\-n\fR[\fIetns\fR] name }
a4b897
 
a4b897
 
a4b897
 .SH OPTIONS
a4b897
@@ -130,6 +131,26 @@ the output.
a4b897
 use the system's name resolver to print DNS names instead of
a4b897
 host addresses.
a4b897
 
a4b897
+.TP
a4b897
+.BR "\-n" , " \-net" , " \-netns " <NETNS>
a4b897
+switches
a4b897
+.B ip
a4b897
+to the specified network namespace
a4b897
+.IR NETNS .
a4b897
+Actually it just simplifies executing of:
a4b897
+
a4b897
+.B ip netns exec
a4b897
+.IR NETNS
a4b897
+.B ip
a4b897
+.RI "[ " OPTIONS " ] " OBJECT " { " COMMAND " | "
a4b897
+.BR help " }"
a4b897
+
a4b897
+to
a4b897
+
a4b897
+.B ip
a4b897
+.RI "-n[etns] " NETNS " [ " OPTIONS " ] " OBJECT " { " COMMAND " | "
a4b897
+.BR help " }"
a4b897
+
a4b897
 .SH IP - COMMAND SYNTAX
a4b897
 
a4b897
 .SS