Skip to content

Commit a25724b

Browse files
committed
Merge branch 'fib_rules-support-sport-dport-and-proto-match'
Roopa Prabhu says: ==================== fib_rules: support sport, dport and proto match This series extends fib rule match support to include sport, dport and ip proto match (to complete the 5-tuple match support). Common use-cases of Policy based routing in the data center require 5-tuple match. The last 2 patches in the series add a call to flow dissect in the fwd path if required by the installed fib rules (controlled by a flag). v1: - Fix errors reported by kbuild and feedback on RFC - extend port match uapi to accomodate port ranges v2: - address comments from Nikolay, David Ahern and Paolo (Thanks!) Pending things I will submit separate patches for: - extack for fib rules - fib rules test (as requested by david ahern) ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 2927499 + 5e5d6fe commit a25724b

File tree

14 files changed

+292
-31
lines changed

14 files changed

+292
-31
lines changed

include/net/fib_rules.h

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ struct fib_rule {
2727
u8 action;
2828
u8 l3mdev;
2929
u8 proto;
30-
/* 1 byte hole, try to use */
30+
u8 ip_proto;
3131
u32 target;
3232
__be64 tun_id;
3333
struct fib_rule __rcu *ctarget;
@@ -40,6 +40,8 @@ struct fib_rule {
4040
char iifname[IFNAMSIZ];
4141
char oifname[IFNAMSIZ];
4242
struct fib_kuid_range uid_range;
43+
struct fib_rule_port_range sport_range;
44+
struct fib_rule_port_range dport_range;
4345
struct rcu_head rcu;
4446
};
4547

@@ -144,6 +146,38 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla)
144146
return frh->table;
145147
}
146148

149+
static inline bool fib_rule_port_range_set(const struct fib_rule_port_range *range)
150+
{
151+
return range->start != 0 && range->end != 0;
152+
}
153+
154+
static inline bool fib_rule_port_inrange(const struct fib_rule_port_range *a,
155+
__be16 port)
156+
{
157+
return ntohs(port) >= a->start &&
158+
ntohs(port) <= a->end;
159+
}
160+
161+
static inline bool fib_rule_port_range_valid(const struct fib_rule_port_range *a)
162+
{
163+
return a->start != 0 && a->end != 0 && a->end < 0xffff &&
164+
a->start <= a->end;
165+
}
166+
167+
static inline bool fib_rule_port_range_compare(struct fib_rule_port_range *a,
168+
struct fib_rule_port_range *b)
169+
{
170+
return a->start == b->start &&
171+
a->end == b->end;
172+
}
173+
174+
static inline bool fib_rule_requires_fldissect(struct fib_rule *rule)
175+
{
176+
return rule->ip_proto ||
177+
fib_rule_port_range_set(&rule->sport_range) ||
178+
fib_rule_port_range_set(&rule->dport_range);
179+
}
180+
147181
struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *,
148182
struct net *);
149183
void fib_rules_unregister(struct fib_rules_ops *);

include/net/ip6_fib.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,24 @@ void fib6_rules_cleanup(void);
415415
bool fib6_rule_default(const struct fib_rule *rule);
416416
int fib6_rules_dump(struct net *net, struct notifier_block *nb);
417417
unsigned int fib6_rules_seq_read(struct net *net);
418+
419+
static inline bool fib6_rules_early_flow_dissect(struct net *net,
420+
struct sk_buff *skb,
421+
struct flowi6 *fl6,
422+
struct flow_keys *flkeys)
423+
{
424+
unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
425+
426+
if (!net->ipv6.fib6_rules_require_fldissect)
427+
return false;
428+
429+
skb_flow_dissect_flow_keys(skb, flkeys, flag);
430+
fl6->fl6_sport = flkeys->ports.src;
431+
fl6->fl6_dport = flkeys->ports.dst;
432+
fl6->flowi6_proto = flkeys->basic.ip_proto;
433+
434+
return true;
435+
}
418436
#else
419437
static inline int fib6_rules_init(void)
420438
{
@@ -436,5 +454,12 @@ static inline unsigned int fib6_rules_seq_read(struct net *net)
436454
{
437455
return 0;
438456
}
457+
static inline bool fib6_rules_early_flow_dissect(struct net *net,
458+
struct sk_buff *skb,
459+
struct flowi6 *fl6,
460+
struct flow_keys *flkeys)
461+
{
462+
return false;
463+
}
439464
#endif
440465
#endif

include/net/ip6_route.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ static inline int ip6_route_get_saddr(struct net *net, struct rt6_info *rt,
127127

128128
struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
129129
const struct in6_addr *saddr, int oif, int flags);
130-
u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb);
130+
u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb,
131+
struct flow_keys *hkeys);
131132

