adds a --ip6-icmp-type option to match on ipv6-icmp types/codes.
The ipv6-icmp name list was taken from iptables 1.4.9.
Signed-off-by: Florian Westphal<fwestphal@xxxxxxxxxx>
---
ebtables.8 | 11 +++
extensions/ebt_ip6.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++++-
include/ebtables_u.h | 4 +
3 files changed, 240 insertions(+), 2 deletions(-)
diff --git a/ebtables.8 b/ebtables.8
index 957c445..6c7acf7 100644
--- a/ebtables.8
+++ b/ebtables.8
@@ -700,6 +700,17 @@ If
The flag
.B --ip6-dport
is an alias for this option.
+.TP
+.BR "--ip6-icmp-type " "[!] {\fItype\fP[:\fItype\fP]/\fIcode\fP[:\fIcode\fP]|\fItypename\fP}"
+Specify ipv6\-icmp type and code to match.
+Ranges for both type and code are supported. Type and code are
+separated by a slash. Valid numbers for type and range are 0 to 255.
+To match a single type including all valid codes, symbolic names can
+be used instead of numbers. The list of known type names is shown by the command
+.nf
+ ebtables \-\-help ip6
+.fi
+This option is only valid for \-\-ip6-prococol ipv6-icmp.
.SS limit
This module matches at a limited rate using a token bucket filter.
A rule using this extension will match until this limit is reached.
diff --git a/extensions/ebt_ip6.c b/extensions/ebt_ip6.c
index 5850523..9ab177d 100644
--- a/extensions/ebt_ip6.c
+++ b/extensions/ebt_ip6.c
@@ -11,6 +11,9 @@
*
*/
+#include<errno.h>
+#include<inttypes.h>
+#include<limits.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
@@ -27,8 +30,9 @@
#define IP_PROTO '4'
#define IP_SPORT '5'
#define IP_DPORT '6'
+#define IP_ICMP6 '7'
-static struct option opts[] =
+static const struct option opts[] =
{
{ "ip6-source" , required_argument, 0, IP_SOURCE },
{ "ip6-src" , required_argument, 0, IP_SOURCE },
@@ -42,9 +46,55 @@ static struct option opts[] =
{ "ip6-sport" , required_argument, 0, IP_SPORT },
{ "ip6-destination-port" , required_argument, 0, IP_DPORT },
{ "ip6-dport" , required_argument, 0, IP_DPORT },
+ { "ip6-icmp-type" , required_argument, 0, IP_ICMP6 },
{ 0 }
};
+
+struct icmpv6_names {
+ const char *name;
+ u_int8_t type;
+ u_int8_t code_min, code_max;
+};
+
+static const struct icmpv6_names icmpv6_codes[] = {
+ { "destination-unreachable", 1, 0, 0xFF },
+ { "no-route", 1, 0, 0 },
+ { "communication-prohibited", 1, 1, 1 },
+ { "address-unreachable", 1, 3, 3 },
+ { "port-unreachable", 1, 4, 4 },
+
+ { "packet-too-big", 2, 0, 0xFF },
+
+ { "time-exceeded", 3, 0, 0xFF },
+ /* Alias */ { "ttl-exceeded", 3, 0, 0xFF },
+ { "ttl-zero-during-transit", 3, 0, 0 },
+ { "ttl-zero-during-reassembly", 3, 1, 1 },
+
+ { "parameter-problem", 4, 0, 0xFF },
+ { "bad-header", 4, 0, 0 },
+ { "unknown-header-type", 4, 1, 1 },
+ { "unknown-option", 4, 2, 2 },
+
+ { "echo-request", 128, 0, 0xFF },
+ /* Alias */ { "ping", 128, 0, 0xFF },
+
+ { "echo-reply", 129, 0, 0xFF },
+ /* Alias */ { "pong", 129, 0, 0xFF },
+
+ { "router-solicitation", 133, 0, 0xFF },
+
+ { "router-advertisement", 134, 0, 0xFF },
+
+ { "neighbour-solicitation", 135, 0, 0xFF },
+ /* Alias */ { "neighbor-solicitation", 135, 0, 0xFF },
+
+ { "neighbour-advertisement", 136, 0, 0xFF },
+ /* Alias */ { "neighbor-advertisement", 136, 0, 0xFF },
+
+ { "redirect", 137, 0, 0xFF },
+};
+
/* transform a protocol and service name into a port number */
static uint16_t parse_port(const char *protocol, const char *name)
{
@@ -91,6 +141,97 @@ 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])
@@ -99,6 +240,58 @@ 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(
@@ -108,7 +301,9 @@ static void print_help()
"--ip6-tclass [!] tclass : ipv6 traffic class specification\n"
"--ip6-proto [!] protocol : ipv6 protocol specification\n"
"--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-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();
}
static void init(struct ebt_entry_match *match)
@@ -170,6 +365,15 @@ 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_ICMP6:
+ ebt_check_option2(flags, EBT_IP6_ICMP6);
+ 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))
+ return 0;
+ break;
+
case IP_TCLASS:
ebt_check_option2(flags, OPT_TCLASS);
if (ebt_check_inverse2(optarg))
@@ -223,6 +427,12 @@ static void final_check(const struct ebt_u_entry *entry,
ebt_print_error("For port filtering the IP protocol must be "
"either 6 (tcp), 17 (udp), 33 (dccp) or "
"132 (sctp)");
+ if ((ipinfo->bitmask& EBT_IP6_ICMP6)&&
+ (!(ipinfo->bitmask& EBT_IP6_PROTO) ||
+ ipinfo->invflags& EBT_IP6_PROTO ||
+ ipinfo->protocol != IPPROTO_ICMPV6))
+ ebt_print_error("For ipv6-icmp filtering the IP protocol must be "
+ "58 (ipv6-icmp)");
}
static void print(const struct ebt_u_entry *entry,
@@ -275,6 +485,12 @@ static void print(const struct ebt_u_entry *entry,
printf("! ");
print_port_range(ipinfo->dport);
}
+ if (ipinfo->bitmask& EBT_IP6_ICMP6) {
+ printf("--ip6-icmp-type ");
+ if (ipinfo->invflags& EBT_IP6_ICMP6)
+ printf("! ");
+ print_icmp_type(ipinfo->icmpv6_type, ipinfo->icmpv6_code);
+ }
}
static int compare(const struct ebt_entry_match *m1,
@@ -317,6 +533,13 @@ static int compare(const struct ebt_entry_match *m1,
ipinfo1->dport[1] != ipinfo2->dport[1])
return 0;
}
+ if (ipinfo1->bitmask& EBT_IP6_ICMP6) {
+ if (ipinfo1->icmpv6_type[0] != ipinfo2->icmpv6_type[0] ||
+ ipinfo1->icmpv6_type[1] != ipinfo2->icmpv6_type[1] ||
+ ipinfo1->icmpv6_code[0] != ipinfo2->icmpv6_code[0] ||
+ ipinfo1->icmpv6_code[1] != ipinfo2->icmpv6_code[1])
+ return 0;
+ }
return 1;
}
diff --git a/include/ebtables_u.h b/include/ebtables_u.h
index 910c213..c29476d 100644
--- a/include/ebtables_u.h
+++ b/include/ebtables_u.h
@@ -376,4 +376,8 @@ extern int ebt_printstyle_mac;
#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
#endif
#define ATOMIC_ENV_VARIABLE "EBTABLES_ATOMIC_FILE"
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
#endif /* EBTABLES_U_H */