[<prev] [next>] [day] [month] [year] [list]
Message-ID: <153687206212.43687.40773709891989438.stgit@anamhost.jf.intel.com>
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(ð_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