Merge branch 'master' into next

Signed-off-by: David Ahern <dsahern@gmail.com>
diff --git a/bridge/link.c b/bridge/link.c
index 074edf0..3bc7af2 100644
--- a/bridge/link.c
+++ b/bridge/link.c
@@ -378,7 +378,7 @@
 			state = strtol(*argv, &endptr, 10);
 			if (!(**argv != '\0' && *endptr == '\0')) {
 				for (state = 0; state < nstates; state++)
-					if (strcmp(port_states[state], *argv) == 0)
+					if (strcasecmp(port_states[state], *argv) == 0)
 						break;
 				if (state == nstates) {
 					fprintf(stderr,
diff --git a/bridge/vlan.c b/bridge/vlan.c
index 205851e..0d142bc 100644
--- a/bridge/vlan.c
+++ b/bridge/vlan.c
@@ -22,6 +22,11 @@
 	VLAN_SHOW_TUNNELINFO,
 };
 
+#define VLAN_ID_LEN 9
+
+#define __stringify_1(x...) #x
+#define __stringify(x...) __stringify_1(x)
+
 static void usage(void)
 {
 	fprintf(stderr,
@@ -256,11 +261,11 @@
 	return 1;
 }
 
-static void open_vlan_port(int ifi_index, const char *fmt,
-			   enum vlan_show_subject subject)
+static void open_vlan_port(int ifi_index, enum vlan_show_subject subject)
 {
 	open_json_object(NULL);
-	print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", fmt,
+	print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname",
+			   "%-" __stringify(IFNAMSIZ) "s  ",
 			   ll_index_to_name(ifi_index));
 	open_json_array(PRINT_JSON,
 			subject == VLAN_SHOW_VLAN ? "vlans": "tunnels");
@@ -272,16 +277,18 @@
 	close_json_object();
 }
 
-static void print_range(const char *name, __u32 start, __u32 id)
+static unsigned int print_range(const char *name, __u32 start, __u32 id)
 {
 	char end[64];
+	int width;
 
 	snprintf(end, sizeof(end), "%sEnd", name);
 
-	print_uint(PRINT_ANY, name, "\t %u", start);
+	width = print_uint(PRINT_ANY, name, "%u", start);
 	if (start != id)
-		print_uint(PRINT_ANY, end, "-%u", id);
+		width += print_uint(PRINT_ANY, end, "-%u", id);
 
+	return width;
 }
 
 static void print_vlan_tunnel_info(struct rtattr *tb, int ifindex)
@@ -290,14 +297,14 @@
 	int rem = RTA_PAYLOAD(list);
 	__u16 last_vid_start = 0;
 	__u32 last_tunid_start = 0;
-
-	open_vlan_port(ifindex, "%s", VLAN_SHOW_TUNNELINFO);
+	bool opened = false;
 
 	for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 		struct rtattr *ttb[IFLA_BRIDGE_VLAN_TUNNEL_MAX+1];
 		__u32 tunnel_id = 0;
 		__u16 tunnel_vid = 0;
 		__u16 tunnel_flags = 0;
+		unsigned int width;
 		int vcheck_ret;
 
 		if (i->rta_type != IFLA_BRIDGE_VLAN_TUNNEL_INFO)
@@ -331,13 +338,33 @@
 		else if (vcheck_ret == 0)
 			continue;
 
+		if (!opened) {
+			open_vlan_port(ifindex, VLAN_SHOW_TUNNELINFO);
+			opened = true;
+		} else {
+			print_string(PRINT_FP, NULL,
+				     "%-" __stringify(IFNAMSIZ) "s  ", "");
+		}
+
 		open_json_object(NULL);
-		print_range("vlan", last_vid_start, tunnel_vid);
+		width = print_range("vlan", last_vid_start, tunnel_vid);
+		if (width <= VLAN_ID_LEN) {
+			char buf[VLAN_ID_LEN + 1];
+
+			snprintf(buf, sizeof(buf), "%-*s",
+				 VLAN_ID_LEN - width, "");
+			print_string(PRINT_FP, NULL, "%s  ", buf);
+		} else {
+			fprintf(stderr, "BUG: vlan range too wide, %u\n",
+				width);
+		}
 		print_range("tunid", last_tunid_start, tunnel_id);
 		close_json_object();
-		print_string(PRINT_FP, NULL, "%s", _SL_);
+		print_nl();
 	}
-	close_vlan_port();
+
+	if (opened)
+		close_vlan_port();
 }
 
 static int print_vlan(struct nlmsghdr *n, void *arg)
@@ -366,16 +393,8 @@
 		return 0;
 
 	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len);
-
-	/* if AF_SPEC isn't there, vlan table is not preset for this port */
-	if (!tb[IFLA_AF_SPEC]) {
-		if (!filter_vlan && !is_json_context()) {
-			color_fprintf(stdout, COLOR_IFNAME, "%s",
-				      ll_index_to_name(ifm->ifi_index));
-			fprintf(stdout, "\tNone\n");
-		}
+	if (!tb[IFLA_AF_SPEC])
 		return 0;
-	}
 
 	switch (*subject) {
 	case VLAN_SHOW_VLAN:
@@ -385,9 +404,7 @@
 		print_vlan_tunnel_info(tb[IFLA_AF_SPEC], ifm->ifi_index);
 		break;
 	}
-	print_string(PRINT_FP, NULL, "%s", _SL_);
 
-	fflush(stdout);
 	return 0;
 }
 
@@ -408,20 +425,23 @@
 static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats)
 {
 	open_json_object(NULL);
-	print_hu(PRINT_ANY, "vid", " %hu", vstats->vid);
 
+	print_hu(PRINT_ANY, "vid", "%hu", vstats->vid);
 	print_vlan_flags(vstats->flags);
+	print_nl();
 
-	print_lluint(PRINT_ANY, "rx_bytes",
-		     "\n                   RX: %llu bytes",
+	print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s    ", "");
+	print_lluint(PRINT_ANY, "rx_bytes", "RX: %llu bytes",
 		     vstats->rx_bytes);
 	print_lluint(PRINT_ANY, "rx_packets", " %llu packets\n",
-		vstats->rx_packets);
-	print_lluint(PRINT_ANY, "tx_bytes",
-		     "                   TX: %llu bytes",
+		     vstats->rx_packets);
+
+	print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s    ", "");
+	print_lluint(PRINT_ANY, "tx_bytes", "TX: %llu bytes",
 		     vstats->tx_bytes);
 	print_lluint(PRINT_ANY, "tx_packets", " %llu packets\n",
-		vstats->tx_packets);
+		     vstats->tx_packets);
+
 	close_json_object();
 }
 
