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

ipv6: fib_rules: Add DSCP mask matching

Extend IPv6 FIB rules to match on DSCP using a mask. Unlike IPv4, also
initialize the DSCP mask when a non-zero 'tos' is specified as there is
no difference in matching between 'tos' and 'dscp'. As a side effect,
this makes it possible to match on 'dscp 0', like in IPv4.

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-4-idosch@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Ido Schimmel and committed by
Jakub Kicinski
c29165c2 2ae00699

+43 -2
+43 -2
net/ipv6/fib6_rules.c
··· 29 29 __be32 flowlabel; 30 30 __be32 flowlabel_mask; 31 31 dscp_t dscp; 32 + dscp_t dscp_mask; 32 33 u8 dscp_full:1; /* DSCP or TOS selector */ 33 34 }; 34 35 ··· 332 331 return 0; 333 332 } 334 333 335 - if (r->dscp && r->dscp != ip6_dscp(fl6->flowlabel)) 334 + if ((r->dscp ^ ip6_dscp(fl6->flowlabel)) & r->dscp_mask) 336 335 return 0; 337 336 338 337 if ((r->flowlabel ^ flowi6_get_flowlabel(fl6)) & r->flowlabel_mask) ··· 361 360 } 362 361 363 362 rule6->dscp = inet_dsfield_to_dscp(nla_get_u8(nla) << 2); 363 + rule6->dscp_mask = inet_dsfield_to_dscp(INET_DSCP_MASK); 364 364 rule6->dscp_full = true; 365 + 366 + return 0; 367 + } 368 + 369 + static int fib6_nl2rule_dscp_mask(const struct nlattr *nla, 370 + struct fib6_rule *rule6, 371 + struct netlink_ext_ack *extack) 372 + { 373 + dscp_t dscp_mask; 374 + 375 + if (!rule6->dscp_full) { 376 + NL_SET_ERR_MSG_ATTR(extack, nla, 377 + "Cannot specify DSCP mask without DSCP value"); 378 + return -EINVAL; 379 + } 380 + 381 + dscp_mask = inet_dsfield_to_dscp(nla_get_u8(nla) << 2); 382 + if (rule6->dscp & ~dscp_mask) { 383 + NL_SET_ERR_MSG_ATTR(extack, nla, "Invalid DSCP mask"); 384 + return -EINVAL; 385 + } 386 + 387 + rule6->dscp_mask = dscp_mask; 365 388 366 389 return 0; 367 390 } ··· 434 409 goto errout; 435 410 } 436 411 rule6->dscp = inet_dsfield_to_dscp(frh->tos); 412 + rule6->dscp_mask = frh->tos ? inet_dsfield_to_dscp(INET_DSCP_MASK) : 0; 437 413 438 414 if (tb[FRA_DSCP] && fib6_nl2rule_dscp(tb[FRA_DSCP], rule6, extack) < 0) 415 + goto errout; 416 + 417 + if (tb[FRA_DSCP_MASK] && 418 + fib6_nl2rule_dscp_mask(tb[FRA_DSCP_MASK], rule6, extack) < 0) 439 419 goto errout; 440 420 441 421 if ((tb[FRA_FLOWLABEL] || tb[FRA_FLOWLABEL_MASK]) && ··· 512 482 return 0; 513 483 } 514 484 485 + if (tb[FRA_DSCP_MASK]) { 486 + dscp_t dscp_mask; 487 + 488 + dscp_mask = inet_dsfield_to_dscp(nla_get_u8(tb[FRA_DSCP_MASK]) << 2); 489 + if (!rule6->dscp_full || rule6->dscp_mask != dscp_mask) 490 + return 0; 491 + } 492 + 515 493 if (tb[FRA_FLOWLABEL] && 516 494 nla_get_be32(tb[FRA_FLOWLABEL]) != rule6->flowlabel) 517 495 return 0; ··· 550 512 if (rule6->dscp_full) { 551 513 frh->tos = 0; 552 514 if (nla_put_u8(skb, FRA_DSCP, 553 - inet_dscp_to_dsfield(rule6->dscp) >> 2)) 515 + inet_dscp_to_dsfield(rule6->dscp) >> 2) || 516 + nla_put_u8(skb, FRA_DSCP_MASK, 517 + inet_dscp_to_dsfield(rule6->dscp_mask) >> 2)) 554 518 goto nla_put_failure; 555 519 } else { 556 520 frh->tos = inet_dscp_to_dsfield(rule6->dscp); ··· 579 539 return nla_total_size(16) /* dst */ 580 540 + nla_total_size(16) /* src */ 581 541 + nla_total_size(1) /* dscp */ 542 + + nla_total_size(1) /* dscp mask */ 582 543 + nla_total_size(4) /* flowlabel */ 583 544 + nla_total_size(4); /* flowlabel mask */ 584 545 }