132133
struct dst_entry *icmp6_dst_alloc(struct net_device *dev, struct flowi6 *fl6);
133134

@@ -266,4 +267,5 @@ static inline bool rt6_duplicate_nexthop(struct rt6_info *a, struct rt6_info *b)
266267
ipv6_addr_equal(&a->rt6i_gateway, &b->rt6i_gateway) &&
267268
!lwtunnel_cmp_encap(a->dst.lwtstate, b->dst.lwtstate);
268269
}
270+
269271
#endif

include/net/ip_fib.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,13 @@ static inline unsigned int fib4_rules_seq_read(struct net *net)
293293
return 0;
294294
}
295295

296+
static inline bool fib4_rules_early_flow_dissect(struct net *net,
297+
struct sk_buff *skb,
298+
struct flowi4 *fl4,
299+
struct flow_keys *flkeys)
300+
{
301+
return false;
302+
}
296303
#else /* CONFIG_IP_MULTIPLE_TABLES */
297304
int __net_init fib4_rules_init(struct net *net);
298305
void __net_exit fib4_rules_exit(struct net *net);
@@ -341,6 +348,24 @@ bool fib4_rule_default(const struct fib_rule *rule);
341348
int fib4_rules_dump(struct net *net, struct notifier_block *nb);
342349
unsigned int fib4_rules_seq_read(struct net *net);
343350

351+
static inline bool fib4_rules_early_flow_dissect(struct net *net,
352+
struct sk_buff *skb,
353+
struct flowi4 *fl4,
354+
struct flow_keys *flkeys)
355+
{
356+
unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
357+
358+
if (!net->ipv4.fib_rules_require_fldissect)
359+
return false;
360+
361+
skb_flow_dissect_flow_keys(skb, flkeys, flag);
362+
fl4->fl4_sport = flkeys->ports.src;
363+
fl4->fl4_dport = flkeys->ports.dst;
364+
fl4->flowi4_proto = flkeys->basic.ip_proto;
365+
366+
return true;
367+
}
368+
344369
#endif /* CONFIG_IP_MULTIPLE_TABLES */
345370

346371
/* Exported by fib_frontend.c */
@@ -371,7 +396,7 @@ int fib_sync_up(struct net_device *dev, unsigned int nh_flags);
371396