@@ -456,10 +476,11 @@
 
 		/* found vlan stats, first time print the interface name */
 		if (!found_vlan) {
-			open_vlan_port(ifindex, "%-16s", VLAN_SHOW_VLAN);
+			open_vlan_port(ifindex, VLAN_SHOW_VLAN);
 			found_vlan = true;
 		} else {
-			print_string(PRINT_FP, NULL, "%-16s", "");
+			print_string(PRINT_FP, NULL,
+				     "%-" __stringify(IFNAMSIZ) "s  ", "");
 		}
 		print_one_vlan_stats(vstats);
 	}
@@ -538,15 +559,17 @@
 		}
 
 		if (!is_json_context()) {
-			printf("port\tvlan ids");
+			printf("%-" __stringify(IFNAMSIZ) "s  %-"
+			       __stringify(VLAN_ID_LEN) "s", "port",
+			       "vlan-id");
 			if (subject == VLAN_SHOW_TUNNELINFO)
-				printf("\ttunnel id");
+				printf("  tunnel-id");
 			printf("\n");
 		}
 
 		ret = rtnl_dump_filter(&rth, print_vlan, &subject);
 		if (ret < 0) {
-			fprintf(stderr, "Dump ternminated\n");
+			fprintf(stderr, "Dump terminated\n");
 			exit(1);
 		}
 	} else {
@@ -559,7 +582,8 @@
 		}
 
 		if (!is_json_context())
-			printf("%-16s vlan id\n", "port");
+			printf("%-" __stringify(IFNAMSIZ) "s  vlan-id\n",
+			       "port");
 
 		if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
 			fprintf(stderr, "Dump terminated\n");
@@ -588,8 +612,7 @@
 	struct rtattr *i, *list = tb;
 	int rem = RTA_PAYLOAD(list);
 	__u16 last_vid_start = 0;
-
-	open_vlan_port(ifindex, "%s", VLAN_SHOW_VLAN);
+	bool opened = false;
 
 	for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 		struct bridge_vlan_info *vinfo;
@@ -608,14 +631,24 @@
 		else if (vcheck_ret == 0)
 			continue;
 
+		if (!opened) {
+			open_vlan_port(ifindex, VLAN_SHOW_VLAN);
+			opened = true;
+		} else {
+			print_string(PRINT_FP, NULL, "%-"
+				     __stringify(IFNAMSIZ) "s  ", "");
+		}
+
 		open_json_object(NULL);
 		print_range("vlan", last_vid_start, vinfo->vid);
 
 		print_vlan_flags(vinfo->flags);
 		close_json_object();
-		print_string(PRINT_FP, NULL, "%s", _SL_);
+		print_nl();
 	}
-	close_vlan_port();
+
+	if (opened)
+		close_vlan_port();
 }
 
 int do_vlan(int argc, char **argv)
diff --git a/devlink/devlink.c b/devlink/devlink.c
index 816b5de..bd48a73 100644
--- a/devlink/devlink.c
+++ b/devlink/devlink.c
@@ -6476,10 +6476,27 @@
 	return err;
 }
 
+static int cmd_region_snapshot_new(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	int err;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_NEW,
+			       NLM_F_REQUEST | NLM_F_ACK);
+
+	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION |
+				DL_OPT_REGION_SNAPSHOT_ID, 0);
+	if (err)
+		return err;
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
+}
+
 static void cmd_region_help(void)
 {
 	pr_err("Usage: devlink region show [ DEV/REGION ]\n");
 	pr_err("       devlink region del DEV/REGION snapshot SNAPSHOT_ID\n");
+	pr_err("       devlink region new DEV/REGION snapshot SNAPSHOT_ID\n");
 	pr_err("       devlink region dump DEV/REGION [ snapshot SNAPSHOT_ID ]\n");
 	pr_err("       devlink region read DEV/REGION [ snapshot SNAPSHOT_ID ] address ADDRESS length LENGTH\n");
 }
@@ -6503,6 +6520,9 @@
 	} else if (dl_argv_match(dl, "read")) {
 		dl_arg_inc(dl);
 		return cmd_region_read(dl);
+	} else if (dl_argv_match(dl, "new")) {
+		dl_arg_inc(dl);
+		return cmd_region_snapshot_new(dl);
 	}
 	pr_err("Command \"%s\" not found\n", dl_argv(dl));
 	return -ENOENT;
diff --git a/include/json_print.h b/include/json_print.h
index 3444479..50e71de 100644
--- a/include/json_print.h
+++ b/include/json_print.h
@@ -44,20 +44,24 @@
 void print_nl(void);
 
 #define _PRINT_FUNC(type_name, type)					\
-	void print_color_##type_name(enum output_type t,		\
-				     enum color_attr color,		\
-				     const char *key,			\
-				     const char *fmt,			\
-				     type value);			\
+	int print_color_##type_name(enum output_type t,			\
+				    enum color_attr color,		\
+				    const char *key,			\
+				    const char *fmt,			\
+				    type value);			\
 									\
-	static inline void print_##type_name(enum output_type t,	\
-					     const char *key,		\
-					     const char *fmt,		\
-					     type value)		\
+	static inline int print_##type_name(enum output_type t,		\
+					    const char *key,		\
+					    const char *fmt,		\
+					    type value)			\
 	{								\
-		print_color_##type_name(t, COLOR_NONE, key, fmt, value);	\
+		return print_color_##type_name(t, COLOR_NONE, key, fmt,	\
+					       value);			\
 	}
 
+/* These functions return 0 if printing to a JSON context, number of
+ * characters printed otherwise (as calculated by printf(3)).
+ */
 _PRINT_FUNC(int, int)
 _PRINT_FUNC(s64, int64_t)
 _PRINT_FUNC(bool, bool)
diff --git a/ip/xfrm_state.c b/ip/xfrm_state.c
index d68f600..f4bf335 100644
--- a/ip/xfrm_state.c
+++ b/ip/xfrm_state.c
@@ -1131,7 +1131,8 @@
 	if (!xfrm_state_filter_match(xsinfo))
 		return 0;
 
-	if (xsinfo->id.proto == IPPROTO_IPIP)
+	if (xsinfo->id.proto == IPPROTO_IPIP ||
+	    xsinfo->id.proto == IPPROTO_IPV6)
 		return 0;
 
 	if (xb->offset > xb->size) {
diff --git a/lib/json_print.c b/lib/json_print.c
index 8e7f32d..fe0705b 100644
--- a/lib/json_print.c
+++ b/lib/json_print.c
@@ -123,20 +123,22 @@
  */
 #define _PRINT_FUNC(type_name, type)					\
 	__attribute__((format(printf, 4, 0)))				\
-	void print_color_##type_name(enum output_type t,		\
-				     enum color_attr color,		\
-				     const char *key,			\
-				     const char *fmt,			\
-				     type value)			\
+	int print_color_##type_name(enum output_type t,			\
+				    enum color_attr color,		\
+				    const char *key,			\
+				    const char *fmt,			\
+				    type value)				\
 	{								\
+		int ret = 0;						\
 		if (_IS_JSON_CONTEXT(t)) {				\
 			if (!key)					\
 				jsonw_##type_name(_jw, value);		\
 			else						\
 				jsonw_##type_name##_field(_jw, key, value); \
 		} else if (_IS_FP_CONTEXT(t)) {				\
-			color_fprintf(stdout, color, fmt, value);          \
+			ret = color_fprintf(stdout, color, fmt, value); \
 		}							\
+		return ret;						\
 	}
 _PRINT_FUNC(int, int);
 _PRINT_FUNC(s64, int64_t);
@@ -162,12 +164,14 @@
 _PRINT_NAME_VALUE_FUNC(string, const char*, s);
 #undef _PRINT_NAME_VALUE_FUNC
 
-void print_color_string(enum output_type type,
-			enum color_attr color,
-			const char *key,
-			const char *fmt,
-			const char *value)
+int print_color_string(enum output_type type,
+		       enum color_attr color,
+		       const char *key,
+		       const char *fmt,
+		       const char *value)
 {
+	int ret = 0;
+
 	if (_IS_JSON_CONTEXT(type)) {
 		if (key && !value)
 			jsonw_name(_jw, key);
@@ -176,8 +180,10 @@
 		else
 			jsonw_string_field(_jw, key, value);
 	} else if (_IS_FP_CONTEXT(type)) {
-		color_fprintf(stdout, color, fmt, value);
+		ret = color_fprintf(stdout, color, fmt, value);
 	}
+
+	return ret;
 }
 
 /*
@@ -185,47 +191,58 @@
  * a value to it, you will need to use "is_json_context()" to have different
  * branch for json and regular output. grep -r "print_bool" for example
  */
-void print_color_bool(enum output_type type,
-		      enum color_attr color,
-		      const char *key,
-		      const char *fmt,
-		      bool value)
+int print_color_bool(enum output_type type,
+		     enum color_attr color,
+		     const char *key,
+		     const char *fmt,
+		     bool value)
 {
+	int ret = 0;
+
 	if (_IS_JSON_CONTEXT(type)) {
 		if (key)
 			jsonw_bool_field(_jw, key, value);
 		else
 			jsonw_bool(_jw, value);
 	} else if (_IS_FP_CONTEXT(type)) {
-		color_fprintf(stdout, color, fmt, value ? "true" : "false");
+		ret = color_fprintf(stdout, color, fmt,
+				    value ? "true" : "false");
 	}
+
+	return ret;
 }
 
 /*
  * In JSON context uses hardcode %#x format: 42 -> 0x2a
  */
-void print_color_0xhex(enum output_type type,
-		       enum color_attr color,
-		       const char *key,
-		       const char *fmt,
-		       unsigned long long hex)
+int print_color_0xhex(enum output_type type,
+		      enum color_attr color,
+		      const char *key,
+		      const char *fmt,
+		      unsigned long long hex)
 {
+	int ret = 0;
+
 	if (_IS_JSON_CONTEXT(type)) {
 		SPRINT_BUF(b1);
 
 		snprintf(b1, sizeof(b1), "%#llx", hex);
 		print_string(PRINT_JSON, key, NULL, b1);
 	} else if (_IS_FP_CONTEXT(type)) {
-		color_fprintf(stdout, color, fmt, hex);
+		ret = color_fprintf(stdout, color, fmt, hex);
 	}
+
+	return ret;
 }
 
-void print_color_hex(enum output_type type,
-		     enum color_attr color,
-		     const char *key,
-		     const char *fmt,
-		     unsigned int hex)
+int print_color_hex(enum output_type type,
+		    enum color_attr color,
+		    const char *key,
+		    const char *fmt,
+		    unsigned int hex)
 {
+	int ret = 0;
+
 	if (_IS_JSON_CONTEXT(type)) {
 		SPRINT_BUF(b1);
 
@@ -235,28 +252,34 @@
 		else
 			jsonw_string(_jw, b1);
 	} else if (_IS_FP_CONTEXT(type)) {
-		color_fprintf(stdout, color, fmt, hex);
+		ret = color_fprintf(stdout, color, fmt, hex);
 	}
+
+	return ret;
 }
 
 /*
  * In JSON context we don't use the argument "value" we simply call jsonw_null
  * whereas FP context can use "value" to output anything
  */
-void print_color_null(enum output_type type,
-		      enum color_attr color,
-		      const char *key,
-		      const char *fmt,
-		      const char *value)
+int print_color_null(enum output_type type,
+		     enum color_attr color,
+		     const char *key,
+		     const char *fmt,
+		     const char *value)
 {
+	int ret = 0;
+
 	if (_IS_JSON_CONTEXT(type)) {
 		if (key)
 			jsonw_null_field(_jw, key);
 		else
 			jsonw_null(_jw);
 	} else if (_IS_FP_CONTEXT(type)) {
-		color_fprintf(stdout, color, fmt, value);
+		ret = color_fprintf(stdout, color, fmt, value);
 	}
+
+	return ret;
 }
 
 /* Print line separator (if not in JSON mode) */
diff --git a/man/man8/.gitignore b/man/man8/.gitignore
index 0c3d150..7b08e91 100644
--- a/man/man8/.gitignore
+++ b/man/man8/.gitignore
@@ -1,4 +1,5 @@
 # these pages are built
 ip-address.8
 ip-link.8
+ip-netns.8
 ip-route.8
diff --git a/man/man8/Makefile b/man/man8/Makefile
index 0269e17..b1fd87b 100644
--- a/man/man8/Makefile
+++ b/man/man8/Makefile
@@ -1,18 +1,16 @@
 # SPDX-License-Identifier: GPL-2.0
-TARGETS = ip-address.8 ip-link.8 ip-route.8
+TARGETS = ip-address.8 ip-link.8 ip-netns.8 ip-route.8
 
 MAN8PAGES = $(TARGETS) $(filter-out $(TARGETS),$(wildcard *.8))
 
 all: $(TARGETS)
 
