Subject: [PATCH iproute2-next] ip rule: Add ipproto and port range to filter list

From: David Ahern <[email-masked]>
Date: Tue, 30 Oct 2018 15:03:30 -0700

From: David Ahern <[email-masked]>

Allow ip rule dumps and flushes to filter based on ipproto, sport
and dport. Example:

$ ip ru ls ipproto udp
99:	from all to 8.8.8.8 ipproto udp dport 53 lookup 1001
$ ip ru ls dport 53
99:	from all to 8.8.8.8 ipproto udp dport 53 lookup 1001

Signed-off-by: David Ahern <[email-masked]>
---
 ip/iprule.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/ip/iprule.c b/ip/iprule.c
index a85a43904e6e..0713694d4a49 100644
--- a/ip/iprule.c
+++ b/ip/iprule.c
@@ -78,6 +78,9 @@ static struct
 	inet_prefix dst;
 	int protocol;
 	int protocolmask;
+	struct fib_rule_port_range sport;
+	struct fib_rule_port_range dport;
+	__u8 ipproto;
 } filter;
 
 static inline int frh_get_table(struct fib_rule_hdr *frh, struct rtattr **tb)
@@ -174,6 +177,39 @@ static bool filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len)
 			return false;
 	}
 
+	if (filter.ipproto) {
+		__u8 ipproto = 0;
+
+		if (tb[FRA_IP_PROTO])
+			ipproto = rta_getattr_u8(tb[FRA_IP_PROTO]);
+		if (filter.ipproto != ipproto)
+			return false;
+	}
+
+	if (filter.sport.start) {
+		const struct fib_rule_port_range *r;
+
+		if (!tb[FRA_SPORT_RANGE])
+			return false;
+
+		r = RTA_DATA(tb[FRA_SPORT_RANGE]);
+		if (r->start != filter.sport.start ||
+		    r->end != filter.sport.end)
+			return false;
+	}
+
+	if (filter.dport.start) {
+		const struct fib_rule_port_range *r;
+
+		if (!tb[FRA_DPORT_RANGE])
+			return false;
+
+		r = RTA_DATA(tb[FRA_DPORT_RANGE]);
+		if (r->start != filter.dport.start ||
+		    r->end != filter.dport.end)
+			return false;
+	}
+
 	table = frh_get_table(frh, tb);
 	if (filter.tb > 0 && filter.tb ^ table)
 		return false;
@@ -607,6 +643,36 @@ static int iprule_list_flush_or_save(int argc, char **argv, int action)
 				filter.protocolmask = 0;
 			}
 			filter.protocol = prot;
+		} else if (strcmp(*argv, "ipproto") == 0) {
+			int ipproto;
+
+			NEXT_ARG();
+			ipproto = inet_proto_a2n(*argv);
+			if (ipproto < 0)
+				invarg("Invalid \"ipproto\" value\n", *argv);
+			filter.ipproto = ipproto;
+		} else if (strcmp(*argv, "sport") == 0) {
+			struct fib_rule_port_range r;
+			int ret = 0;
+
+			NEXT_ARG();
+			ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
+			if (ret == 1)
+				r.end = r.start;
+			else if (ret != 2)
+				invarg("invalid port range\n", *argv);
+			filter.sport = r;
+		} else if (strcmp(*argv, "dport") == 0) {
+			struct fib_rule_port_range r;
+			int ret = 0;
+
+			NEXT_ARG();
+			ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
+			if (ret == 1)
+				r.end = r.start;
+			else if (ret != 2)
+				invarg("invalid dport range\n", *argv);
+			filter.dport = r;
 		} else{
 			if (matches(*argv, "dst") == 0 ||
 			    matches(*argv, "to") == 0) {
-- 
2.11.0