Re: [PATCH] ebtables: extend ebt_ip6 to allow matching on ipv6-icmp types/codes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Florian,

This patch looks good. I'll apply it once the kernel patch is applied.

cheers,
Bart

On 17-12-10 17:24, Florian Westphal wrote:
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 */


--
Bart De Schuymer
www.artinalgorithms.be

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Netfitler Users]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux