diff mbox series

[iptables,3/3] libebt_ip: add icmp support

Message ID 20180417220906.9797-4-fw@strlen.de
State Accepted
Delegated to: Pablo Neira
Headers show
Series libebt_ip: add icmp support | expand

Commit Message

Florian Westphal April 17, 2018, 10:09 p.m. UTC
Was added to ebtables recently, so backport this to ebt-compat.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 extensions/libebt_ip.c      | 297 +++++++++++++++++++++++++++++++++++++++++++-
 extensions/libebt_ip.txlate |   9 ++
 2 files changed, 304 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/extensions/libebt_ip.c b/extensions/libebt_ip.c
index 1a87585c533f..72728542f444 100644
--- a/extensions/libebt_ip.c
+++ b/extensions/libebt_ip.c
@@ -18,8 +18,41 @@ 
 #include <string.h>
 #include <getopt.h>
 #include <netdb.h>
+#include <inttypes.h>
 #include <xtables.h>
-#include <linux/netfilter_bridge/ebt_ip.h>
+
+#include "libxt_icmp.h"
+
+#define EBT_IP_SOURCE 0x01
+#define EBT_IP_DEST 0x02
+#define EBT_IP_TOS 0x04
+#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_ICMP | EBT_IP_IGMP)
+
+struct ebt_ip_info {
+	__be32 saddr;
+	__be32 daddr;
+	__be32 smsk;
+	__be32 dmsk;
+	__u8  tos;
+	__u8  protocol;
+	__u8  bitmask;
+	__u8  invflags;
+	union {
+		__u16 sport[2];
+		__u8 icmp_type[2];
+		__u8 igmp_type[2];
+	};
+	union {
+		__u16 dport[2];
+		__u8 icmp_code[2];
+	};
+};
 
 #define IP_SOURCE	'1'
 #define IP_DEST		'2'
@@ -27,6 +60,8 @@ 
 #define IP_PROTO	'4'
 #define IP_SPORT	'5'
 #define IP_DPORT	'6'
+#define IP_EBT_ICMP	'7'
+#define IP_EBT_IGMP	'8'
 
 static const struct option brip_opts[] = {
 	{ .name = "ip-source",		.has_arg = true, .val = IP_SOURCE },
@@ -40,9 +75,73 @@  static const struct option brip_opts[] = {
 	{ .name = "ip-sport",		.has_arg = true, .val = IP_SPORT },
 	{ .name = "ip-destination-port",.has_arg = true, .val = IP_DPORT },
 	{ .name = "ip-dport",		.has_arg = true, .val = IP_DPORT },
+	{ .name = "ip-icmp-type",       .has_arg = true, .val = IP_EBT_ICMP },
+	{ .name = "ip-igmp-type",       .has_arg = true, .val = IP_EBT_IGMP },
 	XT_GETOPT_TABLEEND,
 };
 
+static const struct xt_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 }
+};
+
+static const struct xt_icmp_names igmp_types[] = {
+	{ "membership-query", 0x11 },
+	{ "membership-report-v1", 0x12 },
+	{ "membership-report-v2", 0x16 },
+	{ "leave-group", 0x17 },
+	{ "membership-report-v3", 0x22 },
+};
+
 static void brip_print_help(void)
 {
 	printf(
@@ -52,7 +151,14 @@  static void brip_print_help(void)
 "--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"
+"--ip-igmp-type [!] type[:type]               : igmp type or type range\n");
+
+	printf("\nValid ICMP Types:\n");
+	xt_print_icmp_types(icmp_codes, ARRAY_SIZE(icmp_codes));
+	printf("\nValid IGMP Types:\n");
+	xt_print_icmp_types(igmp_types, ARRAY_SIZE(igmp_types));
 }
 
 static void brip_init(struct xt_entry_match *match)
@@ -161,6 +267,118 @@  static void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk)
 	*addr = *addr & *msk;
 }
 
