Skip to content

Commit a60a27c

Browse files
committed
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 kernel-patches#1-kernel-patches#6 gradually extend FIB rules to match on layer 4 ports with an optional mask. Patches kernel-patches#7-kernel-patches#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/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
2 parents dfc4b67 + f5d783c commit a60a27c

File tree

7 files changed

+143
-9
lines changed

7 files changed

+143
-9
lines changed

Documentation/netlink/specs/rt_rule.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ attribute-sets:
182182
type: u32
183183
byte-order: big-endian
184184
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
185193

186194
operations:
187195
enum-model: directional
@@ -215,6 +223,8 @@ operations:
215223
- dscp
216224
- flowlabel
217225
- flowlabel-mask
226+
- sport-mask
227+
- dport-mask
218228
-
219229
name: newrule-ntf
220230
doc: Notify a rule creation

include/net/fib_rules.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ struct fib_rule {
4343
struct fib_kuid_range uid_range;
4444
struct fib_rule_port_range sport_range;
4545
struct fib_rule_port_range dport_range;
46+
u16 sport_mask;
47+
u16 dport_mask;
4648
struct rcu_head rcu;
4749
};
4850

@@ -146,6 +148,17 @@ static inline bool fib_rule_port_inrange(const struct fib_rule_port_range *a,
146148
ntohs(port) <= a->end;
147149
}
148150

