netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Ido Schimmel <idosch@nvidia.com>
To: <netdev@vger.kernel.org>
Cc: <stephen@networkplumber.org>, <dsahern@gmail.com>,
	<gnault@redhat.com>, <petrm@nvidia.com>,
	Ido Schimmel <idosch@nvidia.com>
Subject: [PATCH iproute2-next v2 5/5] iprule: Add DSCP mask support
Date: Tue, 25 Feb 2025 11:09:17 +0200	[thread overview]
Message-ID: <20250225090917.499376-6-idosch@nvidia.com> (raw)
In-Reply-To: <20250225090917.499376-1-idosch@nvidia.com>

Add DSCP mask support, allowing users to specify a DSCP value with an
optional mask. Example:

 # ip rule add dscp 1 table 100
 # ip rule add dscp 0x02/0x3f table 200
 # ip rule add dscp AF42/0x3f table 300
 # ip rule add dscp 0x10/0x30 table 400

In non-JSON output, the DSCP mask is not printed in case of exact match
and the DSCP value is printed in hexadecimal format in case of inexact
match:

 $ ip rule show
 0:      from all lookup local
 32762:  from all lookup 400 dscp 0x10/0x30
 32763:  from all lookup 300 dscp AF42
 32764:  from all lookup 200 dscp 2
 32765:  from all lookup 100 dscp 1
 32766:  from all lookup main
 32767:  from all lookup default

Dump can be filtered by DSCP value and mask:

 $ ip rule show dscp 1
 32765:  from all lookup 100 dscp 1
 $ ip rule show dscp AF42
 32763:  from all lookup 300 dscp AF42
 $ ip rule show dscp 0x10/0x30
 32762:  from all lookup 400 dscp 0x10/0x30

In JSON output, the DSCP mask is printed as an hexadecimal string to be
consistent with other masks. The DSCP value is printed as an integer in
order not to break existing scripts:

 $ ip -j -p -N rule show dscp 0x10/0x30
 [ {
         "priority": 32762,
         "src": "all",
         "table": "400",
         "dscp": "16",
         "dscp_mask": "0x30"
     } ]

The mask attribute is only sent to the kernel in case of inexact match
so that iproute2 will continue working with kernels that do not support
the attribute.

Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: Petr Machata <petrm@nvidia.com>
---
 ip/iprule.c           | 73 ++++++++++++++++++++++++++++++++-----------
 man/man8/ip-rule.8.in |  9 +++---
 2 files changed, 60 insertions(+), 22 deletions(-)

diff --git a/ip/iprule.c b/ip/iprule.c
index 3338c7d6773a..3af02da24950 100644
--- a/ip/iprule.c
+++ b/ip/iprule.c
@@ -24,6 +24,7 @@
 #include "json_print.h"
 
 #define PORT_MAX_MASK 0xFFFF