-ip-address.8: ip-address.8.in
-	sed "s|@SYSCONFDIR@|$(CONFDIR)|g" $< > $@
-
-ip-link.8: ip-link.8.in
-	sed "s|@SYSCONFDIR@|$(CONFDIR)|g" $< > $@
-
-ip-route.8: ip-route.8.in
-	sed "s|@SYSCONFDIR@|$(CONFDIR)|g" $< > $@
+%: %.in
+	sed \
+		-e "s|@NETNS_ETC_DIR@|$(NETNS_ETC_DIR)|g" \
+		-e "s|@NETNS_RUN_DIR@|$(NETNS_RUN_DIR)|g" \
+		-e "s|@SYSCONFDIR@|$(CONFDIR)|g" \
+		$< > $@
 
 distclean: clean
 
diff --git a/man/man8/bridge.8 b/man/man8/bridge.8
index b9bd6bc..71f2e89 100644
--- a/man/man8/bridge.8
+++ b/man/man8/bridge.8
@@ -289,36 +289,49 @@
 .BI priority " PRIO "
 the STP port priority. The priority value is an unsigned 8-bit quantity
 (number between 0 and 255). This metric is used in the designated port an
-droot port selectio algorithms.
+droot port selection algorithms.
 
 .TP
 .BI state " STATE "
-the operation state of the port. This is primarily used by user space STP/RSTP
-implementation. One may enter a lowercased port state name, or one of the
+the operation state of the port. Except state 0 (disable STP or BPDU filter feature),
+this is primarily used by user space STP/RSTP
+implementation. One may enter port state name (case insensitive), or one of the
 numbers below. Negative inputs are ignored, and unrecognized names return an
 error.
 
 .B 0
-- port is DISABLED. Make this port completely inactive.
+- port is in STP
+.B DISABLED
+state. Make this port completely inactive for STP. This is also called
+BPDU filter and could be used to disable STP on an untrusted port, like
+a leaf virtual devices.
 .sp
 
 .B 1
-- STP LISTENING state. Only valid if STP is enabled on the bridge. In this
+- port is in STP
+.B LISTENING
+state. Only valid if STP is enabled on the bridge. In this
 state the port listens for STP BPDUs and drops all other traffic frames.
 .sp
 
 .B 2
-- STP LEARNING state. Only valid if STP is enabled on the bridge. In this
+- port is in STP
+.B LEARNING
+state. Only valid if STP is enabled on the bridge. In this
 state the port will accept traffic only for the purpose of updating MAC
 address tables.
 .sp
 
 .B 3
-- STP FORWARDING state. Port is fully active.
+- port is in STP
+.B FORWARDING
+state. Port is fully active.
 .sp
 
 .B 4
-- STP BLOCKING state. Only valid if STP is enabled on the bridge. This state
+- port is in STP
+.B BLOCKING
+state. Only valid if STP is enabled on the bridge. This state
 is used during the STP election process. In this state, port will only process
 STP BPDUs.
 .sp
@@ -327,12 +340,25 @@
 .BR "guard on " or " guard off "
 Controls whether STP BPDUs will be processed by the bridge port. By default,
 the flag is turned off allowed BPDU processing. Turning this flag on will
-cause the port to stop processing STP BPDUs.
+disables
+the bridge port if a STP BPDU packet is received.
+
+If running Spanning Tree on bridge, hostile devices on the network
+may send BPDU on a port and cause network failure. Setting
+.B guard on
+will detect and stop this by disabling the port.
+The port will be restarted if link is brought down, or
+removed and reattached.  For example if guard is enable on
+eth0:
+
+.B ip link set dev eth0 down; ip link set dev eth0 up
 
 .TP
 .BR "hairpin on " or " hairpin off "
 Controls whether traffic may be send back out of the port on which it was
-received. By default, this flag is turned off and the bridge will not forward
+received. This option is also called reflective relay mode, and is used to support
+basic VEPA (Virtual Ethernet Port Aggregator) capabilities.
+By default, this flag is turned off and the bridge will not forward
 traffic back out of the receiving port.
 
 .TP
@@ -346,6 +372,11 @@
 Controls whether a given port is allowed to become root port or not. Only used
 when STP is enabled on the bridge. By default the flag is off.
 
+This feature is also called root port guard.
+If BPDU is received from a leaf (edge) port, it should not
+be elected as root port. This could be used if using STP on a bridge and the downstream bridges are not fully
+trusted; this prevents a hostile guest from rerouting traffic.
+
 .TP
 .BR "learning on " or " learning off "
 Controls whether a given port will learn MAC addresses from received traffic or
@@ -383,6 +414,32 @@
 Controls whether a given port will replicate packets using unicast
 instead of multicast. By default this flag is off.
 
+This is done by copying the packet per host and
+changing the multicast destination MAC to a unicast one accordingly.
+
+.BR mcast_to_unicast
+works on top of the multicast snooping feature of
+the bridge. Which means unicast copies are only delivered to hosts which
+are interested in it and signalized this via IGMP/MLD reports
+previously.
+
+This feature is intended for interface types which have a more reliable
+and/or efficient way to deliver unicast packets than broadcast ones
+(e.g. WiFi).
+
+However, it should only be enabled on interfaces where no IGMPv2/MLDv1
+report suppression takes place. IGMP/MLD report suppression issue is usually
+overcome by the network daemon (supplicant) enabling AP isolation and
+by that separating all STAs.
+
+Delivery of STA-to-STA IP multicast is made possible again by
+enabling and utilizing the bridge hairpin mode, which considers the
+incoming port as a potential outgoing port, too (see
+.B hairpin
+option).
+Hairpin mode is performed after multicast snooping, therefore leading to
+only deliver reports to STAs running a multicast router.
+
 .TP
 .BR "neigh_suppress on " or " neigh_suppress off "
 Controls whether neigh discovery (arp and nd) proxy and suppression is
@@ -472,7 +529,7 @@
 .B router
 - the destination address is associated with a router.
 Valid if the referenced device is a VXLAN type device and has
-route shortcircuit enabled.
+route short circuit enabled.
 .sp
 
 .B use
diff --git a/man/man8/ip-netns.8 b/man/man8/ip-netns.8.in
similarity index 92%
rename from man/man8/ip-netns.8
rename to man/man8/ip-netns.8.in
index c75917d..2911bdd 100644
--- a/man/man8/ip-netns.8
+++ b/man/man8/ip-netns.8.in
@@ -61,9 +61,9 @@
 the processes share the same default network namespace from the init process.
 
 By convention a named network namespace is an object at