151+
static inline bool fib_rule_port_match(const struct fib_rule_port_range *range,
152+
u16 port_mask, __be16 port)
153+
{
154+
if ((range->start ^ ntohs(port)) & port_mask)
155+
return false;
156+
if (!port_mask && fib_rule_port_range_set(range) &&
157+
!fib_rule_port_inrange(range, port))
158+
return false;
159+
return true;
160+
}
161+
149162
static inline bool fib_rule_port_range_valid(const struct fib_rule_port_range *a)
150163
{
151164
return a->start != 0 && a->end != 0 && a->end < 0xffff &&
@@ -159,6 +172,12 @@ static inline bool fib_rule_port_range_compare(struct fib_rule_port_range *a,
159172
a->end == b->end;
160173
}
161174

175+
static inline bool
176+
fib_rule_port_is_range(const struct fib_rule_port_range *range)
177+
{
178+
return range->start != range->end;
179+
}
180+
162181
static inline bool fib_rule_requires_fldissect(struct fib_rule *rule)
163182
{
164183
return rule->iifindex != LOOPBACK_IFINDEX && (rule->ip_proto ||

include/uapi/linux/fib_rules.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ enum {
7070
FRA_DSCP, /* dscp */
7171
FRA_FLOWLABEL, /* flowlabel */
7272
FRA_FLOWLABEL_MASK, /* flowlabel mask */
73+
FRA_SPORT_MASK, /* sport mask */
74+
FRA_DPORT_MASK, /* dport mask */
7375
__FRA_MAX
7476
};
7577

net/core/fib_rules.c

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,11 +481,17 @@ static struct fib_rule *rule_find(struct fib_rules_ops *ops,
481481
&rule->sport_range))
482482
continue;
483483

484+
if (rule->sport_mask && r->sport_mask != rule->sport_mask)
485+
continue;
486+
484487
if (fib_rule_port_range_set(&rule->dport_range) &&
485488
!fib_rule_port_range_compare(&r->dport_range,
486489
&rule->dport_range))
487490
continue;
488491

492+
if (rule->dport_mask && r->dport_mask != rule->dport_mask)
493+
continue;
494+
489495
if (!ops->compare(r, frh, tb))
490496
continue;
491497
return r;
@@ -515,6 +521,33 @@ static int fib_nl2rule_l3mdev(struct nlattr *nla, struct fib_rule *nlrule,
515521
}
516522
#endif
517523

524+
static int fib_nl2rule_port_mask(const struct nlattr *mask_attr,
525+
const struct fib_rule_port_range *range,
526+
u16 *port_mask,
527+
struct netlink_ext_ack *extack)
528+
{
529+
if (!fib_rule_port_range_valid(range)) {
530+
NL_SET_ERR_MSG_ATTR(extack, mask_attr,
531+
"Cannot specify port mask without port value");
532+
return -EINVAL;
533+
}
534+
535+
if (fib_rule_port_is_range(range)) {
536+
NL_SET_ERR_MSG_ATTR(extack, mask_attr,
537+
"Cannot specify port mask for port range");
538+
return -EINVAL;
539+
}
540+
541+
if (range->start & ~nla_get_u16(mask_attr)) {
542+
NL_SET_ERR_MSG_ATTR(extack, mask_attr, "Invalid port mask");
543+
return -EINVAL;
544+
}
545+
546+
*port_mask = nla_get_u16(mask_attr);
547+
548+
return 0;
549+
}
550+
518551
static int fib_nl2rule(struct net *net, struct nlmsghdr *nlh,
519552
struct netlink_ext_ack *extack,
520553
struct fib_rules_ops *ops,
@@ -644,6 +677,16 @@ static int fib_nl2rule(struct net *net, struct nlmsghdr *nlh,
644677
NL_SET_ERR_MSG(extack, "Invalid sport range");
645678
goto errout_free;
646679
}
680+
if (!fib_rule_port_is_range(&nlrule->sport_range))
681+
nlrule->sport_mask = U16_MAX;
682+
}
683+
684+
if (tb[FRA_SPORT_MASK]) {
685+
err = fib_nl2rule_port_mask(tb[FRA_SPORT_MASK],
686+
&nlrule->sport_range,
687+
&nlrule->sport_mask, extack);
688+
if (err)
689+
goto errout_free;
647690
}
648691

649692
if (tb[FRA_DPORT_RANGE]) {
@@ -653,6 +696,16 @@ static int fib_nl2rule(struct net *net, struct nlmsghdr *nlh,
653696
NL_SET_ERR_MSG(extack, "Invalid dport range");
654697
goto errout_free;
655698
}
699+
if (!fib_rule_port_is_range(&nlrule->dport_range))
700+
nlrule->dport_mask = U16_MAX;
701+
}
702+
703+
if (tb[FRA_DPORT_MASK]) {
704+
err = fib_nl2rule_port_mask(tb[FRA_DPORT_MASK],
705+
&nlrule->dport_range,
706+
&nlrule->dport_mask, extack);
707+
if (err)
708+
goto errout_free;
656709
}
657710

658711
*rule = nlrule;
@@ -751,10 +804,16 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh,
751804
&rule->sport_range))
752805
continue;
753806

807+
if (r->sport_mask != rule->sport_mask)
808+
continue;
809+
754810
if (!fib_rule_port_range_compare(&r->dport_range,
755811
&rule->dport_range))
756812
continue;
757813

814+
if (r->dport_mask != rule->dport_mask)
815+
continue;
816+
758817
if (!ops->compare(r, frh, tb))
759818
continue;
760819
return 1;
@@ -784,6 +843,8 @@ static const struct nla_policy fib_rule_policy[FRA_MAX + 1] = {
784843
[FRA_DSCP] = NLA_POLICY_MAX(NLA_U8, INET_DSCP_MASK >> 2),
785844
[FRA_FLOWLABEL] = { .type = NLA_BE32 },
786845
[FRA_FLOWLABEL_MASK] = { .type = NLA_BE32 },
846+
[FRA_SPORT_MASK] = { .type = NLA_U16 },
847+
[FRA_DPORT_MASK] = { .type = NLA_U16 },
787848
};
788849