+static char *parse_range(const char *str, unsigned int res[])
+{
+	char *next;
+
+	if (!xtables_strtoui(str, &next, &res[0], 0, 255))
+		return NULL;
+
+	res[1] = res[0];
+	if (*next == ':') {
+		str = next + 1;
+		if (!xtables_strtoui(str, &next, &res[1], 0, 255))
+			return NULL;
+	}
+
+	return next;
+}
+
+static int ebt_parse_icmp(const struct xt_icmp_names *codes, size_t n_codes,
+			  const char *icmptype, uint8_t type[], uint8_t code[])
+{
+	unsigned int match = n_codes;
+	unsigned int i, number[2];
+
+	for (i = 0; i < n_codes; i++) {
+		if (strncasecmp(codes[i].name, icmptype, strlen(icmptype)))
+			continue;
+		if (match != n_codes)
+			xtables_error(PARAMETER_PROBLEM, "Ambiguous ICMP type `%s':"
+					" `%s' or `%s'?",
+					icmptype, codes[match].name,
+					codes[i].name);
+		match = i;
+	}
+
+	if (match < n_codes) {
+		type[0] = type[1] = codes[match].type;
+		if (code) {
+			code[0] = codes[match].code_min;
+			code[1] = codes[match].code_max;
+		}
+	} else {
+		char *next = parse_range(icmptype, number);
+		if (!next) {
+			xtables_error(PARAMETER_PROBLEM, "Unknown ICMP type `%s'",
+							icmptype);
+			return -1;
+		}
+
+		type[0] = (uint8_t) number[0];
+		type[1] = (uint8_t) number[1];
+		switch (*next) {
+		case 0:
+			if (code) {
+				code[0] = 0;
+				code[1] = 255;
+			}
+			return 0;
+		case '/':
+			if (code) {
+				next = parse_range(next+1, 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:
+			xtables_error(PARAMETER_PROBLEM, "unknown character %c", *next);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static void print_icmp_code(uint8_t *code)
+{
+	if (!code)
+		return;
+
+	if (code[0] == code[1])
+		printf("/%"PRIu8 " ", code[0]);
+	else
+		printf("/%"PRIu8":%"PRIu8 " ", code[0], code[1]);
+}
+
+static void ebt_print_icmp_type(const struct xt_icmp_names *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 (codes[i].type != type[0])
+			continue;
+
+		if (!code || (codes[i].code_min == code[0] &&
+			      codes[i].code_max == code[1])) {
+			printf("%s ", codes[i].name);
+			return;
+		}
+	}
+	printf("%"PRIu8, type[0]);
+	print_icmp_code(code);
+}
+
 static int
 brip_parse(int c, char **argv, int invert, unsigned int *flags,
 	   const void *entry, struct xt_entry_match **match)
@@ -192,6 +410,20 @@  brip_parse(int c, char **argv, int invert, unsigned int *flags,
 		parse_port_range(NULL, optarg, info->dport);
 		info->bitmask |= EBT_IP_DPORT;
 		break;
+	case IP_EBT_ICMP:
+		if (invert)
+			info->invflags |= EBT_IP_ICMP;
+		ebt_parse_icmp(icmp_codes, ARRAY_SIZE(icmp_codes), optarg,
+			      info->icmp_type, info->icmp_code);
+		info->bitmask |= EBT_IP_ICMP;
+		break;
+	case IP_EBT_IGMP:
+		if (invert)
+			info->invflags |= EBT_IP_IGMP;
+		ebt_parse_icmp(igmp_types, ARRAY_SIZE(igmp_types), optarg,
+			       info->igmp_type, NULL);
+		info->bitmask |= EBT_IP_IGMP;
+		break;
 	case IP_EBT_TOS:
 		if (invert)
 			info->invflags |= EBT_IP_TOS;
@@ -289,6 +521,20 @@  static void brip_print(const void *ip, const struct xt_entry_match *match,
 			printf("! ");
 		print_port_range(info->dport);
 	}
+	if (info->bitmask & EBT_IP_ICMP) {
+		printf("--ip-icmp-type ");
+		if (info->invflags & EBT_IP_ICMP)
+			printf("! ");
+		ebt_print_icmp_type(icmp_codes, ARRAY_SIZE(icmp_codes),
+				    info->icmp_type, info->icmp_code);
+	}
+	if (info->bitmask & EBT_IP_IGMP) {
+		printf("--ip-igmp-type ");
+		if (info->invflags & EBT_IP_IGMP)
+			printf("! ");
+		ebt_print_icmp_type(igmp_types, ARRAY_SIZE(igmp_types),
+				    info->igmp_type, NULL);
+	}
 }
 
 static const char *brip_xlate_proto_to_name(uint8_t proto)
@@ -309,6 +555,50 @@  static const char *brip_xlate_proto_to_name(uint8_t proto)
 	}
 }
 
+static void brip_xlate_icmp(struct xt_xlate *xl,
+			    const struct ebt_ip_info *info, int bit)
+{
+	if ((info->bitmask & bit) == 0)
+		return;
+
+	xt_xlate_add(xl, "ip icmp type ");
+	if (info->invflags & bit)
+		xt_xlate_add(xl, "!= ");
+	if (info->icmp_type[0] == info->icmp_type[1])
+		xt_xlate_add(xl, "%d ", info->icmp_type[0]);
+	else
+		xt_xlate_add(xl, "%d-%d ", info->icmp_type[0],
+					   info->icmp_type[1]);
+	if (info->icmp_code[0] == 0 &&
+	    info->icmp_code[1] == 0xff)
+		return;
+
+	xt_xlate_add(xl, "ip icmp code ");
+	if (info->invflags & bit)
+		xt_xlate_add(xl, "!= ");
+	if (info->icmp_code[0] == info->icmp_code[1])
+		xt_xlate_add(xl, "%d ", info->icmp_code[0]);
+	else
+		xt_xlate_add(xl, "%d-%d ", info->icmp_code[0],
+					   info->icmp_code[1]);
+}
+
+static void brip_xlate_igmp(struct xt_xlate *xl,
+			    const struct ebt_ip_info *info, int bit)
+{
+	if ((info->bitmask & bit) == 0)
+		return;
+
+	xt_xlate_add(xl, "@th,0,8 ");
+	if (info->invflags & bit)
+		xt_xlate_add(xl, "!= ");
+	if (info->icmp_type[0] == info->icmp_type[1])
+		xt_xlate_add(xl, "%d ", info->icmp_type[0]);
+	else
+		xt_xlate_add(xl, "%d-%d ", info->icmp_type[0],
+					   info->icmp_type[1]);
+}
+
 static void brip_xlate_th(struct xt_xlate *xl,
 			  const struct ebt_ip_info *info, int bit,
 			  const char *pname)
@@ -415,6 +705,9 @@  static int brip_xlate(struct xt_xlate *xl,
 	brip_xlate_th(xl, info, EBT_IP_SPORT, pname);
 	brip_xlate_th(xl, info, EBT_IP_DPORT, pname);
 
+	brip_xlate_icmp(xl, info, EBT_IP_ICMP);
+	brip_xlate_igmp(xl, info, EBT_IP_IGMP);
+
 	return 1;
 }
 
diff --git a/extensions/libebt_ip.txlate b/extensions/libebt_ip.txlate
index 4d31a700814c..11594cdc0ad8 100644
--- a/extensions/libebt_ip.txlate
+++ b/extensions/libebt_ip.txlate
@@ -15,3 +15,12 @@  nft add rule bridge filter FORWARD udp sport 1024-65535 counter
 
 ebtables-translate -A FORWARD --ip-proto 253
 nft add rule bridge filter FORWARD ip protocol 253 counter
+
+ebtables-translate -A FORWARD --ip-icmp-type "echo-request"
+nft add rule bridge filter FORWARD ip icmp type 8 counter
+
+ebtables-translate -A FORWARD --ip-icmp-type 1/1
+nft add rule bridge filter FORWARD ip icmp type 1 ip icmp code 1 counter
+
+ebtables-translate -A FORWARD --ip-icmp-type ! 1:10
+nft add rule bridge filter FORWARD ip icmp type != 1-10 counter