-.BR "/var/run/netns/" NAME
+.BR "@NETNS_RUN_DIR@/" NAME
 that can be opened. The file descriptor resulting from opening
-.BR "/var/run/netns/" NAME
+.BR "@NETNS_RUN_DIR@/" NAME
 refers to the specified network namespace. Holding that file
 descriptor open keeps the network namespace alive. The file
 descriptor can be used with the
@@ -72,13 +72,13 @@
 
 For applications that are aware of network namespaces, the convention
 is to look for global network configuration files first in
-.BR "/etc/netns/" NAME "/"
+.BR "@NETNS_ETC_DIR@/" NAME "/"
 then in
 .BR "/etc/".
 For example, if you want a different version of
 .BR /etc/resolv.conf
 for a network namespace used to isolate your vpn you would name it
-.BR /etc/netns/myvpn/resolv.conf.
+.BR @NETNS_ETC_DIR@/myvpn/resolv.conf.
 
 .B ip netns exec
 automates handling of this configuration, file convention for network
@@ -89,24 +89,24 @@
 .TP
 .B ip netns list - show all of the named network namespaces
 .sp
-This command displays all of the network namespaces in /var/run/netns
+This command displays all of the network namespaces in @NETNS_RUN_DIR@
 
 .TP
 .B ip netns add NAME - create a new named network namespace
 .sp
-If NAME is available in /var/run/netns/ this command creates a new
+If NAME is available in @NETNS_RUN_DIR@ this command creates a new
 network namespace and assigns NAME.
 
 .TP
 .B ip netns attach NAME PID - create a new named network namespace
 .sp
-If NAME is available in /var/run/netns/ this command attaches the network
+If NAME is available in @NETNS_RUN_DIR@ this command attaches the network
 namespace of the process PID to NAME as if it were created with ip netns.
 
 .TP
 .B ip [-all] netns delete [ NAME ] - delete the name of a network namespace(s)
 .sp
-If NAME is present in /var/run/netns it is umounted and the mount
+If NAME is present in @NETNS_RUN_DIR@ 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 and all physical devices will be moved to the
 default one, otherwise the network namespace persists until it has no more
@@ -160,7 +160,7 @@
 .TP
 .B ip netns identify [PID] - Report network namespaces names for process
 .sp
-This command walks through /var/run/netns and finds all the network
+This command walks through @NETNS_RUN_DIR@ and finds all the network
 namespace names for network namespace of the specified process, if PID is
 not specified then the current process will be used.
 
@@ -201,7 +201,7 @@
 .sp
 Network namespace ids are used to identify a peer network namespace. This
 command displays nsids of the current network namespace and provides the
-corresponding iproute2 netns name (from /var/run/netns) if any.
+corresponding iproute2 netns name (from @NETNS_RUN_DIR@) if any.
 
 The
 .B target-nsid
diff --git a/tc/m_action.c b/tc/m_action.c
index 108329d..66e6724 100644
--- a/tc/m_action.c
+++ b/tc/m_action.c
@@ -177,7 +177,7 @@
 			print_string(PRINT_ANY, NULL, " %s", item->str);
 	}
 	close_json_array(PRINT_JSON, NULL);
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 }
 
 static int parse_hw_stats(const char *str, struct nlmsghdr *n)
@@ -291,7 +291,8 @@
 					invarg(cookie_err_m, *argv);
 				}
 
-				if (hex2mem(*argv, act_ck, slen / 2) < 0)
+				if (slen % 2 ||
+				    hex2mem(*argv, act_ck, slen / 2) < 0)
 					invarg("cookie must be a hex string\n",
 					       *argv);
 
@@ -375,11 +376,11 @@
 
 	if (show_stats && tb[TCA_ACT_STATS]) {
 		print_string(PRINT_FP, NULL, "\tAction statistics:", NULL);
-		print_string(PRINT_FP, NULL, "%s", _SL_);
+		print_nl();
 		open_json_object("stats");
 		print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL);
 		close_json_object();
-		print_string(PRINT_FP, NULL, "%s", _SL_);
+		print_nl();
 	}
 	if (tb[TCA_ACT_COOKIE]) {
 		int strsz = RTA_PAYLOAD(tb[TCA_ACT_COOKIE]);
@@ -388,7 +389,7 @@
 		print_string(PRINT_ANY, "cookie", "\tcookie %s",
 			     hexstring_n2a(RTA_DATA(tb[TCA_ACT_COOKIE]),
 					   strsz, b1, sizeof(b1)));
-		print_string(PRINT_FP, NULL, "%s", _SL_);
+		print_nl();
 	}
 	if (tb[TCA_ACT_FLAGS]) {
 		struct nla_bitfield32 *flags = RTA_DATA(tb[TCA_ACT_FLAGS]);
@@ -397,7 +398,7 @@
 			print_bool(PRINT_ANY, "no_percpu", "\tno_percpu",
 				   flags->value &
 				   TCA_ACT_FLAGS_NO_PERCPU_STATS);
-		print_string(PRINT_FP, NULL, "%s", _SL_);
+		print_nl();
 	}
 	if (tb[TCA_ACT_HW_STATS])
 		print_hw_stats(tb[TCA_ACT_HW_STATS], false);
@@ -457,7 +458,7 @@
 	for (i = 0; i <= tot_acts; i++) {
 		if (tb[i]) {
 			open_json_object(NULL);
-			print_string(PRINT_FP, NULL, "%s", _SL_);
+			print_nl();
 			print_uint(PRINT_ANY, "order",
 				   "\taction order %u: ", i);
 			if (tc_print_one_action(f, tb[i]) < 0) {
@@ -496,7 +497,7 @@
 	open_json_object(NULL);
 	print_uint(PRINT_ANY, "total acts", "total acts %u",
 		   tot_acts ? *tot_acts : 0);
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 	close_json_object();
 	if (tb[TCA_ACT_TAB] == NULL) {
 		if (n->nlmsg_type != RTM_GETACTION)
diff --git a/tc/m_connmark.c b/tc/m_connmark.c
index eac2348..4b2dc4e 100644
--- a/tc/m_connmark.c
+++ b/tc/m_connmark.c
@@ -125,7 +125,7 @@
 	print_uint(PRINT_ANY, "zone", "zone %u", ci->zone);
 	print_action_control(f, " ", ci->action, "");
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 	print_uint(PRINT_ANY, "index", "\t index %u", ci->index);
 	print_int(PRINT_ANY, "ref", " ref %d", ci->refcnt);
 	print_int(PRINT_ANY, "bind", " bind %d", ci->bindcnt);
@@ -137,7 +137,7 @@
 			print_tm(f, tm);
 		}
 	}
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 
 	return 0;
 }
diff --git a/tc/m_ctinfo.c b/tc/m_ctinfo.c
index 5e451f8..e5c1b43 100644
--- a/tc/m_ctinfo.c
+++ b/tc/m_ctinfo.c
@@ -238,7 +238,7 @@
 	print_hu(PRINT_ANY, "zone", "zone %u", zone);
 	print_action_control(f, " ", ci->action, "");
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 	print_uint(PRINT_ANY, "index", "\t index %u", ci->index);
 	print_int(PRINT_ANY, "ref", " ref %d", ci->refcnt);
 	print_int(PRINT_ANY, "bind", " bind %d", ci->bindcnt);
@@ -256,7 +256,7 @@
 	if (show_stats)
 		print_ctinfo_stats(f, tb);
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 
 	return 0;
 }
diff --git a/tc/m_ife.c b/tc/m_ife.c
index 7c612c0..6a85e08 100644
--- a/tc/m_ife.c
+++ b/tc/m_ife.c
@@ -311,7 +311,7 @@
 					 sizeof(b2)));
 	}
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 	print_uint(PRINT_ANY, "index", "\t index %u", p->index);
 	print_int(PRINT_ANY, "ref", " ref %d", p->refcnt);
 	print_int(PRINT_ANY, "bind", " bind %d", p->bindcnt);
@@ -324,7 +324,7 @@
 		}
 	}
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 
 	return 0;
 }
diff --git a/tc/m_mpls.c b/tc/m_mpls.c
index 50eba01..3d5d9b2 100644
--- a/tc/m_mpls.c
+++ b/tc/m_mpls.c
@@ -265,7 +265,7 @@
 		}
 	}
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 
 	return 0;
 }
diff --git a/tc/m_nat.c b/tc/m_nat.c
index c4b02a8..56e8f47 100644
--- a/tc/m_nat.c
+++ b/tc/m_nat.c
@@ -172,7 +172,7 @@
 		     format_host_r(AF_INET, 4, &sel->new_addr, buf1, sizeof(buf1)));
 
 	print_action_control(f, " ", sel->action, "");
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 	print_uint(PRINT_ANY, "index", "\t index %u", sel->index);
 	print_int(PRINT_ANY, "ref", " ref %d", sel->refcnt);
 	print_int(PRINT_ANY, "bind", " bind %d", sel->bindcnt);
@@ -185,7 +185,7 @@
 		}
 	}
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 
 	return 0;
 }
diff --git a/tc/m_sample.c b/tc/m_sample.c
index c068e63..4a30513 100644
--- a/tc/m_sample.c
+++ b/tc/m_sample.c
@@ -167,7 +167,7 @@
 
 	print_action_control(f, " ", p->action, "");
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 	print_uint(PRINT_ANY, "index", "\t index %u", p->index);
 	print_int(PRINT_ANY, "ref", " ref %d", p->refcnt);
 	print_int(PRINT_ANY, "bind", " bind %d", p->bindcnt);
@@ -179,7 +179,7 @@
 			print_tm(f, tm);
 		}
 	}
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 	return 0;
 }
 
diff --git a/tc/m_skbedit.c b/tc/m_skbedit.c
index 761cad5..9afe2f0 100644
--- a/tc/m_skbedit.c
+++ b/tc/m_skbedit.c
@@ -254,7 +254,7 @@
 
 	print_action_control(f, " ", p->action, "");
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 	print_uint(PRINT_ANY, "index", "\t index %u", p->index);
 	print_int(PRINT_ANY, "ref", " ref %d", p->refcnt);
 	print_int(PRINT_ANY, "bind", " bind %d", p->bindcnt);
@@ -267,7 +267,7 @@
 		}
 	}
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 
 	return 0;
 }
diff --git a/tc/m_tunnel_key.c b/tc/m_tunnel_key.c
index a56fe24..bfec907 100644
--- a/tc/m_tunnel_key.c
+++ b/tc/m_tunnel_key.c
@@ -489,7 +489,7 @@
 	else
 		return;
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 	if (matches(name, "src_ip") == 0)
 		print_string(PRINT_ANY, "src_ip", "\tsrc_ip %s",
 			     rt_addr_n2a_rta(family, attr));
@@ -503,7 +503,7 @@
 {
 	if (!attr)
 		return;
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 	print_uint(PRINT_ANY, "key_id", "\tkey_id %u", rta_getattr_be32(attr));
 }
 
@@ -512,7 +512,7 @@
 {
 	if (!attr)
 		return;
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 	print_uint(PRINT_ANY, "dst_port", "\tdst_port %u",
 		   rta_getattr_be16(attr));
 }
@@ -523,7 +523,7 @@
 {
 	if (!attr)
 		return;
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 	print_string(PRINT_ANY, "flag", "\t%s",
 		     rta_getattr_u8(attr) ? name_on : name_off);
 }
@@ -655,11 +655,11 @@
 		return;
 
 	if (matches(name, "tos") == 0 && rta_getattr_u8(attr) != 0) {
-		print_string(PRINT_FP, NULL, "%s", _SL_);
+		print_nl();
 		print_uint(PRINT_ANY, "tos", "\ttos 0x%x",
 			   rta_getattr_u8(attr));
 	} else if (matches(name, "ttl") == 0 && rta_getattr_u8(attr) != 0) {
-		print_string(PRINT_FP, NULL, "%s", _SL_);
+		print_nl();
 		print_uint(PRINT_ANY, "ttl", "\tttl %u",
 			   rta_getattr_u8(attr));
 	}
@@ -712,7 +712,7 @@
 	}
 	print_action_control(f, " ", parm->action, "");
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 	print_uint(PRINT_ANY, "index", "\t index %u", parm->index);
 	print_int(PRINT_ANY, "ref", " ref %d", parm->refcnt);
 	print_int(PRINT_ANY, "bind", " bind %d", parm->bindcnt);
@@ -725,7 +725,7 @@
 		}
 	}
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 
 	return 0;
 }
diff --git a/tc/q_cake.c b/tc/q_cake.c
index 3c78b17..bf116e8 100644
--- a/tc/q_cake.c
+++ b/tc/q_cake.c
@@ -97,6 +97,7 @@
 	unsigned int interval = 0;
 	unsigned int diffserv = 0;
 	unsigned int memlimit = 0;
+	unsigned int fwmark = 0;
 	unsigned int target = 0;
 	__u64 bandwidth = 0;
 	int ack_filter = -1;
