Blob Blame History Raw
From aa7beedaa4ac1371c1d08e8afc8d9b6cfd72e22e Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Mar 2017 13:25:13 +0100
Subject: [PATCH] tc: flower: support masked ICMP code and type match

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1422629
Upstream Status: iproute2.git commit 6374961a00d4b

commit 6374961a00d4b862bdf87c0f22af86d3ff7d0d67
Author: Simon Horman <simon.horman@netronome.com>
Date:   Thu Feb 9 14:49:01 2017 +0100

    tc: flower: support masked ICMP code and type match

    Extend ICMP code and type match to support masks.

    Also add missing documentation to synopsis in manpage.

    tc qdisc add dev eth0 ingress
    tc filter add dev eth0 protocol ipv6 parent ffff: flower \
            indev eth0 ip_proto icmpv6 type 128/240 code 0 action drop

    Signed-off-by: Simon Horman <simon.horman@netronome.com>
---
 man/man8/tc-flower.8 | 16 ++++++++++----
 tc/f_flower.c        | 59 ++++++++++++++++++++++++++++++----------------------
 2 files changed, 46 insertions(+), 29 deletions(-)

diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8
index b1bef8b..fc5bac5 100644
--- a/man/man8/tc-flower.8
+++ b/man/man8/tc-flower.8
@@ -34,7 +34,11 @@ flower \- flow based traffic control filter
 .BR dst_ip " | " src_ip " } "
 .IR PREFIX " | { "
 .BR dst_port " | " src_port " } "
-.IR port_number " } | { "
+.IR port_number " } | "
+.B type
+.IR MASKED_TYPE " | "
+.B code
+.IR MASKED_CODE " | { "
 .BR arp_tip " | " arp_sip " } "
 .IR IPV4_PREFIX " | "
 .BR arp_op " { " request " | " reply " | "
@@ -132,10 +136,14 @@ Match on layer 4 protocol source or destination port number. Only available for
 .BR ip_proto " values " udp ", " tcp  " and " sctp
 which have to be specified in beforehand.
 .TP
-.BI type " NUMBER"
+.BI type " MASKED_TYPE"
 .TQ
-.BI code " NUMBER"
-Match on ICMP type or code. Only available for
+.BI code " MASKED_CODE"
+Match on ICMP type or code. A mask may be optionally provided to limit the
+bits of the address which are matched. A mask is provided by following the
+address with a slash and then the mask. The mask must be as a number which
+represents a bitwise mask If the mask is missing then a match on all bits
+is assumed.  Only available for
 .BR ip_proto " values " icmp  " and " icmpv6
 which have to be specified in beforehand.
 .TP
diff --git a/tc/f_flower.c b/tc/f_flower.c
index 9c13e53..e9d3a96 100644
--- a/tc/f_flower.c
+++ b/tc/f_flower.c
@@ -57,8 +57,8 @@ static void explain(void)
 		"                       src_ip PREFIX |\n"
 		"                       dst_port PORT-NUMBER |\n"
 		"                       src_port PORT-NUMBER |\n"
-		"                       type ICMP-TYPE |\n"
-		"                       code ICMP-CODE |\n"
+		"                       type MASKED-ICMP-TYPE |\n"
+		"                       code MASKED-ICMP-CODE |\n"
 		"                       arp_tip IPV4-PREFIX |\n"
 		"                       arp_sip IPV4-PREFIX |\n"
 		"                       arp_op [ request | reply | OP ] |\n"
@@ -407,24 +407,32 @@ static int flower_icmp_attr_type(__be16 eth_type, __u8 ip_proto,
 	return -1;
 }
 
