lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Date:   Thu, 13 Sep 2018 13:54:22 -0700
From:   Amritha Nambiar <amritha.nambiar@...el.com>
To:     stephen@...workplumber.org, netdev@...r.kernel.org
Cc:     alexander.h.duyck@...el.com, jakub.kicinski@...ronome.com,
        amritha.nambiar@...el.com, sridhar.samudrala@...el.com,
        jhs@...atatu.com, jesse.brandeburg@...el.com, jiri@...nulli.us,
        xiyou.wangcong@...il.com
Subject: [iproute2,RFC PATCH] tc: range: Introduce TC range classifier

Range classifier is introduced to support filters based
on ranges. Only port-range filters are supported currently.
This can be combined with flower classifier to support a
combination of port-ranges and other parameters based
on existing fields supported by cls_flower.

Example:
1. Match on a port range:
-----------------------
$ tc filter add dev enp4s0 protocol ip parent ffff: prio 2 range\
ip_proto tcp dst_port 1-15 skip_hw action drop

$ tc -s filter show dev enp4s0 parent ffff:
filter protocol ip pref 2 range chain 0
filter protocol ip pref 2 range chain 0 handle 0x1
  eth_type ipv4
  ip_proto tcp
  dst_port_min 1
  dst_port_max 15
  skip_hw
  not_in_hw
        action order 1: gact action drop
         random type none pass val 0
         index 1 ref 1 bind 1 installed 34 sec used 2 sec
        Action statistics:
        Sent 1380 bytes 30 pkt (dropped 30, overlimits 0 requeues 0)
        backlog 0b 0p requeues 0

2. Match on IP address and port range:
--------------------------------------
$ tc filter add dev enp4s0 protocol ip parent ffff: prio 2 flower\
  dst_ip 192.168.1.1 skip_hw action goto chain 11

$ tc filter add dev enp4s0 protocol ip parent ffff: prio 2 chain 11\
  range ip_proto tcp dst_port 1-15 action drop

$ tc -s filter show dev enp4s0 parent ffff:
filter protocol ip pref 2 flower chain 0
filter protocol ip pref 2 flower chain 0 handle 0x1
  eth_type ipv4
  dst_ip 192.168.1.1
  skip_hw
  not_in_hw
        action order 1: gact action goto chain 11
         random type none pass val 0
         index 1 ref 1 bind 1 installed 1426 sec used 2 sec
        Action statistics:
        Sent 460 bytes 10 pkt (dropped 0, overlimits 0 requeues 0)
        backlog 0b 0p requeues 0

filter protocol ip pref 2 range chain 11
filter protocol ip pref 2 range chain 11 handle 0x1
  eth_type ipv4
  ip_proto tcp
  dst_port_min 1
  dst_port_max 15
  not_in_hw
        action order 1: gact action drop
         random type none pass val 0
         index 2 ref 1 bind 1 installed 1310 sec used 2 sec
        Action statistics:
        Sent 460 bytes 10 pkt (dropped 10, overlimits 0 requeues 0)
        backlog 0b 0p requeues 0

Signed-off-by: Amritha Nambiar <amritha.nambiar@...el.com>
---
 include/uapi/linux/pkt_cls.h |   19 ++
 tc/Makefile                  |    1 
 tc/f_range.c                 |  369 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 389 insertions(+)
 create mode 100644 tc/f_range.c

diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h
index be382fb..8ef3a5a 100644
--- a/include/uapi/linux/pkt_cls.h
+++ b/include/uapi/linux/pkt_cls.h
@@ -379,6 +379,25 @@ enum {
 
 #define TCA_BPF_MAX (__TCA_BPF_MAX - 1)
 
+/* RANGE classifier */
+
+enum {
+	TCA_RANGE_UNSPEC,
+	TCA_RANGE_CLASSID,		/* u32 */
+	TCA_RANGE_INDEV,
+	TCA_RANGE_ACT,
+	TCA_RANGE_KEY_ETH_TYPE,		/* be16 */
+	TCA_RANGE_KEY_IP_PROTO,		/* u8 */
+	TCA_RANGE_KEY_PORT_SRC_MIN,	/* be16 */
+	TCA_RANGE_KEY_PORT_SRC_MAX,	/* be16 */
+	TCA_RANGE_KEY_PORT_DST_MIN,	/* be16 */
+	TCA_RANGE_KEY_PORT_DST_MAX,	/* be16 */
+	TCA_RANGE_FLAGS,		/* u32 */
+	__TCA_RANGE_MAX,
+};
+
+#define TCA_RANGE_MAX (__TCA_RANGE_MAX - 1)
+
 /* Flower classifier */
 
 enum {
diff --git a/tc/Makefile b/tc/Makefile
index 5a1a7ff..155cabe 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -29,6 +29,7 @@ TCMODULES += f_bpf.o
 TCMODULES += f_flow.o
 TCMODULES += f_cgroup.o
 TCMODULES += f_flower.o
+TCMODULES += f_range.o
 TCMODULES += q_dsmark.o
 TCMODULES += q_gred.o
 TCMODULES += f_tcindex.o
diff --git a/tc/f_range.c b/tc/f_range.c
new file mode 100644
index 0000000..388b275
--- /dev/null
+++ b/tc/f_range.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * f_range.c		Range Classifier
+ *
+ *		This program is free software; you can distribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Amritha Nambiar <amritha.nambiar@...el.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <linux/if_arp.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+enum range_type {
+	RANGE_PORT_SRC,
+	RANGE_PORT_DST
+};
+
+struct range_values {
+	__be16 min_port_type;
+	__be16 max_port_type;
+};
+
+static void explain(void)
+{
+	fprintf(stderr, "Usage: ... range [ MATCH-LIST ]\n");
+	fprintf(stderr, "		  [skip_sw | skip_hw]\n");
+	fprintf(stderr, "                 [ action ACTION_SPEC ] [ classid CLASSID ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: SELECTOR := SAMPLE SAMPLE ...\n");
+	fprintf(stderr, "       FILTERID := X:Y:Z\n");
+	fprintf(stderr, "       ACTION_SPEC := ... look at individual actions\n");
+	fprintf(stderr, "\nNOTE: CLASSID is parsed as hexadecimal input.\n");
+}
+
+static int range_parse_ip_proto(char *str, __be16 eth_type, int type,
+				__u8 *p_ip_proto, struct nlmsghdr *n)
+{
+	int ret;
+	__u8 ip_proto;
+
+	if (eth_type != htons(ETH_P_IP) && eth_type != htons(ETH_P_IPV6))
+		goto err;
+
+	if (matches(str, "tcp") == 0) {
+		ip_proto = IPPROTO_TCP;
+	} else if (matches(str, "udp") == 0) {
+		ip_proto = IPPROTO_UDP;
+	} else if (matches(str, "sctp") == 0) {
+		ip_proto = IPPROTO_SCTP;
+	} else if (matches(str, "icmp") == 0) {
+		if (eth_type != htons(ETH_P_IP))
+			goto err;
+		ip_proto = IPPROTO_ICMP;
+	} else if (matches(str, "icmpv6") == 0) {
+		if (eth_type != htons(ETH_P_IPV6))
+			goto err;
+		ip_proto = IPPROTO_ICMPV6;
+	} else {
+		ret = get_u8(&ip_proto, str, 16);
+		if (ret)
+			return -1;
+	}
+	addattr8(n, MAX_MSG, type, ip_proto);
+	*p_ip_proto = ip_proto;
+	return 0;
+
+err:
+	fprintf(stderr, "Illegal \"eth_type\" for ip proto\n");
+	return -1;
+}
+
+static int range_port_attr_type(__u8 ip_proto, enum range_type type,
+				struct range_values *range)
+{
+	if (ip_proto == IPPROTO_TCP || ip_proto == IPPROTO_UDP ||
+	    ip_proto == IPPROTO_SCTP) {
+		if (type == RANGE_PORT_SRC) {
+			range->min_port_type = TCA_RANGE_KEY_PORT_SRC_MIN;
+			range->max_port_type = TCA_RANGE_KEY_PORT_SRC_MAX;
+		} else {
+			range->min_port_type = TCA_RANGE_KEY_PORT_DST_MIN;
+			range->max_port_type = TCA_RANGE_KEY_PORT_DST_MAX;
+		}
+	} else {
+		return -1;
+	}
+
+	return 0;
+}
+
+static int range_parse_port_range(__be16 *min, __be16 *max, __u8 ip_proto,
+				  enum range_type type, struct nlmsghdr *n)
+{
+	struct range_values range;
+
+	range_port_attr_type(ip_proto, type, &range);
+
+	addattr16(n, MAX_MSG, range.min_port_type, *min);
+	addattr16(n, MAX_MSG, range.max_port_type, *max);
+
+	return 0;
+}
+
+static int get_range(__be16 *min, __be16 *max, char *argv)
+{
+	char *r;
+
+	r = strchr(argv, '-');
+	if (r) {
+		*r = '\0';
+		if (get_be16(min, argv, 10)) {
+			fprintf(stderr, "invalid min range\n");
+			return -1;
+		}
+		if (get_be16(max, r + 1, 10)) {
+			fprintf(stderr, "invalid max range\n");
+			return -1;
+		}
+	} else {
+		fprintf(stderr, "Illegal range format\n");
+		return -1;
+	}
+	return 0;
+}
+
+static int range_parse_opt(struct filter_util *qu, char *handle,
+			   int argc, char **argv, struct nlmsghdr *n)
+{
+	struct tcmsg *t = NLMSG_DATA(n);
+	struct rtattr *tail;
+	__be16 eth_type = TC_H_MIN(t->tcm_info);
+	__u8 ip_proto = 0xff;
+	__u32 flags = 0;
+	int ret;
+
+	if (handle) {
+		ret = get_u32(&t->tcm_handle, handle, 0);
+		if (ret) {
+			fprintf(stderr, "Illegal \"handle\"\n");
+			return -1;
+		}
+	}
+
+	tail = (struct rtattr *)(((void *)n) + NLMSG_ALIGN(n->nlmsg_len));
+	addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0);
+
+	if (argc == 0) {
+		/*at minimal we will match all ethertype packets */
+		goto parse_done;
+	}
+
+	while (argc > 0) {
+		if (matches(*argv, "classid") == 0 ||
+		    strcmp(*argv, "flowid") == 0) {
+			unsigned int handle;
+
+			NEXT_ARG();
+			ret = get_tc_classid(&handle, *argv);
+			if (ret) {
+				fprintf(stderr, "Illegal \"classid\"\n");
+				return -1;
+			}
+			addattr_l(n, MAX_MSG, TCA_RANGE_CLASSID, &handle, 4);
+		} else if (matches(*argv, "ip_proto") == 0) {
+			NEXT_ARG();
+			ret = range_parse_ip_proto(*argv, eth_type,
+						   TCA_RANGE_KEY_IP_PROTO,
+						   &ip_proto, n);
+			if (ret < 0) {
+				fprintf(stderr, "Illegal \"ip_proto\"\n");
+				return -1;
+			}
+		} else if (matches(*argv, "dst_port") == 0) {
+			__be16 min, max;
+
+			NEXT_ARG();
+			ret = get_range(&min, &max, *argv);
+			if (ret < 0)
+				return -1;
+			ret = range_parse_port_range(&min, &max, ip_proto,
+						     RANGE_PORT_DST, n);
+			if (ret < 0) {
+				fprintf(stderr, "Illegal \"dst_port range\"\n");
+				return -1;
+			}
+		} else if (matches(*argv, "src_port") == 0) {
+			__be16 min, max;
+
+			NEXT_ARG();
+			ret = get_range(&min, &max, *argv);
+			if (ret < 0)
+				return -1;
+			ret = range_parse_port_range(&min, &max, ip_proto,
+						     RANGE_PORT_SRC, n);
+			if (ret < 0) {
+				fprintf(stderr, "Illegal \"src_port range\"\n");
+				return -1;
+			}
+		} else if (matches(*argv, "skip_hw") == 0) {
+			flags |= TCA_CLS_FLAGS_SKIP_HW;
+		} else if (matches(*argv, "skip_sw") == 0) {
+			flags |= TCA_CLS_FLAGS_SKIP_SW;
+		} else if (matches(*argv, "action") == 0) {
+			NEXT_ARG();
+			if (parse_action(&argc, &argv, TCA_RANGE_ACT, n)) {
+				fprintf(stderr, "Illegal \"action\"\n");
+				return -1;
+			}
+			continue;
+		} else if (strcmp(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--; argv++;
+	}
+parse_done:
+	ret = addattr32(n, MAX_MSG, TCA_RANGE_FLAGS, flags);
+	if (ret)
+		return ret;
+
+	if (eth_type != htons(ETH_P_ALL)) {
+		ret = addattr16(n, MAX_MSG, TCA_RANGE_KEY_ETH_TYPE, eth_type);
+		if (ret)
+			return ret;
+	}
+
+	tail->rta_len = (((void *)n) + n->nlmsg_len) - (void *)tail;
+	return 0;
+}
+
+static void range_print_eth_type(__be16 *p_eth_type,
+				 struct rtattr *eth_type_attr)
+{
+	SPRINT_BUF(out);
+	__be16 eth_type;
+
+	if (!eth_type_attr)
+		return;
+
+	eth_type = rta_getattr_u16(eth_type_attr);
+	if (eth_type == htons(ETH_P_IP))
+		sprintf(out, "ipv4");
+	else if (eth_type == htons(ETH_P_IPV6))
+		sprintf(out, "ipv6");
+	else if (eth_type == htons(ETH_P_ARP))
+		sprintf(out, "arp");
+	else if (eth_type == htons(ETH_P_RARP))
+		sprintf(out, "rarp");
+	else
+		sprintf(out, "%04x", ntohs(eth_type));
+
+	print_string(PRINT_ANY, "eth_type", "\n  eth_type %s", out);
+	*p_eth_type = eth_type;
+}
+
+static void range_print_ip_proto(__u8 *p_ip_proto, struct rtattr *ip_proto_attr)
+{
+	SPRINT_BUF(out);
+	__u8 ip_proto;
+
+	if (!ip_proto_attr)
+		return;
+
+	ip_proto = rta_getattr_u8(ip_proto_attr);
+	if (ip_proto == IPPROTO_TCP)
+		sprintf(out, "tcp");
+	else if (ip_proto == IPPROTO_UDP)
+		sprintf(out, "udp");
+	else if (ip_proto == IPPROTO_SCTP)
+		sprintf(out, "sctp");
+	else if (ip_proto == IPPROTO_ICMP)
+		sprintf(out, "icmp");
+	else if (ip_proto == IPPROTO_ICMPV6)
+		sprintf(out, "icmpv6");
+	else
+		sprintf(out, "%02x", ip_proto);
+
+	print_string(PRINT_ANY, "ip_proto", "\n  ip_proto %s", out);
+	*p_ip_proto = ip_proto;
+}
+
+static void range_print_port_range(char *name, struct rtattr *attr)
+{
+	SPRINT_BUF(namefrm);
+
+	if (!attr)
+		return;
+
+	sprintf(namefrm, "\n  %s %%u", name);
+	print_uint(PRINT_ANY, name, namefrm, rta_getattr_be16(attr));
+}
+
+static int range_print_opt(struct filter_util *qu, FILE *f,
+			   struct rtattr *opt, __u32 handle)
+{
+	struct rtattr *tb[TCA_RANGE_MAX + 1];
+	struct range_values range;
+	__be16 eth_type = 0;
+	__u8 ip_proto = 0xff;
+
+	if (!opt)
+		return 0;
+
+	parse_rtattr_nested(tb, TCA_RANGE_MAX, opt);
+
+	if (handle)
+		print_uint(PRINT_ANY, "handle", "handle 0x%x ", handle);
+
+	if (tb[TCA_RANGE_CLASSID]) {
+		__u32 h = rta_getattr_u32(tb[TCA_RANGE_CLASSID]);
+
+		SPRINT_BUF(b1);
+		print_string(PRINT_ANY, "classid", "classid %s ",
+			     sprint_tc_classid(h, b1));
+	}
+
+	range_print_eth_type(&eth_type, tb[TCA_RANGE_KEY_ETH_TYPE]);
+	range_print_ip_proto(&ip_proto, tb[TCA_RANGE_KEY_IP_PROTO]);
+
+	if (range_port_attr_type(ip_proto, RANGE_PORT_DST, &range) == 0) {
+		range_print_port_range("dst_port_min", tb[range.min_port_type]);
+		range_print_port_range("dst_port_max", tb[range.max_port_type]);
+	}
+
+	if (range_port_attr_type(ip_proto, RANGE_PORT_SRC, &range) == 0) {
+		range_print_port_range("src_port_min", tb[range.min_port_type]);
+		range_print_port_range("src_port_max", tb[range.max_port_type]);
+	}
+
+	if (tb[TCA_RANGE_FLAGS]) {
+		__u32 flags = rta_getattr_u32(tb[TCA_RANGE_FLAGS]);
+
+		if (flags & TCA_CLS_FLAGS_SKIP_HW)
+			print_bool(PRINT_ANY, "skip_hw", "\n  skip_hw", true);
+		if (flags & TCA_CLS_FLAGS_SKIP_SW)
+			print_bool(PRINT_ANY, "skip_sw", "\n  skip_sw", true);
+
+		if (flags & TCA_CLS_FLAGS_IN_HW)
+			print_bool(PRINT_ANY, "in_hw", "\n  in_hw", true);
+		else if (flags & TCA_CLS_FLAGS_NOT_IN_HW)
+			print_bool(PRINT_ANY, "not_in_hw", "\n  not_in_hw",
+				   true);
+	}
+
+	if (tb[TCA_RANGE_ACT])
+		tc_print_action(f, tb[TCA_RANGE_ACT], 0);
+
+	return 0;
+}
+
+struct filter_util range_filter_util = {
+	.id = "range",
+	.parse_fopt = range_parse_opt,
+	.print_fopt = range_print_opt,
+};

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