372397
#ifdef CONFIG_IP_ROUTE_MULTIPATH
373398
int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4,
374-
const struct sk_buff *skb);
399+
const struct sk_buff *skb, struct flow_keys *flkeys);
375400
#endif
376401
void fib_select_multipath(struct fib_result *res, int hash);
377402
void fib_select_path(struct net *net, struct fib_result *res,

include/net/netns/ipv4.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ struct netns_ipv4 {
5252
#ifdef CONFIG_IP_MULTIPLE_TABLES
5353
struct fib_rules_ops *rules_ops;
5454
bool fib_has_custom_rules;
55+
unsigned int fib_rules_require_fldissect;
5556
struct fib_table __rcu *fib_main;
5657
struct fib_table __rcu *fib_default;
5758
#endif

include/net/netns/ipv6.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ struct netns_ipv6 {
7171
unsigned int ip6_rt_gc_expire;
7272
unsigned long ip6_rt_last_gc;
7373
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
74-
bool fib6_has_custom_rules;
74+
unsigned int fib6_rules_require_fldissect;
75+
bool fib6_has_custom_rules;
7576
struct rt6_info *ip6_prohibit_entry;
7677
struct rt6_info *ip6_blk_hole_entry;
7778
struct fib6_table *fib6_local_tbl;

include/uapi/linux/fib_rules.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ struct fib_rule_uid_range {
3535
__u32 end;
3636
};
3737

38+
struct fib_rule_port_range {
39+
__u16 start;
40+
__u16 end;
41+
};
42+
3843
enum {
3944
FRA_UNSPEC,
4045
FRA_DST, /* destination address */
@@ -59,6 +64,9 @@ enum {
5964
FRA_L3MDEV, /* iif or oif is l3mdev goto its table */
6065
FRA_UID_RANGE, /* UID range */
6166
FRA_PROTOCOL, /* Originator of the rule */
67+
FRA_IP_PROTO, /* ip proto */
68+
FRA_SPORT_RANGE, /* sport */
69+
FRA_DPORT_RANGE, /* dport */
6270
__FRA_MAX
6371
};
6472

net/core/fib_rules.c

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ bool fib_rule_matchall(const struct fib_rule *rule)
3333
if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) ||
3434
!uid_eq(rule->uid_range.end, fib_kuid_range_unset.end))
3535
return false;
36+
if (fib_rule_port_range_set(&rule->sport_range))
37+
return false;
38+
if (fib_rule_port_range_set(&rule->dport_range))
39+
return false;
3640
return true;
3741
}
3842
EXPORT_SYMBOL_GPL(fib_rule_matchall);
@@ -221,6 +225,26 @@ static int nla_put_uid_range(struct sk_buff *skb, struct fib_kuid_range *range)
221225
return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out);
222226
}
223227

228+
static int nla_get_port_range(struct nlattr *pattr,
229+
struct fib_rule_port_range *port_range)
230+
{
231+
const struct fib_rule_port_range *pr = nla_data(pattr);
232+
233+
if (!fib_rule_port_range_valid(pr))
234+
return -EINVAL;
235+
236+
port_range->start = pr->start;
237+
port_range->end = pr->end;
238+
239+
return 0;
240+
}
241+
242+
static int nla_put_port_range(struct sk_buff *skb, int attrtype,
243+
struct fib_rule_port_range *range)
244+
{
245+
return nla_put(skb, attrtype, sizeof(*range), range);
246+
}
247+
224248
static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
225249
struct flowi *fl, int flags,
226250
struct fib_lookup_arg *arg)
@@ -425,6 +449,17 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh,
425449
!uid_eq(r->uid_range.end, rule->uid_range.end))
426450
continue;
427451

452+
if (r->ip_proto != rule->ip_proto)
453+
continue;
454+
455+
if (!fib_rule_port_range_compare(&r->sport_range,
456+
&rule->sport_range))
457+
continue;
458+
459+
if (!fib_rule_port_range_compare(&r->dport_range,
460+
&rule->dport_range))
461+
continue;
462+
428463
if (!ops->compare(r, frh, tb))
429464
continue;
430465
return 1;
@@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh,
569604
rule->uid_range = fib_kuid_range_unset;
570605
}
571606

