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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'net-fib_rules-add-port-mask-support'

Ido Schimmel says:

====================
net: fib_rules: Add port mask support

In some deployments users would like to encode path information into
certain bits of the IPv6 flow label, the UDP source port and the DSCP
field and use this information to route packets accordingly.

Redirecting traffic to a routing table based on specific bits in the UDP
source port is not currently possible. Only exact match and range are
currently supported by FIB rules.

This patchset extends FIB rules to match on layer 4 ports with an
optional mask. The mask is not supported when matching on a range. A
future patchset will add support for matching on the DSCP field with an
optional mask.

Patches #1-#6 gradually extend FIB rules to match on layer 4 ports with
an optional mask.

Patches #7-#8 add test cases for FIB rule port matching.

iproute2 support can be found here [1].

[1] https://github.com/idosch/iproute2/tree/submit/fib_rule_mask_v1
====================

Link: https://patch.msgid.link/20250217134109.311176-1-idosch@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+143 -9
+10
Documentation/netlink/specs/rt_rule.yaml
··· 182 182 type: u32 183 183 byte-order: big-endian 184 184 display-hint: hex 185 + - 186 + name: sport-mask 187 + type: u16 188 + display-hint: hex 189 + - 190 + name: dport-mask 191 + type: u16 192 + display-hint: hex 185 193 186 194 operations: 187 195 enum-model: directional ··· 223 215 - dscp 224 216 - flowlabel 225 217 - flowlabel-mask 218 + - sport-mask 219 + - dport-mask 226 220 - 227 221 name: newrule-ntf 228 222 doc: Notify a rule creation
+19
include/net/fib_rules.h
··· 43 43 struct fib_kuid_range uid_range; 44 44 struct fib_rule_port_range sport_range; 45 45 struct fib_rule_port_range dport_range; 46 + u16 sport_mask; 47 + u16 dport_mask; 46 48 struct rcu_head rcu; 47 49 }; 48 50 ··· 148 146 ntohs(port) <= a->end; 149 147 } 150 148 149 + static inline bool fib_rule_port_match(const struct fib_rule_port_range *range, 150 + u16 port_mask, __be16 port) 151 + { 152 + if ((range->start ^ ntohs(port)) & port_mask) 153 + return false; 154 + if (!port_mask && fib_rule_port_range_set(range) && 155 + !fib_rule_port_inrange(range, port)) 156 + return false; 157 + return true; 158 + } 159 + 151 160 static inline bool fib_rule_port_range_valid(const struct fib_rule_port_range *a) 152 161 { 153 162 return a->start != 0 && a->end != 0 && a->end < 0xffff && ··· 170 157 { 171 158 return a->start == b->start && 172 159 a->end == b->end; 160 + } 161 + 162 + static inline bool 163 + fib_rule_port_is_range(const struct fib_rule_port_range *range) 164 + { 165 + return range->start != range->end; 173 166 } 174 167 175 168 static inline bool fib_rule_requires_fldissect(struct fib_rule *rule)
+2
include/uapi/linux/fib_rules.h
··· 70 70 FRA_DSCP, /* dscp */ 71 71 FRA_FLOWLABEL, /* flowlabel */ 72 72 FRA_FLOWLABEL_MASK, /* flowlabel mask */ 73 + FRA_SPORT_MASK, /* sport mask */ 74 + FRA_DPORT_MASK, /* dport mask */ 73 75 __FRA_MAX 74 76 }; 75 77
+68 -1
net/core/fib_rules.c
··· 481 481 &rule->sport_range)) 482 482 continue; 483 483 484 + if (rule->sport_mask && r->sport_mask != rule->sport_mask) 485 + continue; 486 + 484 487 if (fib_rule_port_range_set(&rule->dport_range) && 485 488 !fib_rule_port_range_compare(&r->dport_range, 486 489 &rule->dport_range)) 490 + continue; 491 + 492 + if (rule->dport_mask && r->dport_mask != rule->dport_mask) 487 493 continue; 488 494 489 495 if (!ops->compare(r, frh, tb)) ··· 520 514 return -1; 521 515 } 522 516 #endif 517 + 518 + static int fib_nl2rule_port_mask(const struct nlattr *mask_attr, 519 + const struct fib_rule_port_range *range, 520 + u16 *port_mask, 521 + struct netlink_ext_ack *extack) 522 + { 523 + if (!fib_rule_port_range_valid(range)) { 524 + NL_SET_ERR_MSG_ATTR(extack, mask_attr, 525 + "Cannot specify port mask without port value"); 526 + return -EINVAL; 527 + } 528 + 529 + if (fib_rule_port_is_range(range)) { 530 + NL_SET_ERR_MSG_ATTR(extack, mask_attr, 531 + "Cannot specify port mask for port range"); 532 + return -EINVAL; 533 + } 534 + 535 + if (range->start & ~nla_get_u16(mask_attr)) { 536 + NL_SET_ERR_MSG_ATTR(extack, mask_attr, "Invalid port mask"); 537 + return -EINVAL; 538 + } 539 + 540 + *port_mask = nla_get_u16(mask_attr); 541 + 542 + return 0; 543 + } 523 544 524 545 static int fib_nl2rule(struct net *net, struct nlmsghdr *nlh, 525 546 struct netlink_ext_ack *extack, ··· 677 644 NL_SET_ERR_MSG(extack, "Invalid sport range"); 678 645 goto errout_free; 679 646 } 647 + if (!fib_rule_port_is_range(&nlrule->sport_range)) 648 + nlrule->sport_mask = U16_MAX; 649 + } 650 + 651 + if (tb[FRA_SPORT_MASK]) { 652 + err = fib_nl2rule_port_mask(tb[FRA_SPORT_MASK], 653 + &nlrule->sport_range, 654 + &nlrule->sport_mask, extack); 655 + if (err) 656 + goto errout_free; 680 657 } 681 658 682 659 if (tb[FRA_DPORT_RANGE]) { ··· 696 653 NL_SET_ERR_MSG(extack, "Invalid dport range"); 697 654 goto errout_free; 698 655 } 656 + if (!fib_rule_port_is_range(&nlrule->dport_range)) 657 + nlrule->dport_mask = U16_MAX; 658 + } 659 + 660 + if (tb[FRA_DPORT_MASK]) { 661 + err = fib_nl2rule_port_mask(tb[FRA_DPORT_MASK], 662 + &nlrule->dport_range, 663 + &nlrule->dport_mask, extack); 664 + if (err) 665 + goto errout_free; 699 666 } 700 667 701 668 *rule = nlrule; ··· 804 751 &rule->sport_range)) 805 752 continue; 806 753 754 + if (r->sport_mask != rule->sport_mask) 755 + continue; 756 + 807 757 if (!fib_rule_port_range_compare(&r->dport_range, 808 758 &rule->dport_range)) 759 + continue; 760 + 761 + if (r->dport_mask != rule->dport_mask) 809 762 continue; 810 763 811 764 if (!ops->compare(r, frh, tb)) ··· 843 784 [FRA_DSCP] = NLA_POLICY_MAX(NLA_U8, INET_DSCP_MASK >> 2), 844 785 [FRA_FLOWLABEL] = { .type = NLA_BE32 }, 845 786 [FRA_FLOWLABEL_MASK] = { .type = NLA_BE32 }, 787 + [FRA_SPORT_MASK] = { .type = NLA_U16 }, 788 + [FRA_DPORT_MASK] = { .type = NLA_U16 }, 846 789 }; 847 790 848 791 int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, ··· 1110 1049 + nla_total_size(1) /* FRA_PROTOCOL */ 1111 1050 + nla_total_size(1) /* FRA_IP_PROTO */ 1112 1051 + nla_total_size(sizeof(struct fib_rule_port_range)) /* FRA_SPORT_RANGE */ 1113 - + nla_total_size(sizeof(struct fib_rule_port_range)); /* FRA_DPORT_RANGE */ 1052 + + nla_total_size(sizeof(struct fib_rule_port_range)) /* FRA_DPORT_RANGE */ 1053 + + nla_total_size(2) /* FRA_SPORT_MASK */ 1054 + + nla_total_size(2); /* FRA_DPORT_MASK */ 1114 1055 1115 1056 if (ops->nlmsg_payload) 1116 1057 payload += ops->nlmsg_payload(rule); ··· 1180 1117 nla_put_uid_range(skb, &rule->uid_range)) || 1181 1118 (fib_rule_port_range_set(&rule->sport_range) && 1182 1119 nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) || 1120 + (rule->sport_mask && nla_put_u16(skb, FRA_SPORT_MASK, 1121 + rule->sport_mask)) || 1183 1122 (fib_rule_port_range_set(&rule->dport_range) && 1184 1123 nla_put_port_range(skb, FRA_DPORT_RANGE, &rule->dport_range)) || 1124 + (rule->dport_mask && nla_put_u16(skb, FRA_DPORT_MASK, 1125 + rule->dport_mask)) || 1185 1126 (rule->ip_proto && nla_put_u8(skb, FRA_IP_PROTO, rule->ip_proto))) 1186 1127 goto nla_put_failure; 1187 1128
+4 -4
net/ipv4/fib_rules.c
··· 201 201 if (rule->ip_proto && (rule->ip_proto != fl4->flowi4_proto)) 202 202 return 0; 203 203 204 - if (fib_rule_port_range_set(&rule->sport_range) && 205 - !fib_rule_port_inrange(&rule->sport_range, fl4->fl4_sport)) 204 + if (!fib_rule_port_match(&rule->sport_range, rule->sport_mask, 205 + fl4->fl4_sport)) 206 206 return 0; 207 207 208 - if (fib_rule_port_range_set(&rule->dport_range) && 209 - !fib_rule_port_inrange(&rule->dport_range, fl4->fl4_dport)) 208 + if (!fib_rule_port_match(&rule->dport_range, rule->dport_mask, 209 + fl4->fl4_dport)) 210 210 return 0; 211 211 212 212 return 1;
+4 -4
net/ipv6/fib6_rules.c
··· 340 340 if (rule->ip_proto && (rule->ip_proto != fl6->flowi6_proto)) 341 341 return 0; 342 342 343 - if (fib_rule_port_range_set(&rule->sport_range) && 344 - !fib_rule_port_inrange(&rule->sport_range, fl6->fl6_sport)) 343 + if (!fib_rule_port_match(&rule->sport_range, rule->sport_mask, 344 + fl6->fl6_sport)) 345 345 return 0; 346 346 347 - if (fib_rule_port_range_set(&rule->dport_range) && 348 - !fib_rule_port_inrange(&rule->dport_range, fl6->fl6_dport)) 347 + if (!fib_rule_port_match(&rule->dport_range, rule->dport_mask, 348 + fl6->fl6_dport)) 349 349 return 0; 350 350 351 351 return 1;
+36
tools/testing/selftests/net/fib_rule_tests.sh
··· 256 256 fib_rule6_test_match_n_redirect "$match" "$match" \ 257 257 "$getnomatch" "sport and dport redirect to table" \ 258 258 "sport and dport no redirect to table" 259 + 260 + match="sport 100-200 dport 300-400" 261 + getmatch="sport 100 dport 400" 262 + getnomatch="sport 100 dport 401" 263 + fib_rule6_test_match_n_redirect "$match" "$getmatch" \ 264 + "$getnomatch" \ 265 + "sport and dport range redirect to table" \ 266 + "sport and dport range no redirect to table" 267 + fi 268 + 269 + ip rule help 2>&1 | grep sport | grep -q MASK 270 + if [ $? -eq 0 ]; then 271 + match="sport 0x0f00/0xff00 dport 0x000f/0x00ff" 272 + getmatch="sport 0x0f11 dport 0x220f" 273 + getnomatch="sport 0x1f11 dport 0x221f" 274 + fib_rule6_test_match_n_redirect "$match" "$getmatch" \ 275 + "$getnomatch" "sport and dport masked redirect to table" \ 276 + "sport and dport masked no redirect to table" 259 277 fi 260 278 261 279 fib_check_iproute_support "ipproto" "ipproto" ··· 543 525 fib_rule4_test_match_n_redirect "$match" "$match" \ 544 526 "$getnomatch" "sport and dport redirect to table" \ 545 527 "sport and dport no redirect to table" 528 + 529 + match="sport 100-200 dport 300-400" 530 + getmatch="sport 100 dport 400" 531 + getnomatch="sport 100 dport 401" 532 + fib_rule4_test_match_n_redirect "$match" "$getmatch" \ 533 + "$getnomatch" \ 534 + "sport and dport range redirect to table" \ 535 + "sport and dport range no redirect to table" 536 + fi 537 + 538 + ip rule help 2>&1 | grep sport | grep -q MASK 539 + if [ $? -eq 0 ]; then 540 + match="sport 0x0f00/0xff00 dport 0x000f/0x00ff" 541 + getmatch="sport 0x0f11 dport 0x220f" 542 + getnomatch="sport 0x1f11 dport 0x221f" 543 + fib_rule4_test_match_n_redirect "$match" "$getmatch" \ 544 + "$getnomatch" "sport and dport masked redirect to table" \ 545 + "sport and dport masked no redirect to table" 546 546 fi 547 547 548 548 fib_check_iproute_support "ipproto" "ipproto"