From: Ido Schimmel Date: Wed, 9 Oct 2024 06:20:54 +0000 (+0300) Subject: iprule: Add DSCP support X-Git-Tag: v6.13.0~15^2~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=75e760026c4d930058d9845fcd149019804393a5;p=thirdparty%2Fiproute2.git iprule: Add DSCP support Add support for 'dscp' selector in ip-rule. Rules can be added with a numeric DSCP value: # ip rule add dscp 1 table 100 # ip rule add dscp 0x02 table 200 Or using symbolic names from /usr/share/iproute2/rt_dsfield or /etc/iproute2/rt_dsfield: # ip rule add dscp AF42 table 300 Dump output: $ ip rule show 0: from all lookup local 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: $ ip rule show dscp 1 32765: from all lookup 100 dscp 1 Or by a symbolic name: $ ip rule show dscp AF42 32763: from all lookup 300 dscp AF42 When the numeric option is specified, symbolic names will be translated to numeric values: $ ip -N rule show 0: from all lookup 255 32763: from all lookup 300 dscp 36 32764: from all lookup 200 dscp 2 32765: from all lookup 100 dscp 1 32766: from all lookup 254 32767: from all lookup 253 The same applies to the JSON output in order to be consistent with existing fields such as "tos" and "table": $ ip -j -p rule show dscp AF42 [ { "priority": 32763, "src": "all", "table": "300", "dscp": "AF42" } ] $ ip -j -p -N rule show dscp AF42 [ { "priority": 32763, "src": "all", "table": "300", "dscp": "36" } ] Signed-off-by: Ido Schimmel Reviewed-by: Daniel Machon Reviewed-by: Guillaume Nault Signed-off-by: David Ahern --- diff --git a/include/rt_names.h b/include/rt_names.h index 02750307..a5c73f38 100644 --- a/include/rt_names.h +++ b/include/rt_names.h @@ -11,6 +11,7 @@ const char *rtnl_rttable_n2a(__u32 id, char *buf, int len); const char *rtnl_rtrealm_n2a(int id, char *buf, int len); const char *rtnl_dsfield_n2a(int id, char *buf, int len); const char *rtnl_dsfield_get_name(int id); +const char *rtnl_dscp_n2a(int id, char *buf, int len); const char *rtnl_group_n2a(int id, char *buf, int len); int rtnl_rtprot_a2n(__u32 *id, const char *arg); @@ -19,6 +20,7 @@ int rtnl_rtscope_a2n(__u32 *id, const char *arg); int rtnl_rttable_a2n(__u32 *id, const char *arg); int rtnl_rtrealm_a2n(__u32 *id, const char *arg); int rtnl_dsfield_a2n(__u32 *id, const char *arg); +int rtnl_dscp_a2n(__u32 *id, const char *arg); int rtnl_group_a2n(int *id, const char *arg); const char *inet_proto_n2a(int proto, char *buf, int len); diff --git a/ip/iprule.c b/ip/iprule.c index 81938f2e..ae067c72 100644 --- a/ip/iprule.c +++ b/ip/iprule.c @@ -46,6 +46,7 @@ static void usage(void) " [ ipproto PROTOCOL ]\n" " [ sport [ NUMBER | NUMBER-NUMBER ]\n" " [ dport [ NUMBER | NUMBER-NUMBER ] ]\n" + " [ dscp DSCP ]\n" "ACTION := [ table TABLE_ID ]\n" " [ protocol PROTO ]\n" " [ nat ADDRESS ]\n" @@ -67,6 +68,7 @@ static struct unsigned int tos, tosmask; unsigned int pref, prefmask; unsigned int fwmark, fwmask; + unsigned int dscp, dscpmask; uint64_t tun_id; char iif[IFNAMSIZ]; char oif[IFNAMSIZ]; @@ -219,6 +221,17 @@ 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]); + + if (filter.dscp != dscp) + return false; + } else { + return false; + } + } + table = frh_get_table(frh, tb); if (filter.tb > 0 && filter.tb ^ table) return false; @@ -468,6 +481,14 @@ int print_rule(struct nlmsghdr *n, void *arg) rtnl_rtprot_n2a(protocol, b1, sizeof(b1))); } } + + 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))); + } + print_string(PRINT_FP, NULL, "\n", ""); close_json_object(); fflush(fp); @@ -697,6 +718,14 @@ static int iprule_list_flush_or_save(int argc, char **argv, int action) else if (ret != 2) invarg("invalid dport range\n", *argv); filter.dport = r; + } 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; } else { if (matches(*argv, "dst") == 0 || matches(*argv, "to") == 0) { @@ -975,6 +1004,13 @@ static int iprule_modify(int cmd, int argc, char **argv) invarg("invalid dport range\n", *argv); addattr_l(&req.n, sizeof(req), FRA_DPORT_RANGE, &r, sizeof(r)); + } else if (strcmp(*argv, "dscp") == 0) { + __u32 dscp; + + NEXT_ARG(); + if (rtnl_dscp_a2n(&dscp, *argv)) + invarg("invalid dscp\n", *argv); + addattr8(&req.n, sizeof(req), FRA_DSCP, dscp); } else { int type; diff --git a/lib/rt_names.c b/lib/rt_names.c index e967e0ca..723f0917 100644 --- a/lib/rt_names.c +++ b/lib/rt_names.c @@ -625,6 +625,17 @@ const char *rtnl_dsfield_get_name(int id) return rtnl_rtdsfield_tab[id]; } +const char *rtnl_dscp_n2a(int id, char *buf, int len) +{ + if (!numeric) { + const char *name = rtnl_dsfield_get_name(id << 2); + + if (name != NULL) + return name; + } + snprintf(buf, len, "%u", id); + return buf; +} int rtnl_dsfield_a2n(__u32 *id, const char *arg) { @@ -658,6 +669,18 @@ int rtnl_dsfield_a2n(__u32 *id, const char *arg) return 0; } +int rtnl_dscp_a2n(__u32 *id, const char *arg) +{ + if (get_u32(id, arg, 0) == 0) + return 0; + + if (rtnl_dsfield_a2n(id, arg) != 0) + return -1; + /* Convert from DS field to DSCP */ + *id >>= 2; + + return 0; +} static struct rtnl_hash_entry dflt_group_entry = { .id = 0, .name = "default" diff --git a/man/man8/ip-rule.8.in b/man/man8/ip-rule.8.in index 48f8222f..51f3050a 100644 --- a/man/man8/ip-rule.8.in +++ b/man/man8/ip-rule.8.in @@ -36,6 +36,8 @@ ip-rule \- routing policy database management .IR PREFIX " ] [ " .B tos .IR TOS " ] [ " +.B dscp +.IR DSCP " ] [ " .B fwmark .IR FWMARK\fR[\fB/\fIMASK "] ] [ " .B iif @@ -234,6 +236,21 @@ a device. .BI dsfield " TOS" 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 +.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 +.B ip rule +will only use the higher six bits. +.B ip rule show +will similarly format DSCP values as symbolic names if possible. The +command line option +.B -N +turns the show translation off. + .TP .BI fwmark " MARK" select the