607+
if (tb[FRA_IP_PROTO])
608+
rule->ip_proto = nla_get_u8(tb[FRA_IP_PROTO]);
609+
610+
if (tb[FRA_SPORT_RANGE]) {
611+
err = nla_get_port_range(tb[FRA_SPORT_RANGE],
612+
&rule->sport_range);
613+
if (err)
614+
goto errout_free;
615+
}
616+
617+
if (tb[FRA_DPORT_RANGE]) {
618+
err = nla_get_port_range(tb[FRA_DPORT_RANGE],
619+
&rule->dport_range);
620+
if (err)
621+
goto errout_free;
622+
}
623+
572624
if ((nlh->nlmsg_flags & NLM_F_EXCL) &&
573625
rule_exists(ops, frh, tb, rule)) {
574626
err = -EEXIST;
@@ -634,6 +686,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
634686
{
635687
struct net *net = sock_net(skb->sk);
636688
struct fib_rule_hdr *frh = nlmsg_data(nlh);
689+
struct fib_rule_port_range sprange = {0, 0};
690+
struct fib_rule_port_range dprange = {0, 0};
637691
struct fib_rules_ops *ops = NULL;
638692
struct fib_rule *rule, *r;
639693
struct nlattr *tb[FRA_MAX+1];
@@ -667,6 +721,20 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
667721
range = fib_kuid_range_unset;
668722
}
669723

724+
if (tb[FRA_SPORT_RANGE]) {
725+
err = nla_get_port_range(tb[FRA_SPORT_RANGE],
726+
&sprange);
727+
if (err)
728+
goto errout;
729+
}
730+
731+
if (tb[FRA_DPORT_RANGE]) {
732+
err = nla_get_port_range(tb[FRA_DPORT_RANGE],
733+
&dprange);
734+
if (err)
735+
goto errout;
736+
}
737+
670738
list_for_each_entry(rule, &ops->rules_list, list) {
671739
if (tb[FRA_PROTOCOL] &&
672740
(rule->proto != nla_get_u8(tb[FRA_PROTOCOL])))
@@ -712,6 +780,18 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
712780
!uid_eq(rule->uid_range.end, range.end)))
713781
continue;
714782

783+
if (tb[FRA_IP_PROTO] &&
784+
(rule->ip_proto != nla_get_u8(tb[FRA_IP_PROTO])))
785+
continue;
786+
787+
if (fib_rule_port_range_set(&sprange) &&
788+
!fib_rule_port_range_compare(&rule->sport_range, &sprange))
789+
continue;
790+
791+
if (fib_rule_port_range_set(&dprange) &&
792+
!fib_rule_port_range_compare(&rule->dport_range, &dprange))
793+
continue;
794+
715795
if (!ops->compare(rule, frh, tb))
716796
continue;
717797

@@ -790,7 +870,10 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops,
790870
+ nla_total_size(4) /* FRA_FWMASK */
791871
+ nla_total_size_64bit(8) /* FRA_TUN_ID */
792872
+ nla_total_size(sizeof(struct fib_kuid_range))
793-
+ nla_total_size(1); /* FRA_PROTOCOL */
873+
+ nla_total_size(1) /* FRA_PROTOCOL */
874+
+ nla_total_size(1) /* FRA_IP_PROTO */
875+
+ nla_total_size(sizeof(struct fib_rule_port_range)) /* FRA_SPORT_RANGE */
876+
+ nla_total_size(sizeof(struct fib_rule_port_range)); /* FRA_DPORT_RANGE */
794877

795878
if (ops->nlmsg_payload)
796879
payload += ops->nlmsg_payload(rule);
@@ -855,7 +938,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule,
855938
(rule->l3mdev &&
856939
nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) ||
857940
(uid_range_set(&rule->uid_range) &&
858-
nla_put_uid_range(skb, &rule->uid_range)))
941+
nla_put_uid_range(skb, &rule->uid_range)) ||
942+
(fib_rule_port_range_set(&rule->sport_range) &&
943+
nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) ||
944+
(fib_rule_port_range_set(&rule->dport_range) &&
945+
nla_put_port_range(skb, FRA_DPORT_RANGE, &rule->dport_range)) ||
946+
(rule->ip_proto && nla_put_u8(skb, FRA_IP_PROTO, rule->ip_proto)))
859947
goto nla_put_failure;
860948

861949
if (rule->suppress_ifgroup != -1) {

0 commit comments

Comments
 (0)