+static int flower_icmp_attr_mask_type(__be16 eth_type, __u8 ip_proto,
+				      enum flower_icmp_field field)
+{
+	if (eth_type == htons(ETH_P_IP) && ip_proto == IPPROTO_ICMP)
+		return field == FLOWER_ICMP_FIELD_CODE ?
+			TCA_FLOWER_KEY_ICMPV4_CODE_MASK :
+			TCA_FLOWER_KEY_ICMPV4_TYPE_MASK;
+	else if (eth_type == htons(ETH_P_IPV6) && ip_proto == IPPROTO_ICMPV6)
+		return field == FLOWER_ICMP_FIELD_CODE ?
+			TCA_FLOWER_KEY_ICMPV6_CODE_MASK :
+			TCA_FLOWER_KEY_ICMPV6_TYPE_MASK;
+
+	return -1;
+}
+
 static int flower_parse_icmp(char *str, __u16 eth_type, __u8 ip_proto,
 			     enum flower_icmp_field field, struct nlmsghdr *n)
 {
-	int ret;
-	int type;
-	uint8_t value;
-
-	type = flower_icmp_attr_type(eth_type, ip_proto, field);
-	if (type < 0)
-		return -1;
+	int value_type, mask_type;
 
-	ret = get_u8(&value, str, 10);
-	if (ret)
+	value_type = flower_icmp_attr_type(eth_type, ip_proto, field);
+	mask_type = flower_icmp_attr_mask_type(eth_type, ip_proto, field);
+	if (value_type < 0 || mask_type < 0)
 		return -1;
 
-	addattr8(n, MAX_MSG, type, value);
-
-	return 0;
+	return flower_parse_u8(str, value_type, mask_type, NULL, NULL, n);
 }
 
 static int flower_port_attr_type(__u8 ip_proto, enum flower_endpoint endpoint)
@@ -1000,12 +1008,6 @@ static void flower_print_key_id(FILE *f, const char *name,
 		fprintf(f, "\n  %s %d", name, rta_getattr_be32(attr));
 }
 
-static void flower_print_icmp(FILE *f, char *name, struct rtattr *attr)
-{
-	if (attr)
-		fprintf(f, "\n  %s %d", name, rta_getattr_u8(attr));
-}
-
 static void flower_print_masked_u8(FILE *f, const char *name,
 				   struct rtattr *attr,
 				   struct rtattr *mask_attr,
@@ -1045,9 +1047,9 @@ static int flower_print_opt(struct filter_util *qu, FILE *f,
 			    struct rtattr *opt, __u32 handle)
 {
 	struct rtattr *tb[TCA_FLOWER_MAX + 1];
+	int nl_type, nl_mask_type;
 	__be16 eth_type = 0;
 	__u8 ip_proto = 0xff;
-	int nl_type;
 
 	if (!opt)
 		return 0;
@@ -1111,12 +1113,19 @@ static int flower_print_opt(struct filter_util *qu, FILE *f,
 
 	nl_type = flower_icmp_attr_type(eth_type, ip_proto,
 					FLOWER_ICMP_FIELD_TYPE);
-	if (nl_type >= 0)
-		flower_print_icmp(f, "icmp_type", tb[nl_type]);
+	nl_mask_type = flower_icmp_attr_mask_type(eth_type, ip_proto,
+						  FLOWER_ICMP_FIELD_TYPE);
+	if (nl_type >= 0 && nl_mask_type >= 0)
+		flower_print_masked_u8(f, "icmp_type", tb[nl_type],
+				       tb[nl_mask_type], NULL);
+
 	nl_type = flower_icmp_attr_type(eth_type, ip_proto,
 					FLOWER_ICMP_FIELD_CODE);
-	if (nl_type >= 0)
-		flower_print_icmp(f, "icmp_code", tb[nl_type]);
+	nl_mask_type = flower_icmp_attr_mask_type(eth_type, ip_proto,
+						  FLOWER_ICMP_FIELD_CODE);
+	if (nl_type >= 0 && nl_mask_type >= 0)
+		flower_print_masked_u8(f, "icmp_code", tb[nl_type],
+				       tb[nl_mask_type], NULL);
 
 	flower_print_ip4_addr(f, "arp_sip", tb[TCA_FLOWER_KEY_ARP_SIP],
 			     tb[TCA_FLOWER_KEY_ARP_SIP_MASK]);
-- 
1.8.3.1