gluon-firmware/patches/lede/0067-ebtables-add-support-f...

1184 lines
38 KiB
Diff

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;
+ }
+