Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv4: fib_rules: Add DSCP mask matching

Extend IPv4 FIB rules to match on DSCP using a mask. The mask is only
set in rules that match on DSCP (not TOS) and initialized to cover the
entire DSCP field if the mask attribute is not specified.

Reviewed-by: Petr Machata <petrm@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: Guillaume Nault <gnault@redhat.com>
Link: https://patch.msgid.link/20250220080525.831924-3-idosch@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Ido Schimmel and committed by
Jakub Kicinski
2ae00699 ca4edd96

+44 -3
+44 -3
net/ipv4/fib_rules.c
··· 37 37 u8 dst_len; 38 38 u8 src_len; 39 39 dscp_t dscp; 40 + dscp_t dscp_mask; 40 41 u8 dscp_full:1; /* DSCP or TOS selector */ 41 42 __be32 src; 42 43 __be32 srcmask; ··· 193 192 * to mask the upper three DSCP bits prior to matching to maintain 194 193 * legacy behavior. 195 194 */ 196 - if (r->dscp_full && r->dscp != inet_dsfield_to_dscp(fl4->flowi4_tos)) 195 + if (r->dscp_full && 196 + (r->dscp ^ inet_dsfield_to_dscp(fl4->flowi4_tos)) & r->dscp_mask) 197 197 return 0; 198 198 else if (!r->dscp_full && r->dscp && 199 199 !fib_dscp_masked_match(r->dscp, fl4)) ··· 237 235 } 238 236 239 237 rule4->dscp = inet_dsfield_to_dscp(nla_get_u8(nla) << 2); 238 + rule4->dscp_mask = inet_dsfield_to_dscp(INET_DSCP_MASK); 240 239 rule4->dscp_full = true; 240 + 241 + return 0; 242 + } 243 + 244 + static int fib4_nl2rule_dscp_mask(const struct nlattr *nla, 245 + struct fib4_rule *rule4, 246 + struct netlink_ext_ack *extack) 247 + { 248 + dscp_t dscp_mask; 249 + 250 + if (!rule4->dscp_full) { 251 + NL_SET_ERR_MSG_ATTR(extack, nla, 252 + "Cannot specify DSCP mask without DSCP value"); 253 + return -EINVAL; 254 + } 255 + 256 + dscp_mask = inet_dsfield_to_dscp(nla_get_u8(nla) << 2); 257 + if (rule4->dscp & ~dscp_mask) { 258 + NL_SET_ERR_MSG_ATTR(extack, nla, "Invalid DSCP mask"); 259 + return -EINVAL; 260 + } 261 + 262 + rule4->dscp_mask = dscp_mask; 241 263 242 264 return 0; 243 265 } ··· 295 269 296 270 if (tb[FRA_DSCP] && 297 271 fib4_nl2rule_dscp(tb[FRA_DSCP], rule4, extack) < 0) 272 + goto errout; 273 + 274 + if (tb[FRA_DSCP_MASK] && 275 + fib4_nl2rule_dscp_mask(tb[FRA_DSCP_MASK], rule4, extack) < 0) 298 276 goto errout; 299 277 300 278 /* split local/main if they are not already split */ ··· 396 366 return 0; 397 367 } 398 368 369 + if (tb[FRA_DSCP_MASK]) { 370 + dscp_t dscp_mask; 371 + 372 + dscp_mask = inet_dsfield_to_dscp(nla_get_u8(tb[FRA_DSCP_MASK]) << 2); 373 + if (!rule4->dscp_full || rule4->dscp_mask != dscp_mask) 374 + return 0; 375 + } 376 + 399 377 #ifdef CONFIG_IP_ROUTE_CLASSID 400 378 if (tb[FRA_FLOW] && (rule4->tclassid != nla_get_u32(tb[FRA_FLOW]))) 401 379 return 0; ··· 429 391 if (rule4->dscp_full) { 430 392 frh->tos = 0; 431 393 if (nla_put_u8(skb, FRA_DSCP, 432 - inet_dscp_to_dsfield(rule4->dscp) >> 2)) 394 + inet_dscp_to_dsfield(rule4->dscp) >> 2) || 395 + nla_put_u8(skb, FRA_DSCP_MASK, 396 + inet_dscp_to_dsfield(rule4->dscp_mask) >> 2)) 433 397 goto nla_put_failure; 434 398 } else { 435 399 frh->tos = inet_dscp_to_dsfield(rule4->dscp); ··· 458 418 return nla_total_size(4) /* dst */ 459 419 + nla_total_size(4) /* src */ 460 420 + nla_total_size(4) /* flow */ 461 - + nla_total_size(1); /* dscp */ 421 + + nla_total_size(1) /* dscp */ 422 + + nla_total_size(1); /* dscp mask */ 462 423 } 463 424 464 425 static void fib4_rule_flush_cache(struct fib_rules_ops *ops)