789850
int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
@@ -1049,7 +1110,9 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops,
10491110
+ nla_total_size(1) /* FRA_PROTOCOL */
10501111
+ nla_total_size(1) /* FRA_IP_PROTO */
10511112
+ nla_total_size(sizeof(struct fib_rule_port_range)) /* FRA_SPORT_RANGE */
1052-
+ nla_total_size(sizeof(struct fib_rule_port_range)); /* FRA_DPORT_RANGE */
1113+
+ nla_total_size(sizeof(struct fib_rule_port_range)) /* FRA_DPORT_RANGE */
1114+
+ nla_total_size(2) /* FRA_SPORT_MASK */
1115+
+ nla_total_size(2); /* FRA_DPORT_MASK */
10531116

10541117
if (ops->nlmsg_payload)
10551118
payload += ops->nlmsg_payload(rule);
@@ -1117,8 +1180,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule,
11171180
nla_put_uid_range(skb, &rule->uid_range)) ||
11181181
(fib_rule_port_range_set(&rule->sport_range) &&
11191182
nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) ||
1183+
(rule->sport_mask && nla_put_u16(skb, FRA_SPORT_MASK,
1184+
rule->sport_mask)) ||
11201185
(fib_rule_port_range_set(&rule->dport_range) &&
11211186
nla_put_port_range(skb, FRA_DPORT_RANGE, &rule->dport_range)) ||
1187+
(rule->dport_mask && nla_put_u16(skb, FRA_DPORT_MASK,
1188+
rule->dport_mask)) ||
11221189
(rule->ip_proto && nla_put_u8(skb, FRA_IP_PROTO, rule->ip_proto)))
11231190
goto nla_put_failure;
11241191

net/ipv4/fib_rules.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,12 @@ INDIRECT_CALLABLE_SCOPE int fib4_rule_match(struct fib_rule *rule,
201201
if (rule->ip_proto && (rule->ip_proto != fl4->flowi4_proto))
202202
return 0;
203203

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))
206206
return 0;
207207

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))
210210
return 0;
211211

212212
return 1;

net/ipv6/fib6_rules.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -340,12 +340,12 @@ INDIRECT_CALLABLE_SCOPE int fib6_rule_match(struct fib_rule *rule,
340340
if (rule->ip_proto && (rule->ip_proto != fl6->flowi6_proto))
341341
return 0;
342342

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))
345345
return 0;
346346

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))
349349
return 0;
350350

351351
return 1;

tools/testing/selftests/net/fib_rule_tests.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,24 @@ fib_rule6_test()
256256
fib_rule6_test_match_n_redirect "$match" "$match" \
257257
"$getnomatch" "sport and dport redirect to table" \
258258
"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"
259277
fi
260278

261279
fib_check_iproute_support "ipproto" "ipproto"
@@ -525,6 +543,24 @@ fib_rule4_test()
525543
fib_rule4_test_match_n_redirect "$match" "$match" \
526544
"$getnomatch" "sport and dport redirect to table" \
527545
"sport and dport no redirect to table"
546+
547+
match="sport 100-200 dport 300-400"
548+
getmatch="sport 100 dport 400"
549+
getnomatch="sport 100 dport 401"
550+
fib_rule4_test_match_n_redirect "$match" "$getmatch" \
551+
"$getnomatch" \
552+
"sport and dport range redirect to table" \
553+
"sport and dport range no redirect to table"
554+
fi
555+
556+
ip rule help 2>&1 | grep sport | grep -q MASK
557+
if [ $? -eq 0 ]; then
558+
match="sport 0x0f00/0xff00 dport 0x000f/0x00ff"
559+
getmatch="sport 0x0f11 dport 0x220f"
560+
getnomatch="sport 0x1f11 dport 0x221f"
561+
fib_rule4_test_match_n_redirect "$match" "$getmatch" \
562+
"$getnomatch" "sport and dport masked redirect to table" \
563+
"sport and dport masked no redirect to table"
528564
fi
529565

530566
fib_check_iproute_support "ipproto" "ipproto"

0 commit comments

Comments
 (0)