diff mbox series

[iptables,10/23] extensions: libebt_ip6: Use guided option parser

Message ID 20231220160636.11778-11-phil@nwl.cc
State Accepted
Headers show
Series Guided option parser for ebtables | expand

Commit Message

Phil Sutter Dec. 20, 2023, 4:06 p.m. UTC
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 extensions/libebt_ip6.c | 199 +++++++++++++++-------------------------
 extensions/libebt_ip6.t |   8 ++
 2 files changed, 83 insertions(+), 124 deletions(-)
diff mbox series

Patch

diff --git a/extensions/libebt_ip6.c b/extensions/libebt_ip6.c
index d926e86a585f4..0d7403e72589a 100644
--- a/extensions/libebt_ip6.c
+++ b/extensions/libebt_ip6.c
@@ -18,59 +18,59 @@ 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <getopt.h>
 #include <netdb.h>
 #include <xtables.h>
 #include <linux/netfilter_bridge/ebt_ip6.h>
 
 #include "libxt_icmp.h"
 
-#define IP_SOURCE '1'
-#define IP_DEST   '2'
-#define IP_TCLASS '3'
-#define IP_PROTO  '4'
-#define IP_SPORT  '5'
-#define IP_DPORT  '6'
-#define IP_ICMP6  '7'
-
-static const struct option brip6_opts[] = {
-	{ .name = "ip6-source",		.has_arg = true, .val = IP_SOURCE },
-	{ .name = "ip6-src",		.has_arg = true, .val = IP_SOURCE },
-	{ .name = "ip6-destination",	.has_arg = true, .val = IP_DEST },
-	{ .name = "ip6-dst",		.has_arg = true, .val = IP_DEST },
-	{ .name = "ip6-tclass",		.has_arg = true, .val = IP_TCLASS },
-	{ .name = "ip6-protocol",	.has_arg = true, .val = IP_PROTO },
-	{ .name = "ip6-proto",		.has_arg = true, .val = IP_PROTO },
-	{ .name = "ip6-source-port",	.has_arg = true, .val = IP_SPORT },
-	{ .name = "ip6-sport",		.has_arg = true, .val = IP_SPORT },
-	{ .name = "ip6-destination-port",.has_arg = true,.val = IP_DPORT },
-	{ .name = "ip6-dport",		.has_arg = true, .val = IP_DPORT },
-	{ .name = "ip6-icmp-type",	.has_arg = true, .val = IP_ICMP6 },
-	XT_GETOPT_TABLEEND,
+/* must correspond to the bit position in EBT_IP6_* defines */
+enum {
+	O_SOURCE = 0,
+	O_DEST,
+	O_TCLASS,
+	O_PROTO,
+	O_SPORT,
+	O_DPORT,
+	O_ICMP6,
+	F_PORT = 1 << O_ICMP6,
+	F_ICMP6 = 1 << O_SPORT | 1 << O_DPORT,
 };
 
-static void
-parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
-{
-	char *buffer;
-	char *cp;
-
-	buffer = xtables_strdup(portstring);
-	if ((cp = strchr(buffer, ':')) == NULL)
-		ports[0] = ports[1] = xtables_parse_port(buffer, NULL);
-	else {
-		*cp = '\0';
-		cp++;
-
-		ports[0] = buffer[0] ? xtables_parse_port(buffer, NULL) : 0;
-		ports[1] = cp[0] ? xtables_parse_port(cp, NULL) : 0xFFFF;
-
-		if (ports[0] > ports[1])
-			xtables_error(PARAMETER_PROBLEM,
-				      "invalid portrange (min > max)");
-	}
-	free(buffer);
-}
+static const struct xt_option_entry brip6_opts[] = {
+	{ .name = "ip6-source",		.id = O_SOURCE, .type = XTTYPE_HOSTMASK,
+	  .flags = XTOPT_INVERT },
+	{ .name = "ip6-src",		.id = O_SOURCE, .type = XTTYPE_HOSTMASK,
+	  .flags = XTOPT_INVERT },
+	{ .name = "ip6-destination",	.id = O_DEST, .type = XTTYPE_HOSTMASK,
+	  .flags = XTOPT_INVERT },
+	{ .name = "ip6-dst",		.id = O_DEST, .type = XTTYPE_HOSTMASK,
+	  .flags = XTOPT_INVERT },
+	{ .name = "ip6-tclass",		.id = O_TCLASS, .type = XTTYPE_UINT8,
+	  .flags = XTOPT_INVERT | XTOPT_PUT,
+	  XTOPT_POINTER(struct ebt_ip6_info, tclass) },
+	{ .name = "ip6-protocol",	.id = O_PROTO, .type = XTTYPE_PROTOCOL,
+	  .flags = XTOPT_INVERT | XTOPT_PUT,
+	  XTOPT_POINTER(struct ebt_ip6_info, protocol) },
+	{ .name = "ip6-proto",		.id = O_PROTO, .type = XTTYPE_PROTOCOL,
+	  .flags = XTOPT_INVERT | XTOPT_PUT,
+	  XTOPT_POINTER(struct ebt_ip6_info, protocol) },
+	{ .name = "ip6-source-port",	.id = O_SPORT, .type = XTTYPE_PORTRC,
+	  .excl = F_PORT, .flags = XTOPT_INVERT | XTOPT_PUT,
+	  XTOPT_POINTER(struct ebt_ip6_info, sport) },
+	{ .name = "ip6-sport",		.id = O_SPORT, .type = XTTYPE_PORTRC,
+	  .excl = F_PORT, .flags = XTOPT_INVERT | XTOPT_PUT,
+	  XTOPT_POINTER(struct ebt_ip6_info, sport) },
+	{ .name = "ip6-destination-port",.id = O_DPORT, .type = XTTYPE_PORTRC,
+	  .excl = F_PORT, .flags = XTOPT_INVERT | XTOPT_PUT,
+	  XTOPT_POINTER(struct ebt_ip6_info, dport) },
+	{ .name = "ip6-dport",		.id = O_DPORT, .type = XTTYPE_PORTRC,
+	  .excl = F_PORT, .flags = XTOPT_INVERT | XTOPT_PUT,
+	  XTOPT_POINTER(struct ebt_ip6_info, dport) },
+	{ .name = "ip6-icmp-type",	.id = O_ICMP6, .type = XTTYPE_STRING,
+	  .excl = F_ICMP6, .flags = XTOPT_INVERT },
+	XTOPT_TABLEEND,
+};
 
 static void print_port_range(uint16_t *ports)
 {
@@ -127,91 +127,42 @@  static void brip6_print_help(void)
 	xt_print_icmp_types(icmpv6_codes, ARRAY_SIZE(icmpv6_codes));
 }
 
-/* wrap xtables_ip6parse_any(), ignoring any but the first returned address */
-static void ebt_parse_ip6_address(char *address,
-				  struct in6_addr *addr, struct in6_addr *msk)
-{
-	struct in6_addr *addrp;
-	unsigned int naddrs;
-
-	xtables_ip6parse_any(address, &addrp, msk, &naddrs);
-	if (naddrs != 1)
-		xtables_error(PARAMETER_PROBLEM,
-			      "Invalid IPv6 Address '%s' specified", address);
-	memcpy(addr, addrp, sizeof(*addr));
-	free(addrp);
-}
-
-#define OPT_SOURCE 0x01
-#define OPT_DEST   0x02
-#define OPT_TCLASS 0x04
-#define OPT_PROTO  0x08
-#define OPT_SPORT  0x10
-#define OPT_DPORT  0x20
-static int
-brip6_parse(int c, char **argv, int invert, unsigned int *flags,
-	   const void *entry, struct xt_entry_match **match)
+static void brip6_parse(struct xt_option_call *cb)
 {
-	struct ebt_ip6_info *info = (struct ebt_ip6_info *)(*match)->data;
+	struct ebt_ip6_info *info = cb->data;
 	unsigned int i;
-	char *end;
-
-	switch (c) {
-	case IP_SOURCE:
-		if (invert)
-			info->invflags |= EBT_IP6_SOURCE;
-		ebt_parse_ip6_address(optarg, &info->saddr, &info->smsk);
-		info->bitmask |= EBT_IP6_SOURCE;
-		break;
-	case IP_DEST:
-		if (invert)
-			info->invflags |= EBT_IP6_DEST;
-		ebt_parse_ip6_address(optarg, &info->daddr, &info->dmsk);
-		info->bitmask |= EBT_IP6_DEST;
-		break;
-	case IP_SPORT:
-		if (invert)
-			info->invflags |= EBT_IP6_SPORT;
-		parse_port_range(NULL, optarg, info->sport);
-		info->bitmask |= EBT_IP6_SPORT;
-		break;
-	case IP_DPORT:
-		if (invert)
-			info->invflags |= EBT_IP6_DPORT;
-		parse_port_range(NULL, optarg, info->dport);
-		info->bitmask |= EBT_IP6_DPORT;
-		break;
-	case IP_ICMP6:
-		if (invert)
-			info->invflags |= EBT_IP6_ICMP6;
-		ebt_parse_icmpv6(optarg, info->icmpv6_type, info->icmpv6_code);
-		info->bitmask |= EBT_IP6_ICMP6;
+
+	/* XXX: overriding afinfo family is dangerous, but
+	 *      required for XTTYPE_HOSTMASK parsing */
+	xtables_set_nfproto(NFPROTO_IPV6);
+	xtables_option_parse(cb);
+	xtables_set_nfproto(NFPROTO_BRIDGE);
+
+	info->bitmask |= 1 << cb->entry->id;
+	info->invflags |= cb->invert ? 1 << cb->entry->id : 0;
+
+	switch (cb->entry->id) {
+	case O_SOURCE:
+		for (i = 0; i < ARRAY_SIZE(cb->val.haddr.all); i++)
+			cb->val.haddr.all[i] &= cb->val.hmask.all[i];
+		info->saddr = cb->val.haddr.in6;
+		info->smsk = cb->val.hmask.in6;
 		break;
-	case IP_TCLASS:
-		if (invert)
-			info->invflags |= EBT_IP6_TCLASS;
-		if (!xtables_strtoui(optarg, &end, &i, 0, 255))
-			xtables_error(PARAMETER_PROBLEM, "Problem with specified IPv6 traffic class '%s'", optarg);
-		info->tclass = i;
-		info->bitmask |= EBT_IP6_TCLASS;
+	case O_DEST:
+		for (i = 0; i < ARRAY_SIZE(cb->val.haddr.all); i++)
+			cb->val.haddr.all[i] &= cb->val.hmask.all[i];
+		info->daddr = cb->val.haddr.in6;
+		info->dmsk = cb->val.hmask.in6;
 		break;
-	case IP_PROTO:
-		if (invert)
-			info->invflags |= EBT_IP6_PROTO;
-		info->protocol = xtables_parse_protocol(optarg);
-		info->bitmask |= EBT_IP6_PROTO;
+	case O_ICMP6:
+		ebt_parse_icmpv6(cb->arg, info->icmpv6_type, info->icmpv6_code);
 		break;
-	default:
-		return 0;
 	}
-
-	*flags |= info->bitmask;
-	return 1;
 }
 
-static void brip6_final_check(unsigned int flags)
+static void brip6_final_check(struct xt_fcheck_call *fc)
 {
-	if (!flags)
+	if (!fc->xflags)
 		xtables_error(PARAMETER_PROBLEM,
 			      "You must specify proper arguments");
 }
@@ -441,11 +392,11 @@  static struct xtables_match brip6_match = {
 	.size		= XT_ALIGN(sizeof(struct ebt_ip6_info)),
 	.userspacesize	= XT_ALIGN(sizeof(struct ebt_ip6_info)),
 	.help		= brip6_print_help,
-	.parse		= brip6_parse,
-	.final_check	= brip6_final_check,
+	.x6_parse	= brip6_parse,
+	.x6_fcheck	= brip6_final_check,
 	.print		= brip6_print,
 	.xlate		= brip6_xlate,
-	.extra_opts	= brip6_opts,
+	.x6_options	= brip6_opts,
 };
 
 void _init(void)
diff --git a/extensions/libebt_ip6.t b/extensions/libebt_ip6.t
index fa1038af25649..19358431d7ca0 100644
--- a/extensions/libebt_ip6.t
+++ b/extensions/libebt_ip6.t
@@ -2,14 +2,22 @@ 
 -p ip6 --ip6-src ! dead::beef/64 -j ACCEPT;-p IPv6 --ip6-src ! dead::/64 -j ACCEPT;OK
 -p IPv6 --ip6-dst dead:beef::/64 -j ACCEPT;=;OK
 -p IPv6 --ip6-dst f00:ba::;=;OK
+-p IPv6 --ip6-dst ! f00:ba::;=;OK
+-p IPv6 --ip6-src 10.0.0.1;;FAIL
 -p IPv6 --ip6-tclass 0xFF;=;OK
+-p IPv6 --ip6-tclass ! 0xFF;=;OK
 -p IPv6 --ip6-proto tcp --ip6-dport 22;=;OK
 -p IPv6 --ip6-proto tcp --ip6-dport ! 22;=;OK
+-p IPv6 --ip6-proto tcp --ip6-sport ! 22 --ip6-dport 22;=;OK
 -p IPv6 --ip6-proto udp --ip6-sport 1024:65535;=;OK
 -p IPv6 --ip6-proto 253;=;OK
+-p IPv6 --ip6-proto ! 253;=;OK
 -p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type echo-request -j CONTINUE;=;OK
 -p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type echo-request;=;OK
+-p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type ! echo-request;=;OK
 -p ip6 --ip6-protocol icmpv6 --ip6-icmp-type 1/1;-p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type communication-prohibited -j CONTINUE;OK
 -p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type ! 1:10/0:255;=;OK
 --ip6-proto ipv6-icmp ! --ip6-icmp-type 1:10/0:255;=;FAIL
 ! -p IPv6 --ip6-proto ipv6-icmp ! --ip6-icmp-type 1:10/0:255;=;FAIL
+-p IPv6 --ip6-proto tcp --ip6-sport 22 --ip6-icmp-type echo-request;;FAIL
+-p IPv6 --ip6-proto tcp --ip6-dport 22 --ip6-icmp-type echo-request;;FAIL