@@ -107,7 +108,6 @@
 	int autorate = -1;
 	int ingress = -1;
 	int overhead = 0;
-	int fwmark = -1;
 	int wash = -1;
 	int nat = -1;
 	int atm = -1;
@@ -335,15 +335,12 @@
 				return -1;
 			}
 		} else if (strcmp(*argv, "fwmark") == 0) {
-			unsigned int fwm;
-
 			NEXT_ARG();
-			if (get_u32(&fwm, *argv, 0)) {
+			if (get_u32(&fwmark, *argv, 0)) {
 				fprintf(stderr,
 					"Illegal value for \"fwmark\": \"%s\"\n", *argv);
 				return -1;
 			}
-			fwmark = fwm;
 		} else if (strcmp(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -388,7 +385,7 @@
 	if (memlimit)
 		addattr_l(n, 1024, TCA_CAKE_MEMORY, &memlimit,
 			  sizeof(memlimit));
-	if (fwmark != -1)
+	if (fwmark)
 		addattr_l(n, 1024, TCA_CAKE_FWMARK, &fwmark,
 			  sizeof(fwmark));
 	if (nat != -1)
@@ -523,6 +520,10 @@
 	    RTA_PAYLOAD(tb[TCA_CAKE_RTT]) >= sizeof(__u32)) {
 		interval = rta_getattr_u32(tb[TCA_CAKE_RTT]);
 	}
+	if (tb[TCA_CAKE_MEMORY] &&
+		RTA_PAYLOAD(tb[TCA_CAKE_MEMORY]) >= sizeof(__u32)) {
+		memlimit = rta_getattr_u32(tb[TCA_CAKE_MEMORY]);
+	}
 	if (tb[TCA_CAKE_FWMARK] &&
 	    RTA_PAYLOAD(tb[TCA_CAKE_FWMARK]) >= sizeof(__u32)) {
 		fwmark = rta_getattr_u32(tb[TCA_CAKE_FWMARK]);
@@ -575,7 +576,7 @@
 
 	if (memlimit) {
 		print_uint(PRINT_JSON, "memlimit", NULL, memlimit);
-		print_string(PRINT_FP, NULL, "memlimit %s",
+		print_string(PRINT_FP, NULL, "memlimit %s ",
 			     sprint_size(memlimit, b1));
 	}
 
diff --git a/tc/q_fq.c b/tc/q_fq.c
index 44d8a7e..ffae052 100644
--- a/tc/q_fq.c
+++ b/tc/q_fq.c
@@ -57,6 +57,7 @@
 		"		[ [no]pacing ] [ refill_delay TIME ]\n"
 		"		[ low_rate_threshold RATE ]\n"
 		"		[ orphan_mask MASK]\n"
+		"		[ timer_slack TIME]\n"
 		"		[ ce_threshold TIME ]\n");
 }
 
@@ -86,6 +87,7 @@
 	unsigned int refill_delay;
 	unsigned int orphan_mask;
 	unsigned int ce_threshold;
+	unsigned int timer_slack;
 	bool set_plimit = false;
 	bool set_flow_plimit = false;
 	bool set_quantum = false;
@@ -96,6 +98,7 @@
 	bool set_orphan_mask = false;
 	bool set_low_rate_threshold = false;
 	bool set_ce_threshold = false;
+	bool set_timer_slack = false;
 	int pacing = -1;
 	struct rtattr *tail;
 
@@ -146,6 +149,20 @@
 				return -1;
 			}
 			set_ce_threshold = true;
+		} else if (strcmp(*argv, "timer_slack") == 0) {
+			__s64 t64;
+
+			NEXT_ARG();
+			if (get_time64(&t64, *argv)) {
+				fprintf(stderr, "Illegal \"timer_slack\"\n");
+				return -1;
+			}
+			timer_slack = t64;
+			if (timer_slack != t64) {
+				fprintf(stderr, "Illegal (out of range) \"timer_slack\"\n");
+				return -1;
+			}
+			set_timer_slack = true;
 		} else if (strcmp(*argv, "defrate") == 0) {
 			NEXT_ARG();
 			if (strchr(*argv, '%')) {
@@ -240,6 +257,9 @@
 	if (set_ce_threshold)
 		addattr_l(n, 1024, TCA_FQ_CE_THRESHOLD,
 			  &ce_threshold, sizeof(ce_threshold));
+    if (set_timer_slack)
+		addattr_l(n, 1024, TCA_FQ_TIMER_SLACK,
+			  &timer_slack, sizeof(timer_slack));
 	addattr_nest_end(n, tail);
 	return 0;
 }
@@ -254,6 +274,7 @@
 	unsigned int refill_delay;
 	unsigned int orphan_mask;
 	unsigned int ce_threshold;
+	unsigned int timer_slack;
 
 	SPRINT_BUF(b1);
 
@@ -355,6 +376,12 @@
 		}
 	}
 
+	if (tb[TCA_FQ_TIMER_SLACK] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_TIMER_SLACK]) >= sizeof(__u32)) {
+		timer_slack = rta_getattr_u32(tb[TCA_FQ_TIMER_SLACK]);
+		fprintf(f, "timer_slack %s ", sprint_time64(timer_slack, b1));
+	}
+
 	return 0;
 }
 
diff --git a/tc/q_fq_codel.c b/tc/q_fq_codel.c
index efed4d2..1a51302 100644
--- a/tc/q_fq_codel.c
+++ b/tc/q_fq_codel.c
@@ -54,12 +54,14 @@
 					"[ memory_limit BYTES ]\n"
 					"[ target TIME ] [ interval TIME ]\n"
 					"[ quantum BYTES ] [ [no]ecn ]\n"
-					"[ ce_threshold TIME ]\n");
+					"[ ce_threshold TIME ]\n"
+					"[ drop_batch SIZE ]\n");
 }
 
 static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv,
 			      struct nlmsghdr *n, const char *dev)
 {
+	unsigned int drop_batch = 0;
 	unsigned int limit = 0;
 	unsigned int flows = 0;
 	unsigned int target = 0;
@@ -89,6 +91,12 @@
 				fprintf(stderr, "Illegal \"quantum\"\n");
 				return -1;
 			}