+#define DSCP_MAX_MASK 0x3F
 
 enum list_action {
 	IPRULE_LIST,
@@ -48,7 +49,7 @@ static void usage(void)
 		"            [ ipproto PROTOCOL ]\n"
 		"            [ sport [ NUMBER[/MASK] | NUMBER-NUMBER ]\n"
 		"            [ dport [ NUMBER[/MASK] | NUMBER-NUMBER ] ]\n"
-		"            [ dscp DSCP ] [ flowlabel FLOWLABEL[/MASK] ]\n"
+		"            [ dscp DSCP[/MASK] ] [ flowlabel FLOWLABEL[/MASK] ]\n"
 		"ACTION := [ table TABLE_ID ]\n"
 		"          [ protocol PROTO ]\n"
 		"          [ nat ADDRESS ]\n"
@@ -238,14 +239,21 @@ static bool filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len)
 	}
 
 	if (filter.dscpmask) {
-		if (tb[FRA_DSCP]) {
-			__u8 dscp = rta_getattr_u8(tb[FRA_DSCP]);
+		__u8 dscp_mask = DSCP_MAX_MASK;
+		__u8 dscp;
 
-			if (filter.dscp != dscp)
-				return false;
-		} else {
+		if (!tb[FRA_DSCP])
+			return false;
+
+		dscp = rta_getattr_u8(tb[FRA_DSCP]);
+		if (filter.dscp != dscp)
+			return false;
+
+		if (tb[FRA_DSCP_MASK])
+			dscp_mask = rta_getattr_u8(tb[FRA_DSCP_MASK]);
+
+		if (filter.dscpmask != dscp_mask)
 			return false;
-		}
 	}
 
 	if (filter.flowlabel_mask) {
@@ -552,8 +560,24 @@ int print_rule(struct nlmsghdr *n, void *arg)
 	if (tb[FRA_DSCP]) {
 		__u8 dscp = rta_getattr_u8(tb[FRA_DSCP]);
 
-		print_string(PRINT_ANY, "dscp", " dscp %s",
-			     rtnl_dscp_n2a(dscp, b1, sizeof(b1)));
+		if (tb[FRA_DSCP_MASK]) {
+			__u8 mask = rta_getattr_u8(tb[FRA_DSCP_MASK]);
+
+			print_string(PRINT_JSON, "dscp", NULL,
+				     rtnl_dscp_n2a(dscp, b1, sizeof(b1)));
+			print_0xhex(PRINT_JSON, "dscp_mask", NULL, mask);
+			if (mask == DSCP_MAX_MASK) {
+				print_string(PRINT_FP, NULL, " dscp %s",
+					     rtnl_dscp_n2a(dscp, b1,
+							   sizeof(b1)));
+			} else {
+				print_0xhex(PRINT_FP, NULL, " dscp %#x", dscp);
+				print_0xhex(PRINT_FP, NULL, "/%#x", mask);
+			}
+		} else {
+			print_string(PRINT_ANY, "dscp", " dscp %s",
+				     rtnl_dscp_n2a(dscp, b1, sizeof(b1)));
+		}
 	}
 
 	/* The kernel will either provide both attributes, or none */
@@ -687,6 +711,21 @@ static void iprule_port_parse(char *arg, struct fib_rule_port_range *r,
 	r->end = r->start;
 }
 
+static void iprule_dscp_parse(char *arg, __u32 *dscp, __u32 *mask)
+{
+	char *slash;
+
+	*mask = DSCP_MAX_MASK;
+
+	slash = strchr(arg, '/');
+	if (slash != NULL)
+		*slash = '\0';
+	if (rtnl_dscp_a2n(dscp, arg))
+		invarg("invalid dscp", arg);
+	if (slash && get_u32(mask, slash + 1, 0))
+		invarg("invalid dscp mask", slash + 1);
+}
+
 static void iprule_flowlabel_parse(char *arg, __u32 *flowlabel,
 				   __u32 *flowlabel_mask)
 {
@@ -841,13 +880,9 @@ static int iprule_list_flush_or_save(int argc, char **argv, int action)
 			iprule_port_parse(*argv, &filter.dport,
 					  &filter.dport_mask);
 		} else if (strcmp(*argv, "dscp") == 0) {
-			__u32 dscp;
-
 			NEXT_ARG();
-			if (rtnl_dscp_a2n(&dscp, *argv))
-				invarg("invalid dscp\n", *argv);
-			filter.dscp = dscp;
-			filter.dscpmask = 1;
+			iprule_dscp_parse(*argv, &filter.dscp,
+					  &filter.dscpmask);
 		} else if (strcmp(*argv, "flowlabel") == 0) {
 			NEXT_ARG();
 
@@ -1130,12 +1165,14 @@ static int iprule_modify(int cmd, int argc, char **argv)
 				addattr16(&req.n, sizeof(req), FRA_DPORT_MASK,
 					  dport_mask);
 		} else if (strcmp(*argv, "dscp") == 0) {
-			__u32 dscp;
+			__u32 dscp, dscp_mask;
 
 			NEXT_ARG();
-			if (rtnl_dscp_a2n(&dscp, *argv))
-				invarg("invalid dscp\n", *argv);
+			iprule_dscp_parse(*argv, &dscp, &dscp_mask);
 			addattr8(&req.n, sizeof(req), FRA_DSCP, dscp);
+			if (dscp_mask != DSCP_MAX_MASK)
+				addattr8(&req.n, sizeof(req), FRA_DSCP_MASK,
+					 dscp_mask);
 		} else if (strcmp(*argv, "flowlabel") == 0) {
 			__u32 flowlabel, flowlabel_mask;
 
diff --git a/man/man8/ip-rule.8.in b/man/man8/ip-rule.8.in
index 6a2a3cd848df..6331a021d95a 100644
--- a/man/man8/ip-rule.8.in
+++ b/man/man8/ip-rule.8.in
@@ -37,7 +37,7 @@ ip-rule \- routing policy database management
 .B  tos
 .IR TOS " ] [ "
 .B  dscp
-.IR DSCP " ] [ "
+.IR DSCP\fR[\fB/\fIMASK "] ] [ "
 .B  fwmark
 .IR FWMARK\fR[\fB/\fIMASK "] ] [ "
 .B  iif
@@ -239,9 +239,10 @@ a device.
 select the TOS value to match.
 
 .TP
-.BI dscp " DSCP"
-select the DSCP value to match. DSCP values can be written either directly as
-numeric values (valid values are 0-63), or using symbolic names specified in
+.BI dscp " DSCP\fR[\fB/\fIMASK\fR]"
+select the DSCP value to match with an optional mask. DSCP values can be
+written either directly as numeric values (valid values are 0-63), or using
+symbolic names specified in
 .BR @SYSCONF_USR_DIR@/rt_dsfield " or " @SYSCONF_ETC_DIR@/rt_dsfield
 (has precedence if exists).
 However, note that the file specifies full 8-bit dsfield values, whereas
-- 
2.48.1


  parent reply	other threads:[~2025-02-25  9:11 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-25  9:09 [PATCH iproute2-next v2 0/5] iprule: Add mask support for L4 ports and DSCP Ido Schimmel
2025-02-25  9:09 ` [PATCH iproute2-next v2 1/5] Sync uAPI headers Ido Schimmel
2025-02-25  9:09 ` [PATCH iproute2-next v2 2/5] iprule: Move port parsing to a function Ido Schimmel
2025-02-25  9:09 ` [PATCH iproute2-next v2 3/5] iprule: Allow specifying ports in hexadecimal notation Ido Schimmel
2025-02-25  9:09 ` [PATCH iproute2-next v2 4/5] iprule: Add port mask support Ido Schimmel
2025-02-25  9:58   ` Petr Machata
2025-02-25  9:09 ` Ido Schimmel [this message]
2025-02-26 15:18 ` [PATCH iproute2-next v2 0/5] iprule: Add mask support for L4 ports and DSCP Guillaume Nault
2025-02-28 16:24   ` David Ahern

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250225090917.499376-6-idosch@nvidia.com \
    --to=idosch@nvidia.com \
    --cc=dsahern@gmail.com \
    --cc=gnault@redhat.com \
    --cc=netdev@vger.kernel.org \
    --cc=petrm@nvidia.com \
    --cc=stephen@networkplumber.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).