diff --git a/patches/lede/0067-ebtables-add-support-for-ICMP-IGMP-type-matches.patch b/patches/lede/0067-ebtables-add-support-for-ICMP-IGMP-type-matches.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c5b4de9a0999a3c705953a21164ca8b569aab85d
--- /dev/null
+++ b/patches/lede/0067-ebtables-add-support-for-ICMP-IGMP-type-matches.patch
@@ -0,0 +1,1183 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Sun, 4 Mar 2018 10:26:34 +0100
+Subject: ebtables: add support for ICMP/IGMP type matches
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+
+diff --git a/package/network/utils/ebtables/patches/301-0001-include-sync-linux-netfilter_bridge-ebt_ip.h-with-ke.patch b/package/network/utils/ebtables/patches/301-0001-include-sync-linux-netfilter_bridge-ebt_ip.h-with-ke.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..b6f42c6a4affc9bb04a6ac9269698e751a932f2a
+--- /dev/null
++++ b/package/network/utils/ebtables/patches/301-0001-include-sync-linux-netfilter_bridge-ebt_ip.h-with-ke.patch
+@@ -0,0 +1,56 @@
++From 44fcc3392bb7d52df2ad52b8e9437255b8c29d5a Mon Sep 17 00:00:00 2001
++Message-Id: <44fcc3392bb7d52df2ad52b8e9437255b8c29d5a.1520151963.git.mschiffer@universe-factory.net>
++In-Reply-To: <cover.1520150717.git.mschiffer@universe-factory.net>
++References: <cover.1520150717.git.mschiffer@universe-factory.net>
++From: Matthias Schiffer <mschiffer@universe-factory.net>
++Date: Sat, 3 Mar 2018 12:14:48 +0100
++Subject: [PATCH ebtables 1/4] include: sync linux/netfilter_bridge/ebt_ip.h
++ with kernel
++
++Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
++---
++ include/linux/netfilter_bridge/ebt_ip.h | 16 +++++++++++++---
++ 1 file changed, 13 insertions(+), 3 deletions(-)
++
++diff --git a/include/linux/netfilter_bridge/ebt_ip.h b/include/linux/netfilter_bridge/ebt_ip.h
++index c4bbc41b0ea4..46d6261370b0 100644
++--- a/include/linux/netfilter_bridge/ebt_ip.h
+++++ b/include/linux/netfilter_bridge/ebt_ip.h
++@@ -1,3 +1,4 @@
+++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
++ /*
++  *  ebt_ip
++  *
++@@ -23,8 +24,10 @@
++ #define EBT_IP_PROTO 0x08
++ #define EBT_IP_SPORT 0x10
++ #define EBT_IP_DPORT 0x20
+++#define EBT_IP_ICMP 0x40
+++#define EBT_IP_IGMP 0x80
++ #define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
++- EBT_IP_SPORT | EBT_IP_DPORT )
+++		     EBT_IP_SPORT | EBT_IP_DPORT | EBT_IP_ICMP | EBT_IP_IGMP)
++ #define EBT_IP_MATCH "ip"
++ 
++ /* the same values are used for the invflags */
++@@ -37,8 +40,15 @@ struct ebt_ip_info {
++ 	__u8  protocol;
++ 	__u8  bitmask;
++ 	__u8  invflags;
++-	__u16 sport[2];
++-	__u16 dport[2];
+++	union {
+++		__u16 sport[2];
+++		__u8 icmp_type[2];
+++		__u8 igmp_type[2];
+++	};
+++	union {
+++		__u16 dport[2];
+++		__u8 icmp_code[2];
+++	};
++ };
++ 
++ #endif
++-- 
++2.16.2
++
+diff --git a/package/network/utils/ebtables/patches/301-0002-Move-ICMP-type-handling-functions-from-ebt_ip6-to-us.patch b/package/network/utils/ebtables/patches/301-0002-Move-ICMP-type-handling-functions-from-ebt_ip6-to-us.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..c3eaec4412fcaebffa84b409a0a66e2f714a0e8d
+--- /dev/null
++++ b/package/network/utils/ebtables/patches/301-0002-Move-ICMP-type-handling-functions-from-ebt_ip6-to-us.patch
+@@ -0,0 +1,465 @@
++From e40c68fcf13e2244ab6c87844126167e998ccb56 Mon Sep 17 00:00:00 2001
++Message-Id: <e40c68fcf13e2244ab6c87844126167e998ccb56.1520151963.git.mschiffer@universe-factory.net>
++In-Reply-To: <cover.1520150717.git.mschiffer@universe-factory.net>
++References: <cover.1520150717.git.mschiffer@universe-factory.net>
++From: Matthias Schiffer <mschiffer@universe-factory.net>
++Date: Sun, 4 Mar 2018 08:18:18 +0100
++Subject: [PATCH ebtables 2/4] Move ICMP type handling functions from ebt_ip6
++ to useful_functions.c
++
++Allow using these functions for ebt_ip as well.
++
++Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
++---
++ extensions/ebt_ip6.c | 165 +++------------------------------------------------
++ include/ebtables_u.h |  17 +++++-
++ useful_functions.c   | 151 +++++++++++++++++++++++++++++++++++++++++++++-
++ 3 files changed, 174 insertions(+), 159 deletions(-)
++
++diff --git a/extensions/ebt_ip6.c b/extensions/ebt_ip6.c
++index dd48547b0010..347797b4afe1 100644
++--- a/extensions/ebt_ip6.c
+++++ b/extensions/ebt_ip6.c
++@@ -11,9 +11,6 @@
++  *
++  */
++ 
++-#include <errno.h>
++-#include <inttypes.h>
++-#include <limits.h>
++ #include <stdio.h>
++ #include <stdlib.h>
++ #include <string.h>
++@@ -51,13 +48,7 @@ static const struct option opts[] =
++ };
++ 
++ 
++-struct icmpv6_names {
++-	const char *name;
++-	uint8_t type;
++-	uint8_t code_min, code_max;
++-};
++-
++-static const struct icmpv6_names icmpv6_codes[] = {
+++static const struct ebt_icmp_names icmpv6_codes[] = {
++ 	{ "destination-unreachable", 1, 0, 0xFF },
++ 	{ "no-route", 1, 0, 0 },
++ 	{ "communication-prohibited", 1, 1, 1 },
++@@ -141,97 +132,6 @@ parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
++ 	free(buffer);
++ }
++ 
++-static char*
++-parse_num(const char *str, long min, long max, long *num)
++-{
++-	char *end;
++-
++-	errno = 0;
++-	*num = strtol(str, &end, 10);
++-	if (errno && (*num == LONG_MIN || *num == LONG_MAX)) {
++-		ebt_print_error("Invalid number %s: %s", str, strerror(errno));
++-		return NULL;
++-	}
++-	if (min <= max) {
++-		if (*num > max || *num < min) {
++-			ebt_print_error("Value %ld out of range (%ld, %ld)", *num, min, max);
++-			return NULL;
++-		}
++-	}
++-	if (*num == 0 && str == end)
++-		return NULL;
++-	return end;
++-}
++-
++-static char *
++-parse_range(const char *str, long min, long max, long num[])
++-{
++-	char *next;
++-
++-	next = parse_num(str, min, max, num);
++-	if (next == NULL)
++-		return NULL;
++-	if (next && *next == ':')
++-		next = parse_num(next+1, min, max, &num[1]);
++-	else
++-		num[1] = num[0];
++-	return next;
++-}
++-
++-static int
++-parse_icmpv6(const char *icmpv6type, uint8_t type[], uint8_t code[])
++-{
++-	static const unsigned int limit = ARRAY_SIZE(icmpv6_codes);
++-	unsigned int match = limit;
++-	unsigned int i;
++-	long number[2];
++-
++-	for (i = 0; i < limit; i++) {
++-		if (strncasecmp(icmpv6_codes[i].name, icmpv6type, strlen(icmpv6type)))
++-			continue;
++-		if (match != limit)
++-			ebt_print_error("Ambiguous ICMPv6 type `%s':"
++-					" `%s' or `%s'?",
++-					icmpv6type, icmpv6_codes[match].name,
++-					icmpv6_codes[i].name);
++-		match = i;
++-	}
++-
++-	if (match < limit) {
++-		type[0] = type[1] = icmpv6_codes[match].type;
++-		code[0] = icmpv6_codes[match].code_min;
++-		code[1] = icmpv6_codes[match].code_max;
++-	} else {
++-		char *next = parse_range(icmpv6type, 0, 255, number);
++-		if (!next) {
++-			ebt_print_error("Unknown ICMPv6 type `%s'",
++-							icmpv6type);
++-			return -1;
++-		}
++-		type[0] = (uint8_t) number[0];
++-		type[1] = (uint8_t) number[1];
++-		switch (*next) {
++-		case 0:
++-			code[0] = 0;
++-			code[1] = 255;
++-			return 0;
++-		case '/':
++-			next = parse_range(next+1, 0, 255, number);
++-			code[0] = (uint8_t) number[0];
++-			code[1] = (uint8_t) number[1];
++-			if (next == NULL)
++-				return -1;
++-			if (next && *next == 0)
++-				return 0;
++-		/* fallthrough */
++-		default:
++-			ebt_print_error("unknown character %c", *next);
++-			return -1;
++-		}
++-	}
++-	return 0;
++-}
++-
++ static void print_port_range(uint16_t *ports)
++ {
++ 	if (ports[0] == ports[1])
++@@ -240,58 +140,6 @@ static void print_port_range(uint16_t *ports)
++ 		printf("%d:%d ", ports[0], ports[1]);
++ }
++ 
++-static void print_icmp_code(uint8_t *code)
++-{
++-	if (code[0] == code[1])
++-		printf("/%"PRIu8 " ", code[0]);
++-	else
++-		printf("/%"PRIu8":%"PRIu8 " ", code[0], code[1]);
++-}
++-
++-static void print_icmp_type(uint8_t *type, uint8_t *code)
++-{
++-	unsigned int i;
++-
++-	if (type[0] != type[1]) {
++-		printf("%"PRIu8 ":%" PRIu8, type[0], type[1]);
++-		print_icmp_code(code);
++-		return;
++-	}
++-
++-	for (i = 0; i < ARRAY_SIZE(icmpv6_codes); i++) {
++-		if (icmpv6_codes[i].type != type[0])
++-			continue;
++-
++-		if (icmpv6_codes[i].code_min == code[0] &&
++-		    icmpv6_codes[i].code_max == code[1]) {
++-			printf("%s ", icmpv6_codes[i].name);
++-			return;
++-		}
++-	}
++-	printf("%"PRIu8, type[0]);
++-	print_icmp_code(code);
++-}
++-
++-static void print_icmpv6types(void)
++-{
++-	unsigned int i;
++-        printf("Valid ICMPv6 Types:");
++-
++-	for (i=0; i < ARRAY_SIZE(icmpv6_codes); i++) {
++-		if (i && icmpv6_codes[i].type == icmpv6_codes[i-1].type) {
++-			if (icmpv6_codes[i].code_min == icmpv6_codes[i-1].code_min
++-			    && (icmpv6_codes[i].code_max
++-			        == icmpv6_codes[i-1].code_max))
++-				printf(" (%s)", icmpv6_codes[i].name);
++-			else
++-				printf("\n   %s", icmpv6_codes[i].name);
++-		}
++-		else
++-			printf("\n%s", icmpv6_codes[i].name);
++-	}
++-	printf("\n");
++-}
++-
++ static void print_help()
++ {
++ 	printf(
++@@ -303,7 +151,9 @@ static void print_help()
++ "--ip6-sport  [!] port[:port]   : tcp/udp source port or port range\n"
++ "--ip6-dport  [!] port[:port]   : tcp/udp destination port or port range\n"
++ "--ip6-icmp-type [!] type[[:type]/code[:code]] : ipv6-icmp type/code or type/code range\n");
++-print_icmpv6types();
+++
+++	printf("\nValid ICMPv6 Types:\n");
+++	ebt_print_icmp_types(icmpv6_codes, ARRAY_SIZE(icmpv6_codes));
++ }
++ 
++ static void init(struct ebt_entry_match *match)
++@@ -374,7 +224,9 @@ static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
++ 		ipinfo->bitmask |= EBT_IP6_ICMP6;
++ 		if (ebt_check_inverse2(optarg))
++ 			ipinfo->invflags |= EBT_IP6_ICMP6;
++-		if (parse_icmpv6(optarg, ipinfo->icmpv6_type, ipinfo->icmpv6_code))
+++		if (ebt_parse_icmp(icmpv6_codes, ARRAY_SIZE(icmpv6_codes),
+++				   optarg, ipinfo->icmpv6_type,
+++				   ipinfo->icmpv6_code))
++ 			return 0;
++ 		break;
++ 
++@@ -493,7 +345,8 @@ static void print(const struct ebt_u_entry *entry,
++ 		printf("--ip6-icmp-type ");
++ 		if (ipinfo->invflags & EBT_IP6_ICMP6)
++ 			printf("! ");
++-		print_icmp_type(ipinfo->icmpv6_type, ipinfo->icmpv6_code);
+++		ebt_print_icmp_type(icmpv6_codes, ARRAY_SIZE(icmpv6_codes),
+++				    ipinfo->icmpv6_type, ipinfo->icmpv6_code);
++ 	}
++ }
++ 
++diff --git a/include/ebtables_u.h b/include/ebtables_u.h
++index 35a5bcc54c86..17afa9487f5a 100644
++--- a/include/ebtables_u.h
+++++ b/include/ebtables_u.h
++@@ -222,6 +222,15 @@ struct ebt_u_target
++ 	struct ebt_u_target *next;
++ };
++ 
+++
+++struct ebt_icmp_names {
+++	const char *name;
+++	uint8_t type;
+++	uint8_t code_min, code_max;
+++};
+++
+++
+++
++ /* libebtc.c */
++ 
++ extern struct ebt_u_table *ebt_tables;
++@@ -300,11 +309,17 @@ void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask)
++ int ebt_get_mac_and_mask(const char *from, unsigned char *to, unsigned char *mask);
++ void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk);
++ char *ebt_mask_to_dotted(uint32_t mask);
++-void ebt_parse_ip6_address(char *address, struct in6_addr *addr, 
+++void ebt_parse_ip6_address(char *address, struct in6_addr *addr,
++ 						   struct in6_addr *msk);
++ char *ebt_ip6_to_numeric(const struct in6_addr *addrp);
++ char *ebt_ip6_mask_to_string(const struct in6_addr *msk);
++ 
+++int ebt_parse_icmp(const struct ebt_icmp_names *icmp_codes, size_t n_codes,
+++		   const char *icmptype, uint8_t type[], uint8_t code[]);
+++void ebt_print_icmp_type(const struct ebt_icmp_names *icmp_codes,
+++			 size_t n_codes, uint8_t *type, uint8_t *code);
+++void ebt_print_icmp_types(const struct ebt_icmp_names *icmp_codes,
+++			  size_t n_codes);
++ 
++ int do_command(int argc, char *argv[], int exec_style,
++                struct ebt_u_replace *replace_);
++diff --git a/useful_functions.c b/useful_functions.c
++index d14cbe9dbdba..8f54bae83fae 100644
++--- a/useful_functions.c
+++++ b/useful_functions.c
++@@ -24,6 +24,9 @@
++  */
++ #include "include/ebtables_u.h"
++ #include "include/ethernetdb.h"
+++#include <errno.h>
+++#include <inttypes.h>
+++#include <limits.h>
++ #include <stdio.h>
++ #include <netinet/ether.h>
++ #include <string.h>
++@@ -34,6 +37,7 @@
++ #include <sys/socket.h>
++ #include <arpa/inet.h>
++ 
+++
++ const unsigned char mac_type_unicast[ETH_ALEN] =   {0,0,0,0,0,0};
++ const unsigned char msk_type_unicast[ETH_ALEN] =   {1,0,0,0,0,0};
++ const unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
++@@ -188,7 +192,7 @@ static int undot_ip(char *ip, unsigned char *ip2)
++ 			return -1;
++ 		*q = '\0';
++ 		onebyte = strtol(p, &end, 10);
++-		if (*end != '\0' || onebyte > 255 || onebyte < 0)   
+++		if (*end != '\0' || onebyte > 255 || onebyte < 0)
++ 			return -1;
++ 		ip2[i] = (unsigned char)onebyte;
++ 		p = q + 1;
++@@ -275,7 +279,7 @@ char *ebt_mask_to_dotted(uint32_t mask)
++ 		*buf = '\0';
++ 	else
++ 		/* Mask was not a decent combination of 1's and 0's */
++-		sprintf(buf, "/%d.%d.%d.%d", ((unsigned char *)&mask)[0], 
+++		sprintf(buf, "/%d.%d.%d.%d", ((unsigned char *)&mask)[0],
++ 		   ((unsigned char *)&mask)[1], ((unsigned char *)&mask)[2],
++ 		   ((unsigned char *)&mask)[3]);
++ 
++@@ -424,3 +428,146 @@ char *ebt_ip6_mask_to_string(const struct in6_addr *msk)
++ 		sprintf(buf, "/%s", ebt_ip6_to_numeric(msk));
++ 	return buf;
++ }
+++
+++static char*
+++parse_num(const char *str, long min, long max, long *num)
+++{
+++	char *end;
+++
+++	errno = 0;
+++	*num = strtol(str, &end, 10);
+++	if (errno && (*num == LONG_MIN || *num == LONG_MAX)) {
+++		ebt_print_error("Invalid number %s: %s", str, strerror(errno));
+++		return NULL;
+++	}
+++	if (min <= max) {
+++		if (*num > max || *num < min) {
+++			ebt_print_error("Value %ld out of range (%ld, %ld)", *num, min, max);
+++			return NULL;
+++		}
+++	}
+++	if (*num == 0 && str == end)
+++		return NULL;
+++	return end;
+++}
+++
+++static char *
+++parse_range(const char *str, long min, long max, long num[])
+++{
+++	char *next;
+++
+++	next = parse_num(str, min, max, num);
+++	if (next == NULL)
+++		return NULL;
+++	if (next && *next == ':')
+++		next = parse_num(next+1, min, max, &num[1]);
+++	else
+++		num[1] = num[0];
+++	return next;
+++}
+++
+++int ebt_parse_icmp(const struct ebt_icmp_names *icmp_codes, size_t n_codes,
+++		   const char *icmptype, uint8_t type[], uint8_t code[])
+++{
+++	unsigned int match = n_codes;
+++	unsigned int i;
+++	long number[2];
+++
+++	for (i = 0; i < n_codes; i++) {
+++		if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype)))
+++			continue;
+++		if (match != n_codes)
+++			ebt_print_error("Ambiguous ICMP type `%s':"
+++					" `%s' or `%s'?",
+++					icmptype, icmp_codes[match].name,
+++					icmp_codes[i].name);
+++		match = i;
+++	}
+++
+++	if (match < n_codes) {
+++		type[0] = type[1] = icmp_codes[match].type;
+++		code[0] = icmp_codes[match].code_min;
+++		code[1] = icmp_codes[match].code_max;
+++	} else {
+++		char *next = parse_range(icmptype, 0, 255, number);
+++		if (!next) {
+++			ebt_print_error("Unknown ICMP type `%s'",
+++							icmptype);
+++			return -1;
+++		}
+++		type[0] = (uint8_t) number[0];
+++		type[1] = (uint8_t) number[1];
+++		switch (*next) {
+++		case 0:
+++			code[0] = 0;
+++			code[1] = 255;
+++			return 0;
+++		case '/':
+++			next = parse_range(next+1, 0, 255, number);
+++			code[0] = (uint8_t) number[0];
+++			code[1] = (uint8_t) number[1];
+++			if (next == NULL)
+++				return -1;
+++			if (next && *next == 0)
+++				return 0;
+++		/* fallthrough */
+++		default:
+++			ebt_print_error("unknown character %c", *next);
+++			return -1;
+++		}
+++	}
+++	return 0;
+++}
+++
+++static void print_icmp_code(uint8_t *code)
+++{
+++	if (code[0] == code[1])
+++		printf("/%"PRIu8 " ", code[0]);
+++	else
+++		printf("/%"PRIu8":%"PRIu8 " ", code[0], code[1]);
+++}
+++
+++void ebt_print_icmp_type(const struct ebt_icmp_names *icmp_codes,
+++			 size_t n_codes, uint8_t *type, uint8_t *code)
+++{
+++	unsigned int i;
+++
+++	if (type[0] != type[1]) {
+++		printf("%"PRIu8 ":%" PRIu8, type[0], type[1]);
+++		print_icmp_code(code);
+++		return;
+++	}
+++
+++	for (i = 0; i < n_codes; i++) {
+++		if (icmp_codes[i].type != type[0])
+++			continue;
+++
+++		if (icmp_codes[i].code_min == code[0] &&
+++		    icmp_codes[i].code_max == code[1]) {
+++			printf("%s ", icmp_codes[i].name);
+++			return;
+++		}
+++	}
+++	printf("%"PRIu8, type[0]);
+++	print_icmp_code(code);
+++}
+++
+++void ebt_print_icmp_types(const struct ebt_icmp_names *icmp_codes,
+++			  size_t n_codes)
+++{
+++	unsigned int i;
+++
+++	for (i = 0; i < n_codes; i++) {
+++		if (i && icmp_codes[i].type == icmp_codes[i-1].type) {
+++			if (icmp_codes[i].code_min == icmp_codes[i-1].code_min
+++			    && (icmp_codes[i].code_max
+++			        == icmp_codes[i-1].code_max))
+++				printf(" (%s)", icmp_codes[i].name);
+++			else
+++				printf("\n   %s", icmp_codes[i].name);
+++		}
+++		else
+++			printf("\n%s", icmp_codes[i].name);
+++	}
+++	printf("\n");
+++}
++-- 
++2.16.2
++
+diff --git a/package/network/utils/ebtables/patches/301-0003-ebt_ip-add-support-for-matching-ICMP-type-and-code.patch b/package/network/utils/ebtables/patches/301-0003-ebt_ip-add-support-for-matching-ICMP-type-and-code.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..2c83168933d88535747f0bc18d3ebbe03c6b1f30
+--- /dev/null
++++ b/package/network/utils/ebtables/patches/301-0003-ebt_ip-add-support-for-matching-ICMP-type-and-code.patch
+@@ -0,0 +1,182 @@
++From 76bc7b4ede217228e782a6d4dfaa67f772a08441 Mon Sep 17 00:00:00 2001
++Message-Id: <76bc7b4ede217228e782a6d4dfaa67f772a08441.1520151963.git.mschiffer@universe-factory.net>
++In-Reply-To: <cover.1520150717.git.mschiffer@universe-factory.net>
++References: <cover.1520150717.git.mschiffer@universe-factory.net>
++From: Matthias Schiffer <mschiffer@universe-factory.net>
++Date: Sat, 3 Mar 2018 12:42:46 +0100
++Subject: [PATCH ebtables 3/4] ebt_ip: add support for matching ICMP type and
++ code
++
++We already have ICMPv6 type/code matches. This adds support for IPv4 ICMP
++matches in the same way.
++
++Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
++---
++ extensions/ebt_ip.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++--
++ 1 file changed, 94 insertions(+), 2 deletions(-)
++
++diff --git a/extensions/ebt_ip.c b/extensions/ebt_ip.c
++index 59559feffa50..42660d4564fb 100644
++--- a/extensions/ebt_ip.c
+++++ b/extensions/ebt_ip.c
++@@ -24,6 +24,7 @@
++ #define IP_PROTO  '4'
++ #define IP_SPORT  '5'
++ #define IP_DPORT  '6'
+++#define IP_ICMP   '7'
++ 
++ static const struct option opts[] =
++ {
++@@ -38,9 +39,64 @@ static const struct option opts[] =
++ 	{ "ip-sport"            , required_argument, 0, IP_SPORT  },
++ 	{ "ip-destination-port" , required_argument, 0, IP_DPORT  },
++ 	{ "ip-dport"            , required_argument, 0, IP_DPORT  },
+++	{ "ip-icmp-type"        , required_argument, 0, IP_ICMP   },
++ 	{ 0 }
++ };
++ 
+++static const struct ebt_icmp_names icmp_codes[] = {
+++	{ "echo-reply", 0, 0, 0xFF },
+++	/* Alias */ { "pong", 0, 0, 0xFF },
+++
+++	{ "destination-unreachable", 3, 0, 0xFF },
+++	{   "network-unreachable", 3, 0, 0 },
+++	{   "host-unreachable", 3, 1, 1 },
+++	{   "protocol-unreachable", 3, 2, 2 },
+++	{   "port-unreachable", 3, 3, 3 },
+++	{   "fragmentation-needed", 3, 4, 4 },
+++	{   "source-route-failed", 3, 5, 5 },
+++	{   "network-unknown", 3, 6, 6 },
+++	{   "host-unknown", 3, 7, 7 },
+++	{   "network-prohibited", 3, 9, 9 },
+++	{   "host-prohibited", 3, 10, 10 },
+++	{   "TOS-network-unreachable", 3, 11, 11 },
+++	{   "TOS-host-unreachable", 3, 12, 12 },
+++	{   "communication-prohibited", 3, 13, 13 },
+++	{   "host-precedence-violation", 3, 14, 14 },
+++	{   "precedence-cutoff", 3, 15, 15 },
+++
+++	{ "source-quench", 4, 0, 0xFF },
+++
+++	{ "redirect", 5, 0, 0xFF },
+++	{   "network-redirect", 5, 0, 0 },
+++	{   "host-redirect", 5, 1, 1 },
+++	{   "TOS-network-redirect", 5, 2, 2 },
+++	{   "TOS-host-redirect", 5, 3, 3 },
+++
+++	{ "echo-request", 8, 0, 0xFF },
+++	/* Alias */ { "ping", 8, 0, 0xFF },
+++
+++	{ "router-advertisement", 9, 0, 0xFF },
+++
+++	{ "router-solicitation", 10, 0, 0xFF },
+++
+++	{ "time-exceeded", 11, 0, 0xFF },
+++	/* Alias */ { "ttl-exceeded", 11, 0, 0xFF },
+++	{   "ttl-zero-during-transit", 11, 0, 0 },
+++	{   "ttl-zero-during-reassembly", 11, 1, 1 },
+++
+++	{ "parameter-problem", 12, 0, 0xFF },
+++	{   "ip-header-bad", 12, 0, 0 },
+++	{   "required-option-missing", 12, 1, 1 },
+++
+++	{ "timestamp-request", 13, 0, 0xFF },
+++
+++	{ "timestamp-reply", 14, 0, 0xFF },
+++
+++	{ "address-mask-request", 17, 0, 0xFF },
+++
+++	{ "address-mask-reply", 18, 0, 0xFF }
+++};
+++
++ /* put the mask into 4 bytes */
++ /* transform a protocol and service name into a port number */
++ static uint16_t parse_port(const char *protocol, const char *name)
++@@ -105,7 +161,11 @@ static void print_help()
++ "--ip-tos    [!] tos           : ip tos specification\n"
++ "--ip-proto  [!] protocol      : ip protocol specification\n"
++ "--ip-sport  [!] port[:port]   : tcp/udp source port or port range\n"
++-"--ip-dport  [!] port[:port]   : tcp/udp destination port or port range\n");
+++"--ip-dport  [!] port[:port]   : tcp/udp destination port or port range\n"
+++"--ip-icmp-type [!] type[[:type]/code[:code]] : icmp type/code or type/code range\n");
+++
+++	printf("\nValid ICMP Types:\n");
+++	ebt_print_icmp_types(icmp_codes, ARRAY_SIZE(icmp_codes));
++ }
++ 
++ static void init(struct ebt_entry_match *match)
++@@ -122,6 +182,7 @@ static void init(struct ebt_entry_match *match)
++ #define OPT_PROTO  0x08
++ #define OPT_SPORT  0x10
++ #define OPT_DPORT  0x20
+++#define OPT_ICMP   0x40
++ static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
++    unsigned int *flags, struct ebt_entry_match **match)
++ {
++@@ -170,6 +231,16 @@ static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
++ 			parse_port_range(NULL, optarg, ipinfo->dport);
++ 		break;
++ 
+++	case IP_ICMP:
+++		ebt_check_option2(flags, OPT_ICMP);
+++		ipinfo->bitmask |= EBT_IP_ICMP;
+++		if (ebt_check_inverse2(optarg))
+++			ipinfo->invflags |= EBT_IP_ICMP;
+++		if (ebt_parse_icmp(icmp_codes, ARRAY_SIZE(icmp_codes), optarg,
+++				   ipinfo->icmp_type, ipinfo->icmp_code))
+++			return 0;
+++		break;
+++
++ 	case IP_myTOS:
++ 		ebt_check_option2(flags, OPT_TOS);
++ 		if (ebt_check_inverse2(optarg))
++@@ -219,10 +290,17 @@ static void final_check(const struct ebt_u_entry *entry,
++ 		(ipinfo->protocol!=IPPROTO_TCP &&
++ 		 ipinfo->protocol!=IPPROTO_UDP &&
++ 		 ipinfo->protocol!=IPPROTO_SCTP &&
++-		 ipinfo->protocol!=IPPROTO_DCCP)))
+++		 ipinfo->protocol!=IPPROTO_DCCP))) {
++ 		ebt_print_error("For port filtering the IP protocol must be "
++ 				"either 6 (tcp), 17 (udp), 33 (dccp) or "
++ 				"132 (sctp)");
+++	} else if ((ipinfo->bitmask & EBT_IP_ICMP) &&
+++	         (!(ipinfo->bitmask & EBT_IP_PROTO) ||
+++	            ipinfo->invflags & EBT_IP_PROTO ||
+++	            ipinfo->protocol != IPPROTO_ICMP)) {
+++		ebt_print_error("For ICMP filtering the IP protocol must be "
+++				"1 (icmp)");
+++	}
++ }
++ 
++ static void print(const struct ebt_u_entry *entry,
++@@ -280,6 +358,13 @@ static void print(const struct ebt_u_entry *entry,
++ 			printf("! ");
++ 		print_port_range(ipinfo->dport);
++ 	}
+++	if (ipinfo->bitmask & EBT_IP_ICMP) {
+++		printf("--ip-icmp-type ");
+++		if (ipinfo->invflags & EBT_IP_ICMP)
+++			printf("! ");
+++		ebt_print_icmp_type(icmp_codes, ARRAY_SIZE(icmp_codes),
+++				    ipinfo->icmp_type, ipinfo->icmp_code);
+++	}
++ }
++ 
++ static int compare(const struct ebt_entry_match *m1,
++@@ -322,6 +407,13 @@ static int compare(const struct ebt_entry_match *m1,
++ 		   ipinfo1->dport[1] != ipinfo2->dport[1])
++ 			return 0;
++ 	}
+++	if (ipinfo1->bitmask & EBT_IP_ICMP) {
+++		if (ipinfo1->icmp_type[0] != ipinfo2->icmp_type[0] ||
+++		    ipinfo1->icmp_type[1] != ipinfo2->icmp_type[1] ||
+++		    ipinfo1->icmp_code[0] != ipinfo2->icmp_code[0] ||
+++		    ipinfo1->icmp_code[1] != ipinfo2->icmp_code[1])
+++			return 0;
+++	}
++ 	return 1;
++ }
++ 
++-- 
++2.16.2
++
+diff --git a/package/network/utils/ebtables/patches/301-0004-ebt_ip-add-support-for-matching-IGMP-type.patch b/package/network/utils/ebtables/patches/301-0004-ebt_ip-add-support-for-matching-IGMP-type.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..1a7fe6d2ffcd0baf620dd36d6d3ba200f8b823e9
+--- /dev/null
++++ b/package/network/utils/ebtables/patches/301-0004-ebt_ip-add-support-for-matching-IGMP-type.patch
+@@ -0,0 +1,207 @@
++From b20e495bdf0c5eec3244cf7f98bae24316ffc3ab Mon Sep 17 00:00:00 2001
++Message-Id: <b20e495bdf0c5eec3244cf7f98bae24316ffc3ab.1520151963.git.mschiffer@universe-factory.net>
++In-Reply-To: <cover.1520150717.git.mschiffer@universe-factory.net>
++References: <cover.1520150717.git.mschiffer@universe-factory.net>
++From: Matthias Schiffer <mschiffer@universe-factory.net>
++Date: Sat, 3 Mar 2018 13:50:23 +0100
++Subject: [PATCH ebtables 4/4] ebt_ip: add support for matching IGMP type
++
++We already have ICMPv6 type/code matches (which can be used to distinguish
++different types of MLD packets). Add support for IPv4 IGMP matches in the
++same way.
++
++To reuse as much code as possible, the ICMP type/code handling functions
++are extended to allow passing a NULL code range.
++
++Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
++---
++ extensions/ebt_ip.c | 44 +++++++++++++++++++++++++++++++++++++++++++-
++ useful_functions.c  | 35 ++++++++++++++++++++++-------------
++ 2 files changed, 65 insertions(+), 14 deletions(-)
++
++diff --git a/extensions/ebt_ip.c b/extensions/ebt_ip.c
++index 42660d4564fb..1ffdb95f156d 100644
++--- a/extensions/ebt_ip.c
+++++ b/extensions/ebt_ip.c
++@@ -25,6 +25,7 @@
++ #define IP_SPORT  '5'
++ #define IP_DPORT  '6'
++ #define IP_ICMP   '7'
+++#define IP_IGMP   '8'
++ 
++ static const struct option opts[] =
++ {
++@@ -40,6 +41,7 @@ static const struct option opts[] =
++ 	{ "ip-destination-port" , required_argument, 0, IP_DPORT  },
++ 	{ "ip-dport"            , required_argument, 0, IP_DPORT  },
++ 	{ "ip-icmp-type"        , required_argument, 0, IP_ICMP   },
+++	{ "ip-igmp-type"        , required_argument, 0, IP_IGMP   },
++ 	{ 0 }
++ };
++ 
++@@ -97,6 +99,14 @@ static const struct ebt_icmp_names icmp_codes[] = {
++ 	{ "address-mask-reply", 18, 0, 0xFF }
++ };
++ 
+++static const struct ebt_icmp_names igmp_types[] = {
+++	{ "membership-query", 0x11 },
+++	{ "membership-report-v1", 0x12 },
+++	{ "membership-report-v2", 0x16 },
+++	{ "leave-group", 0x17 },
+++	{ "membership-report-v3", 0x22 },
+++};
+++
++ /* put the mask into 4 bytes */
++ /* transform a protocol and service name into a port number */
++ static uint16_t parse_port(const char *protocol, const char *name)
++@@ -162,10 +172,13 @@ static void print_help()
++ "--ip-proto  [!] protocol      : ip protocol specification\n"
++ "--ip-sport  [!] port[:port]   : tcp/udp source port or port range\n"
++ "--ip-dport  [!] port[:port]   : tcp/udp destination port or port range\n"
++-"--ip-icmp-type [!] type[[:type]/code[:code]] : icmp type/code or type/code range\n");
+++"--ip-icmp-type [!] type[[:type]/code[:code]] : icmp type/code or type/code range\n"
+++"--ip-igmp-type [!] type[:type]               : igmp type or type range\n");
++ 
++ 	printf("\nValid ICMP Types:\n");
++ 	ebt_print_icmp_types(icmp_codes, ARRAY_SIZE(icmp_codes));
+++	printf("\nValid IGMP Types:\n");
+++	ebt_print_icmp_types(igmp_types, ARRAY_SIZE(igmp_types));
++ }
++ 
++ static void init(struct ebt_entry_match *match)
++@@ -183,6 +196,7 @@ static void init(struct ebt_entry_match *match)
++ #define OPT_SPORT  0x10
++ #define OPT_DPORT  0x20
++ #define OPT_ICMP   0x40
+++#define OPT_IGMP   0x80
++ static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
++    unsigned int *flags, struct ebt_entry_match **match)
++ {
++@@ -241,6 +255,16 @@ static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
++ 			return 0;
++ 		break;
++ 
+++	case IP_IGMP:
+++		ebt_check_option2(flags, OPT_IGMP);
+++		ipinfo->bitmask |= EBT_IP_IGMP;
+++		if (ebt_check_inverse2(optarg))
+++			ipinfo->invflags |= EBT_IP_IGMP;
+++		if (ebt_parse_icmp(igmp_types, ARRAY_SIZE(igmp_types), optarg,
+++				   ipinfo->igmp_type, NULL))
+++			return 0;
+++		break;
+++
++ 	case IP_myTOS:
++ 		ebt_check_option2(flags, OPT_TOS);
++ 		if (ebt_check_inverse2(optarg))
++@@ -300,6 +324,12 @@ static void final_check(const struct ebt_u_entry *entry,
++ 	            ipinfo->protocol != IPPROTO_ICMP)) {
++ 		ebt_print_error("For ICMP filtering the IP protocol must be "
++ 				"1 (icmp)");
+++	} else if ((ipinfo->bitmask & EBT_IP_IGMP) &&
+++	         (!(ipinfo->bitmask & EBT_IP_PROTO) ||
+++	            ipinfo->invflags & EBT_IP_PROTO ||
+++	            ipinfo->protocol != IPPROTO_IGMP)) {
+++		ebt_print_error("For IGMP filtering the IP protocol must be "
+++				"2 (igmp)");
++ 	}
++ }
++ 
++@@ -365,6 +395,13 @@ static void print(const struct ebt_u_entry *entry,
++ 		ebt_print_icmp_type(icmp_codes, ARRAY_SIZE(icmp_codes),
++ 				    ipinfo->icmp_type, ipinfo->icmp_code);
++ 	}
+++	if (ipinfo->bitmask & EBT_IP_IGMP) {
+++		printf("--ip-igmp-type ");
+++		if (ipinfo->invflags & EBT_IP_IGMP)
+++			printf("! ");
+++		ebt_print_icmp_type(igmp_types, ARRAY_SIZE(igmp_types),
+++				    ipinfo->igmp_type, NULL);
+++	}
++ }
++ 
++ static int compare(const struct ebt_entry_match *m1,
++@@ -414,6 +451,11 @@ static int compare(const struct ebt_entry_match *m1,
++ 		    ipinfo1->icmp_code[1] != ipinfo2->icmp_code[1])
++ 			return 0;
++ 	}
+++	if (ipinfo1->bitmask & EBT_IP_IGMP) {
+++		if (ipinfo1->igmp_type[0] != ipinfo2->igmp_type[0] ||
+++		    ipinfo1->igmp_type[1] != ipinfo2->igmp_type[1])
+++			return 0;
+++	}
++ 	return 1;
++ }
++ 
++diff --git a/useful_functions.c b/useful_functions.c
++index 8f54bae83fae..8a34f820f230 100644
++--- a/useful_functions.c
+++++ b/useful_functions.c
++@@ -486,8 +486,10 @@ int ebt_parse_icmp(const struct ebt_icmp_names *icmp_codes, size_t n_codes,
++ 
++ 	if (match < n_codes) {
++ 		type[0] = type[1] = icmp_codes[match].type;
++-		code[0] = icmp_codes[match].code_min;
++-		code[1] = icmp_codes[match].code_max;
+++		if (code) {
+++			code[0] = icmp_codes[match].code_min;
+++			code[1] = icmp_codes[match].code_max;
+++		}
++ 	} else {
++ 		char *next = parse_range(icmptype, 0, 255, number);
++ 		if (!next) {
++@@ -499,17 +501,21 @@ int ebt_parse_icmp(const struct ebt_icmp_names *icmp_codes, size_t n_codes,
++ 		type[1] = (uint8_t) number[1];
++ 		switch (*next) {
++ 		case 0:
++-			code[0] = 0;
++-			code[1] = 255;
+++			if (code) {
+++				code[0] = 0;
+++				code[1] = 255;
+++			}
++ 			return 0;
++ 		case '/':
++-			next = parse_range(next+1, 0, 255, number);
++-			code[0] = (uint8_t) number[0];
++-			code[1] = (uint8_t) number[1];
++-			if (next == NULL)
++-				return -1;
++-			if (next && *next == 0)
++-				return 0;
+++			if (code) {
+++				next = parse_range(next+1, 0, 255, number);
+++				code[0] = (uint8_t) number[0];
+++				code[1] = (uint8_t) number[1];
+++				if (next == NULL)
+++					return -1;
+++				if (next && *next == 0)
+++					return 0;
+++			}
++ 		/* fallthrough */
++ 		default:
++ 			ebt_print_error("unknown character %c", *next);
++@@ -521,6 +527,9 @@ int ebt_parse_icmp(const struct ebt_icmp_names *icmp_codes, size_t n_codes,
++ 
++ static void print_icmp_code(uint8_t *code)
++ {
+++	if (!code)
+++		return;
+++
++ 	if (code[0] == code[1])
++ 		printf("/%"PRIu8 " ", code[0]);
++ 	else
++@@ -542,8 +551,8 @@ void ebt_print_icmp_type(const struct ebt_icmp_names *icmp_codes,
++ 		if (icmp_codes[i].type != type[0])
++ 			continue;
++ 
++-		if (icmp_codes[i].code_min == code[0] &&
++-		    icmp_codes[i].code_max == code[1]) {
+++		if (!code || (icmp_codes[i].code_min == code[0] &&
+++			      icmp_codes[i].code_max == code[1])) {
++ 			printf("%s ", icmp_codes[i].name);
++ 			return;
++ 		}
++-- 
++2.16.2
++
+diff --git a/target/linux/generic/patches-4.4/614-0001-ebtables-add-support-for-matching-ICMP-type-and-code.patch b/target/linux/generic/patches-4.4/614-0001-ebtables-add-support-for-matching-ICMP-type-and-code.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..1f3d1a2fea50154238c03707b0d7a96db8d1f5d9
+--- /dev/null
++++ b/target/linux/generic/patches-4.4/614-0001-ebtables-add-support-for-matching-ICMP-type-and-code.patch
+@@ -0,0 +1,139 @@
++From 4f8fa78149e0921c8efdc1adc5e12686ffe7667f Mon Sep 17 00:00:00 2001
++Message-Id: <4f8fa78149e0921c8efdc1adc5e12686ffe7667f.1520150717.git.mschiffer@universe-factory.net>
++In-Reply-To: <cover.1520150717.git.mschiffer@universe-factory.net>
++References: <cover.1520150717.git.mschiffer@universe-factory.net>
++From: Matthias Schiffer <mschiffer@universe-factory.net>
++Date: Sat, 3 Mar 2018 11:55:21 +0100
++Subject: [PATCH nf-next 1/2] ebtables: add support for matching ICMP type and
++ code
++
++We already have ICMPv6 type/code matches. This adds support for IPv4 ICMP
++matches in the same way.
++
++Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
++---
++ include/uapi/linux/netfilter_bridge/ebt_ip.h | 13 +++++++--
++ net/bridge/netfilter/ebt_ip.c                | 43 +++++++++++++++++++++-------
++ 2 files changed, 43 insertions(+), 13 deletions(-)
++
++--- a/include/uapi/linux/netfilter_bridge/ebt_ip.h
+++++ b/include/uapi/linux/netfilter_bridge/ebt_ip.h
++@@ -23,8 +23,9 @@
++ #define EBT_IP_PROTO 0x08
++ #define EBT_IP_SPORT 0x10
++ #define EBT_IP_DPORT 0x20
+++#define EBT_IP_ICMP 0x40
++ #define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
++- EBT_IP_SPORT | EBT_IP_DPORT )
+++		     EBT_IP_SPORT | EBT_IP_DPORT | EBT_IP_ICMP)
++ #define EBT_IP_MATCH "ip"
++ 
++ /* the same values are used for the invflags */
++@@ -37,8 +38,14 @@ struct ebt_ip_info {
++ 	__u8  protocol;
++ 	__u8  bitmask;
++ 	__u8  invflags;
++-	__u16 sport[2];
++-	__u16 dport[2];
+++	union {
+++		__u16 sport[2];
+++		__u8 icmp_type[2];
+++	};
+++	union {
+++		__u16 dport[2];
+++		__u8 icmp_code[2];
+++	};
++ };
++ 
++ #endif
++--- a/net/bridge/netfilter/ebt_ip.c
+++++ b/net/bridge/netfilter/ebt_ip.c
++@@ -19,9 +19,15 @@
++ #include <linux/netfilter_bridge/ebtables.h>
++ #include <linux/netfilter_bridge/ebt_ip.h>
++ 
++-struct tcpudphdr {
++-	__be16 src;
++-	__be16 dst;
+++union pkthdr {
+++	struct {
+++		__be16 src;
+++		__be16 dst;
+++	} tcpudphdr;
+++	struct {
+++		u8 type;
+++		u8 code;
+++	} icmphdr;
++ };
++ 
++ static bool
++@@ -30,8 +36,8 @@ ebt_ip_mt(const struct sk_buff *skb, str
++ 	const struct ebt_ip_info *info = par->matchinfo;
++ 	const struct iphdr *ih;
++ 	struct iphdr _iph;
++-	const struct tcpudphdr *pptr;
++-	struct tcpudphdr _ports;
+++	const union pkthdr *pptr;
+++	union pkthdr _pkthdr;
++ 
++ 	ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
++ 	if (ih == NULL)
++@@ -50,29 +56,38 @@ ebt_ip_mt(const struct sk_buff *skb, str
++ 	if (info->bitmask & EBT_IP_PROTO) {
++ 		if (FWINV(info->protocol != ih->protocol, EBT_IP_PROTO))
++ 			return false;
++-		if (!(info->bitmask & EBT_IP_DPORT) &&
++-		    !(info->bitmask & EBT_IP_SPORT))
+++		if (!(info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT |
+++				       EBT_IP_ICMP)))
++ 			return true;
++ 		if (ntohs(ih->frag_off) & IP_OFFSET)
++ 			return false;
+++
+++		/* min icmp headersize is 4, so sizeof(_pkthdr) is ok. */
++ 		pptr = skb_header_pointer(skb, ih->ihl*4,
++-					  sizeof(_ports), &_ports);
+++					  sizeof(_pkthdr), &_pkthdr);
++ 		if (pptr == NULL)
++ 			return false;
++ 		if (info->bitmask & EBT_IP_DPORT) {
++-			u32 dst = ntohs(pptr->dst);
+++			u32 dst = ntohs(pptr->tcpudphdr.dst);
++ 			if (FWINV(dst < info->dport[0] ||
++ 				  dst > info->dport[1],
++ 				  EBT_IP_DPORT))
++ 			return false;
++ 		}
++ 		if (info->bitmask & EBT_IP_SPORT) {
++-			u32 src = ntohs(pptr->src);
+++			u32 src = ntohs(pptr->tcpudphdr.src);
++ 			if (FWINV(src < info->sport[0] ||
++ 				  src > info->sport[1],
++ 				  EBT_IP_SPORT))
++ 			return false;
++ 		}
+++		if ((info->bitmask & EBT_IP_ICMP) &&
+++		    FWINV(pptr->icmphdr.type < info->icmp_type[0] ||
+++			  pptr->icmphdr.type > info->icmp_type[1] ||
+++			  pptr->icmphdr.code < info->icmp_code[0] ||
+++			  pptr->icmphdr.code > info->icmp_code[1],
+++			  EBT_IP_ICMP))
+++			return false;
++ 	}
++ 	return true;
++ }
++@@ -101,6 +116,14 @@ static int ebt_ip_mt_check(const struct
++ 		return -EINVAL;
++ 	if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
++ 		return -EINVAL;
+++	if (info->bitmask & EBT_IP_ICMP) {
+++		if ((info->invflags & EBT_IP_PROTO) ||
+++		    info->protocol != IPPROTO_ICMP)
+++			return -EINVAL;
+++		if (info->icmp_type[0] > info->icmp_type[1] ||
+++		    info->icmp_code[0] > info->icmp_code[1])
+++			return -EINVAL;
+++	}
++ 	return 0;
++ }
++ 
+diff --git a/target/linux/generic/patches-4.4/614-0002-ebtables-add-support-for-matching-IGMP-type.patch b/target/linux/generic/patches-4.4/614-0002-ebtables-add-support-for-matching-IGMP-type.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..3c8760dbebefddb9bb6a0b9bb724210c573d854c
+--- /dev/null
++++ b/target/linux/generic/patches-4.4/614-0002-ebtables-add-support-for-matching-IGMP-type.patch
+@@ -0,0 +1,92 @@
++From 68c6d1b60803e9690f2a6168b80a92ae45c6578b Mon Sep 17 00:00:00 2001
++Message-Id: <68c6d1b60803e9690f2a6168b80a92ae45c6578b.1520150717.git.mschiffer@universe-factory.net>
++In-Reply-To: <cover.1520150717.git.mschiffer@universe-factory.net>
++References: <cover.1520150717.git.mschiffer@universe-factory.net>
++From: Matthias Schiffer <mschiffer@universe-factory.net>
++Date: Sat, 3 Mar 2018 12:02:21 +0100
++Subject: [PATCH nf-next 2/2] ebtables: add support for matching IGMP type
++
++We already have ICMPv6 type/code matches (which can be used to distinguish
++different types of MLD packets). Add support for IPv4 IGMP matches in the
++same way.
++
++Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
++---
++ include/uapi/linux/netfilter_bridge/ebt_ip.h |  4 +++-
++ net/bridge/netfilter/ebt_ip.c                | 19 +++++++++++++++++--
++ 2 files changed, 20 insertions(+), 3 deletions(-)
++
++--- a/include/uapi/linux/netfilter_bridge/ebt_ip.h
+++++ b/include/uapi/linux/netfilter_bridge/ebt_ip.h
++@@ -24,8 +24,9 @@
++ #define EBT_IP_SPORT 0x10
++ #define EBT_IP_DPORT 0x20
++ #define EBT_IP_ICMP 0x40
+++#define EBT_IP_IGMP 0x80
++ #define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
++-		     EBT_IP_SPORT | EBT_IP_DPORT | EBT_IP_ICMP)
+++		     EBT_IP_SPORT | EBT_IP_DPORT | EBT_IP_ICMP | EBT_IP_IGMP)
++ #define EBT_IP_MATCH "ip"
++ 
++ /* the same values are used for the invflags */
++@@ -41,6 +42,7 @@ struct ebt_ip_info {
++ 	union {
++ 		__u16 sport[2];
++ 		__u8 icmp_type[2];
+++		__u8 igmp_type[2];
++ 	};
++ 	union {
++ 		__u16 dport[2];
++--- a/net/bridge/netfilter/ebt_ip.c
+++++ b/net/bridge/netfilter/ebt_ip.c
++@@ -28,6 +28,9 @@ union pkthdr {
++ 		u8 type;
++ 		u8 code;
++ 	} icmphdr;
+++	struct {
+++		u8 type;
+++	} igmphdr;
++ };
++ 
++ static bool
++@@ -57,12 +60,12 @@ ebt_ip_mt(const struct sk_buff *skb, str
++ 		if (FWINV(info->protocol != ih->protocol, EBT_IP_PROTO))
++ 			return false;
++ 		if (!(info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT |
++-				       EBT_IP_ICMP)))
+++				       EBT_IP_ICMP | EBT_IP_IGMP)))
++ 			return true;
++ 		if (ntohs(ih->frag_off) & IP_OFFSET)
++ 			return false;
++ 
++-		/* min icmp headersize is 4, so sizeof(_pkthdr) is ok. */
+++		/* min icmp/igmp headersize is 4, so sizeof(_pkthdr) is ok. */
++ 		pptr = skb_header_pointer(skb, ih->ihl*4,
++ 					  sizeof(_pkthdr), &_pkthdr);
++ 		if (pptr == NULL)
++@@ -88,6 +91,11 @@ ebt_ip_mt(const struct sk_buff *skb, str
++ 			  pptr->icmphdr.code > info->icmp_code[1],
++ 			  EBT_IP_ICMP))
++ 			return false;
+++		if ((info->bitmask & EBT_IP_IGMP) &&
+++		    FWINV(pptr->igmphdr.type < info->igmp_type[0] ||
+++			  pptr->igmphdr.type > info->igmp_type[1],
+++			  EBT_IP_IGMP))
+++			return false;
++ 	}
++ 	return true;
++ }
++@@ -124,6 +132,13 @@ static int ebt_ip_mt_check(const struct
++ 		    info->icmp_code[0] > info->icmp_code[1])
++ 			return -EINVAL;
++ 	}
+++	if (info->bitmask & EBT_IP_IGMP) {
+++		if ((info->invflags & EBT_IP_PROTO) ||
+++		    info->protocol != IPPROTO_IGMP)
+++			return -EINVAL;
+++		if (info->igmp_type[0] > info->igmp_type[1])
+++			return -EINVAL;
+++	}
++ 	return 0;
++ }
++