+		} else if (strcmp(*argv, "drop_batch") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&drop_batch, *argv, 0)) {
+				fprintf(stderr, "Illegal \"drop_batch\"\n");
+				return -1;
+			}
 		} else if (strcmp(*argv, "target") == 0) {
 			NEXT_ARG();
 			if (get_time(&target, *argv)) {
@@ -147,6 +155,8 @@
 	if (memory != ~0U)
 		addattr_l(n, 1024, TCA_FQ_CODEL_MEMORY_LIMIT,
 			  &memory, sizeof(memory));
+	if (drop_batch)
+		addattr_l(n, 1024, TCA_FQ_CODEL_DROP_BATCH_SIZE, &drop_batch, sizeof(drop_batch));
 
 	addattr_nest_end(n, tail);
 	return 0;
@@ -163,6 +173,7 @@
 	unsigned int quantum;
 	unsigned int ce_threshold;
 	unsigned int memory_limit;
+	unsigned int drop_batch;
 
 	SPRINT_BUF(b1);
 
@@ -220,6 +231,12 @@
 		if (ecn)
 			print_bool(PRINT_ANY, "ecn", "ecn ", true);
 	}
+	if (tb[TCA_FQ_CODEL_DROP_BATCH_SIZE] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_DROP_BATCH_SIZE]) >= sizeof(__u32)) {
+		drop_batch = rta_getattr_u32(tb[TCA_FQ_CODEL_DROP_BATCH_SIZE]);
+		if (drop_batch)
+			print_uint(PRINT_ANY, "drop_batch", "drop_batch %u ", drop_batch);
+	}
 
 	return 0;
 }
@@ -264,7 +281,7 @@
 			st->qdisc_stats.old_flows_len);
 	}
 	if (st->type == TCA_FQ_CODEL_XSTATS_CLASS) {
-		print_uint(PRINT_ANY, "deficit", "  deficit %u",
+		print_int(PRINT_ANY, "deficit", "  deficit %d",
 			st->class_stats.deficit);
 		print_uint(PRINT_ANY, "count", " count %u",
 			st->class_stats.count);
diff --git a/tc/q_taprio.c b/tc/q_taprio.c
index b995443..e43db9d 100644
--- a/tc/q_taprio.c
+++ b/tc/q_taprio.c
@@ -368,7 +368,7 @@
 
 	open_json_array(PRINT_JSON, "schedule");
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 
 	for (item = RTA_DATA(list); RTA_OK(item, rem); item = RTA_NEXT(item, rem)) {
 		struct rtattr *tb[TCA_TAPRIO_SCHED_ENTRY_MAX + 1];
@@ -396,7 +396,7 @@
 		print_uint(PRINT_ANY, "interval", " interval %u", interval);
 		close_json_object();
 
-		print_string(PRINT_FP, NULL, "%s", _SL_);
+		print_nl();
 	}
 
 	close_json_array(PRINT_ANY, "");
@@ -454,7 +454,7 @@
 		print_uint(PRINT_ANY, NULL, " %u", qopt->prio_tc_map[i]);
 	close_json_array(PRINT_ANY, "");
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 
 	open_json_array(PRINT_ANY, "queues");
 	for (i = 0; i < qopt->num_tc; i++) {
@@ -465,7 +465,7 @@
 	}
 	close_json_array(PRINT_ANY, "");
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 
 	if (tb[TCA_TAPRIO_ATTR_SCHED_CLOCKID])
 		clockid = rta_getattr_s32(tb[TCA_TAPRIO_ATTR_SCHED_CLOCKID]);
diff --git a/tc/tc_util.c b/tc/tc_util.c
index 5f13d72..12f865c 100644
--- a/tc/tc_util.c
+++ b/tc/tc_util.c
@@ -385,6 +385,11 @@
 	}
 
 	*size = sz;
+
+	/* detect if an overflow happened */
+	if (*size != floor(sz))
+		return -1;
+
 	return 0;
 }
 
@@ -783,7 +788,7 @@
 			   sizeof(bs)));
 
 		if (bs.bytes >= bs_hw.bytes && bs.packets >= bs_hw.packets) {
-			print_string(PRINT_FP, NULL, "%s", _SL_);
+			print_nl();
 			print_string(PRINT_FP, NULL, "%s", prefix);
 			print_lluint(PRINT_ANY, "sw_bytes",
 				     "Sent software %llu bytes",
@@ -793,7 +798,7 @@
 		}
 	}
 
-	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_nl();
 	print_string(PRINT_FP, NULL, "%s", prefix);
 	print_lluint(PRINT_ANY, "hw_bytes", "Sent hardware %llu bytes",
 		     bs_hw.bytes);
diff --git a/testsuite/tests/bridge/vlan/show.t b/testsuite/tests/bridge/vlan/show.t
new file mode 100755
index 0000000..3def202
--- /dev/null
+++ b/testsuite/tests/bridge/vlan/show.t
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+. lib/generic.sh
+
+ts_log "[Testing vlan show]"
+
+BR_DEV="$(rand_dev)"
+VX0_DEV="$(rand_dev)"
+VX1_DEV="$(rand_dev)"
+
+ts_ip "$0" "Add $BR_DEV bridge interface" link add $BR_DEV type bridge
+
+ts_ip "$0" "Add $VX0_DEV vxlan interface" \
+	link add $VX0_DEV type vxlan dstport 4789 external
+ts_ip "$0" "Enslave $VX0_DEV under $BR_DEV" \
+	link set dev $VX0_DEV master $BR_DEV
+ts_bridge "$0" "Delete default vlan from $VX0_DEV" \
+	vlan del dev $VX0_DEV vid 1
+ts_ip "$0" "Add $VX1_DEV vxlan interface" \
+	link add $VX1_DEV type vxlan dstport 4790 external
+ts_ip "$0" "Enslave $VX1_DEV under $BR_DEV" \
+	link set dev $VX1_DEV master $BR_DEV
+
+# Test that bridge ports without vlans do not appear in the output
+ts_bridge "$0" "Show vlan" vlan
+test_on_not "$VX0_DEV"
+
+# Test that bridge ports without tunnels do not appear in the output
+ts_bridge "$0" "Show vlan tunnel info" vlan tunnelshow
+test_lines_count 1 # header only
diff --git a/testsuite/tests/bridge/vlan/tunnelshow.t b/testsuite/tests/bridge/vlan/tunnelshow.t
index fd41bfc..3e9c12a 100755
--- a/testsuite/tests/bridge/vlan/tunnelshow.t
+++ b/testsuite/tests/bridge/vlan/tunnelshow.t
@@ -28,6 +28,6 @@
 
 ts_bridge "$0" "Show tunnel info" vlan tunnelshow dev $VX_DEV
 test_on "1030\s+65556"
-test_lines_count 5
+test_lines_count 4
 
 ts_bridge "$0" "Dump tunnel info" -j vlan tunnelshow dev $VX_DEV