Commit a25724b0 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 29274991 5e5d6fed
...@@ -27,7 +27,7 @@ struct fib_rule { ...@@ -27,7 +27,7 @@ struct fib_rule {
u8 action; u8 action;
u8 l3mdev; u8 l3mdev;
u8 proto; u8 proto;
/* 1 byte hole, try to use */ u8 ip_proto;
u32 target; u32 target;
__be64 tun_id; __be64 tun_id;
struct fib_rule __rcu *ctarget; struct fib_rule __rcu *ctarget;
...@@ -40,6 +40,8 @@ struct fib_rule { ...@@ -40,6 +40,8 @@ struct fib_rule {
char iifname[IFNAMSIZ]; char iifname[IFNAMSIZ];
char oifname[IFNAMSIZ]; char oifname[IFNAMSIZ];
struct fib_kuid_range uid_range; struct fib_kuid_range uid_range;
struct fib_rule_port_range sport_range;
struct fib_rule_port_range dport_range;
struct rcu_head rcu; struct rcu_head rcu;
}; };
...@@ -144,6 +146,38 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla) ...@@ -144,6 +146,38 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla)
return frh->table; return frh->table;
} }
static inline bool fib_rule_port_range_set(const struct fib_rule_port_range *range)
{
return range->start != 0 && range->end != 0;
}
static inline bool fib_rule_port_inrange(const struct fib_rule_port_range *a,
__be16 port)
{
return ntohs(port) >= a->start &&
ntohs(port) <= a->end;
}
static inline bool fib_rule_port_range_valid(const struct fib_rule_port_range *a)
{
return a->start != 0 && a->end != 0 && a->end < 0xffff &&
a->start <= a->end;
}
static inline bool fib_rule_port_range_compare(struct fib_rule_port_range *a,
struct fib_rule_port_range *b)
{
return a->start == b->start &&
a->end == b->end;
}
static inline bool fib_rule_requires_fldissect(struct fib_rule *rule)
{
return rule->ip_proto ||
fib_rule_port_range_set(&rule->sport_range) ||
fib_rule_port_range_set(&rule->dport_range);
}
struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *, struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *,
struct net *); struct net *);
void fib_rules_unregister(struct fib_rules_ops *); void fib_rules_unregister(struct fib_rules_ops *);
......
...@@ -415,6 +415,24 @@ void fib6_rules_cleanup(void); ...@@ -415,6 +415,24 @@ void fib6_rules_cleanup(void);
bool fib6_rule_default(const struct fib_rule *rule); bool fib6_rule_default(const struct fib_rule *rule);
int fib6_rules_dump(struct net *net, struct notifier_block *nb); int fib6_rules_dump(struct net *net, struct notifier_block *nb);
unsigned int fib6_rules_seq_read(struct net *net); unsigned int fib6_rules_seq_read(struct net *net);
static inline bool fib6_rules_early_flow_dissect(struct net *net,
struct sk_buff *skb,
struct flowi6 *fl6,
struct flow_keys *flkeys)
{
unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
if (!net->ipv6.fib6_rules_require_fldissect)
return false;
skb_flow_dissect_flow_keys(skb, flkeys, flag);
fl6->fl6_sport = flkeys->ports.src;
fl6->fl6_dport = flkeys->ports.dst;
fl6->flowi6_proto = flkeys->basic.ip_proto;
return true;
}
#else #else
static inline int fib6_rules_init(void) static inline int fib6_rules_init(void)
{ {
...@@ -436,5 +454,12 @@ static inline unsigned int fib6_rules_seq_read(struct net *net) ...@@ -436,5 +454,12 @@ static inline unsigned int fib6_rules_seq_read(struct net *net)
{ {
return 0; return 0;
} }
static inline bool fib6_rules_early_flow_dissect(struct net *net,
struct sk_buff *skb,
struct flowi6 *fl6,
struct flow_keys *flkeys)
{
return false;
}
#endif #endif
#endif #endif
...@@ -127,7 +127,8 @@ static inline int ip6_route_get_saddr(struct net *net, struct rt6_info *rt, ...@@ -127,7 +127,8 @@ static inline int ip6_route_get_saddr(struct net *net, struct rt6_info *rt,
struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
const struct in6_addr *saddr, int oif, int flags); const struct in6_addr *saddr, int oif, int flags);
u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb); u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb,
struct flow_keys *hkeys);
struct dst_entry *icmp6_dst_alloc(struct net_device *dev, struct flowi6 *fl6); struct dst_entry *icmp6_dst_alloc(struct net_device *dev, struct flowi6 *fl6);
...@@ -266,4 +267,5 @@ static inline bool rt6_duplicate_nexthop(struct rt6_info *a, struct rt6_info *b) ...@@ -266,4 +267,5 @@ static inline bool rt6_duplicate_nexthop(struct rt6_info *a, struct rt6_info *b)
ipv6_addr_equal(&a->rt6i_gateway, &b->rt6i_gateway) && ipv6_addr_equal(&a->rt6i_gateway, &b->rt6i_gateway) &&
!lwtunnel_cmp_encap(a->dst.lwtstate, b->dst.lwtstate); !lwtunnel_cmp_encap(a->dst.lwtstate, b->dst.lwtstate);
} }
#endif #endif
...@@ -293,6 +293,13 @@ static inline unsigned int fib4_rules_seq_read(struct net *net) ...@@ -293,6 +293,13 @@ static inline unsigned int fib4_rules_seq_read(struct net *net)
return 0; return 0;
} }
static inline bool fib4_rules_early_flow_dissect(struct net *net,
struct sk_buff *skb,
struct flowi4 *fl4,
struct flow_keys *flkeys)
{
return false;
}
#else /* CONFIG_IP_MULTIPLE_TABLES */ #else /* CONFIG_IP_MULTIPLE_TABLES */
int __net_init fib4_rules_init(struct net *net); int __net_init fib4_rules_init(struct net *net);
void __net_exit fib4_rules_exit(struct net *net); void __net_exit fib4_rules_exit(struct net *net);
...@@ -341,6 +348,24 @@ bool fib4_rule_default(const struct fib_rule *rule); ...@@ -341,6 +348,24 @@ bool fib4_rule_default(const struct fib_rule *rule);
int fib4_rules_dump(struct net *net, struct notifier_block *nb); int fib4_rules_dump(struct net *net, struct notifier_block *nb);
unsigned int fib4_rules_seq_read(struct net *net); unsigned int fib4_rules_seq_read(struct net *net);
static inline bool fib4_rules_early_flow_dissect(struct net *net,
struct sk_buff *skb,
struct flowi4 *fl4,
struct flow_keys *flkeys)
{
unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
if (!net->ipv4.fib_rules_require_fldissect)
return false;
skb_flow_dissect_flow_keys(skb, flkeys, flag);
fl4->fl4_sport = flkeys->ports.src;
fl4->fl4_dport = flkeys->ports.dst;
fl4->flowi4_proto = flkeys->basic.ip_proto;
return true;
}
#endif /* CONFIG_IP_MULTIPLE_TABLES */ #endif /* CONFIG_IP_MULTIPLE_TABLES */
/* Exported by fib_frontend.c */ /* Exported by fib_frontend.c */
...@@ -371,7 +396,7 @@ int fib_sync_up(struct net_device *dev, unsigned int nh_flags); ...@@ -371,7 +396,7 @@ int fib_sync_up(struct net_device *dev, unsigned int nh_flags);
#ifdef CONFIG_IP_ROUTE_MULTIPATH #ifdef CONFIG_IP_ROUTE_MULTIPATH
int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4, int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4,
const struct sk_buff *skb); const struct sk_buff *skb, struct flow_keys *flkeys);
#endif #endif
void fib_select_multipath(struct fib_result *res, int hash); void fib_select_multipath(struct fib_result *res, int hash);
void fib_select_path(struct net *net, struct fib_result *res, void fib_select_path(struct net *net, struct fib_result *res,
......
...@@ -52,6 +52,7 @@ struct netns_ipv4 { ...@@ -52,6 +52,7 @@ struct netns_ipv4 {
#ifdef CONFIG_IP_MULTIPLE_TABLES #ifdef CONFIG_IP_MULTIPLE_TABLES
struct fib_rules_ops *rules_ops; struct fib_rules_ops *rules_ops;
bool fib_has_custom_rules; bool fib_has_custom_rules;
unsigned int fib_rules_require_fldissect;
struct fib_table __rcu *fib_main; struct fib_table __rcu *fib_main;
struct fib_table __rcu *fib_default; struct fib_table __rcu *fib_default;
#endif #endif
......
...@@ -71,7 +71,8 @@ struct netns_ipv6 { ...@@ -71,7 +71,8 @@ struct netns_ipv6 {
unsigned int ip6_rt_gc_expire; unsigned int ip6_rt_gc_expire;
unsigned long ip6_rt_last_gc; unsigned long ip6_rt_last_gc;
#ifdef CONFIG_IPV6_MULTIPLE_TABLES #ifdef CONFIG_IPV6_MULTIPLE_TABLES
bool fib6_has_custom_rules; unsigned int fib6_rules_require_fldissect;
bool fib6_has_custom_rules;
struct rt6_info *ip6_prohibit_entry; struct rt6_info *ip6_prohibit_entry;
struct rt6_info *ip6_blk_hole_entry; struct rt6_info *ip6_blk_hole_entry;
struct fib6_table *fib6_local_tbl; struct fib6_table *fib6_local_tbl;
......
...@@ -35,6 +35,11 @@ struct fib_rule_uid_range { ...@@ -35,6 +35,11 @@ struct fib_rule_uid_range {
__u32 end; __u32 end;
}; };
struct fib_rule_port_range {
__u16 start;
__u16 end;
};
enum { enum {
FRA_UNSPEC, FRA_UNSPEC,
FRA_DST, /* destination address */ FRA_DST, /* destination address */
...@@ -59,6 +64,9 @@ enum { ...@@ -59,6 +64,9 @@ enum {
FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ FRA_L3MDEV, /* iif or oif is l3mdev goto its table */
FRA_UID_RANGE, /* UID range */ FRA_UID_RANGE, /* UID range */
FRA_PROTOCOL, /* Originator of the rule */ FRA_PROTOCOL, /* Originator of the rule */
FRA_IP_PROTO, /* ip proto */
FRA_SPORT_RANGE, /* sport */
FRA_DPORT_RANGE, /* dport */
__FRA_MAX __FRA_MAX
}; };
......
...@@ -33,6 +33,10 @@ bool fib_rule_matchall(const struct fib_rule *rule) ...@@ -33,6 +33,10 @@ bool fib_rule_matchall(const struct fib_rule *rule)
if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) || if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) ||
!uid_eq(rule->uid_range.end, fib_kuid_range_unset.end)) !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end))
return false; return false;
if (fib_rule_port_range_set(&rule->sport_range))
return false;
if (fib_rule_port_range_set(&rule->dport_range))
return false;
return true; return true;
} }
EXPORT_SYMBOL_GPL(fib_rule_matchall); 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) ...@@ -221,6 +225,26 @@ static int nla_put_uid_range(struct sk_buff *skb, struct fib_kuid_range *range)
return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out); return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out);
} }
static int nla_get_port_range(struct nlattr *pattr,
struct fib_rule_port_range *port_range)
{
const struct fib_rule_port_range *pr = nla_data(pattr);
if (!fib_rule_port_range_valid(pr))
return -EINVAL;
port_range->start = pr->start;
port_range->end = pr->end;
return 0;
}
static int nla_put_port_range(struct sk_buff *skb, int attrtype,
struct fib_rule_port_range *range)
{
return nla_put(skb, attrtype, sizeof(*range), range);
}
static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
struct flowi *fl, int flags, struct flowi *fl, int flags,
struct fib_lookup_arg *arg) struct fib_lookup_arg *arg)
...@@ -425,6 +449,17 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh, ...@@ -425,6 +449,17 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh,
!uid_eq(r->uid_range.end, rule->uid_range.end)) !uid_eq(r->uid_range.end, rule->uid_range.end))
continue; continue;
if (r->ip_proto != rule->ip_proto)
continue;
if (!fib_rule_port_range_compare(&r->sport_range,
&rule->sport_range))
continue;
if (!fib_rule_port_range_compare(&r->dport_range,
&rule->dport_range))
continue;
if (!ops->compare(r, frh, tb)) if (!ops->compare(r, frh, tb))
continue; continue;
return 1; return 1;
...@@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh,
rule->uid_range = fib_kuid_range_unset; rule->uid_range = fib_kuid_range_unset;
} }
if (tb[FRA_IP_PROTO])
rule->ip_proto = nla_get_u8(tb[FRA_IP_PROTO]);
if (tb[FRA_SPORT_RANGE]) {
err = nla_get_port_range(tb[FRA_SPORT_RANGE],
&rule->sport_range);
if (err)
goto errout_free;
}
if (tb[FRA_DPORT_RANGE]) {
err = nla_get_port_range(tb[FRA_DPORT_RANGE],
&rule->dport_range);
if (err)
goto errout_free;
}
if ((nlh->nlmsg_flags & NLM_F_EXCL) && if ((nlh->nlmsg_flags & NLM_F_EXCL) &&
rule_exists(ops, frh, tb, rule)) { rule_exists(ops, frh, tb, rule)) {
err = -EEXIST; err = -EEXIST;
...@@ -634,6 +686,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -634,6 +686,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct fib_rule_hdr *frh = nlmsg_data(nlh); struct fib_rule_hdr *frh = nlmsg_data(nlh);
struct fib_rule_port_range sprange = {0, 0};
struct fib_rule_port_range dprange = {0, 0};
struct fib_rules_ops *ops = NULL; struct fib_rules_ops *ops = NULL;
struct fib_rule *rule, *r; struct fib_rule *rule, *r;
struct nlattr *tb[FRA_MAX+1]; struct nlattr *tb[FRA_MAX+1];
...@@ -667,6 +721,20 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -667,6 +721,20 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
range = fib_kuid_range_unset; range = fib_kuid_range_unset;
} }
if (tb[FRA_SPORT_RANGE]) {
err = nla_get_port_range(tb[FRA_SPORT_RANGE],
&sprange);
if (err)
goto errout;
}
if (tb[FRA_DPORT_RANGE]) {
err = nla_get_port_range(tb[FRA_DPORT_RANGE],
&dprange);
if (err)
goto errout;
}
list_for_each_entry(rule, &ops->rules_list, list) { list_for_each_entry(rule, &ops->rules_list, list) {
if (tb[FRA_PROTOCOL] && if (tb[FRA_PROTOCOL] &&
(rule->proto != nla_get_u8(tb[FRA_PROTOCOL]))) (rule->proto != nla_get_u8(tb[FRA_PROTOCOL])))
...@@ -712,6 +780,18 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -712,6 +780,18 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
!uid_eq(rule->uid_range.end, range.end))) !uid_eq(rule->uid_range.end, range.end)))
continue; continue;
if (tb[FRA_IP_PROTO] &&
(rule->ip_proto != nla_get_u8(tb[FRA_IP_PROTO])))
continue;
if (fib_rule_port_range_set(&sprange) &&
!fib_rule_port_range_compare(&rule->sport_range, &sprange))
continue;
if (fib_rule_port_range_set(&dprange) &&
!fib_rule_port_range_compare(&rule->dport_range, &dprange))
continue;
if (!ops->compare(rule, frh, tb)) if (!ops->compare(rule, frh, tb))
continue; continue;
...@@ -790,7 +870,10 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, ...@@ -790,7 +870,10 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops,
+ nla_total_size(4) /* FRA_FWMASK */ + nla_total_size(4) /* FRA_FWMASK */
+ nla_total_size_64bit(8) /* FRA_TUN_ID */ + nla_total_size_64bit(8) /* FRA_TUN_ID */
+ nla_total_size(sizeof(struct fib_kuid_range)) + nla_total_size(sizeof(struct fib_kuid_range))
+ nla_total_size(1); /* FRA_PROTOCOL */ + nla_total_size(1) /* FRA_PROTOCOL */
+ nla_total_size(1) /* FRA_IP_PROTO */
+ nla_total_size(sizeof(struct fib_rule_port_range)) /* FRA_SPORT_RANGE */
+ nla_total_size(sizeof(struct fib_rule_port_range)); /* FRA_DPORT_RANGE */
if (ops->nlmsg_payload) if (ops->nlmsg_payload)
payload += ops->nlmsg_payload(rule); payload += ops->nlmsg_payload(rule);
...@@ -855,7 +938,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, ...@@ -855,7 +938,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule,
(rule->l3mdev && (rule->l3mdev &&
nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) || nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) ||
(uid_range_set(&rule->uid_range) && (uid_range_set(&rule->uid_range) &&
nla_put_uid_range(skb, &rule->uid_range))) nla_put_uid_range(skb, &rule->uid_range)) ||
(fib_rule_port_range_set(&rule->sport_range) &&
nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) ||
(fib_rule_port_range_set(&rule->dport_range) &&
nla_put_port_range(skb, FRA_DPORT_RANGE, &rule->dport_range)) ||
(rule->ip_proto && nla_put_u8(skb, FRA_IP_PROTO, rule->ip_proto)))
goto nla_put_failure; goto nla_put_failure;
if (rule->suppress_ifgroup != -1) { if (rule->suppress_ifgroup != -1) {
......
...@@ -182,6 +182,17 @@ static int fib4_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) ...@@ -182,6 +182,17 @@ static int fib4_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
if (r->tos && (r->tos != fl4->flowi4_tos)) if (r->tos && (r->tos != fl4->flowi4_tos))
return 0; return 0;
if (rule->ip_proto && (rule->ip_proto != fl4->flowi4_proto))
return 0;
if (fib_rule_port_range_set(&rule->sport_range) &&
!fib_rule_port_inrange(&rule->sport_range, fl4->fl4_sport))
return 0;
if (fib_rule_port_range_set(&rule->dport_range) &&
!fib_rule_port_inrange(&rule->dport_range, fl4->fl4_dport))
return 0;
return 1; return 1;
} }
...@@ -244,6 +255,9 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, ...@@ -244,6 +255,9 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
} }
#endif #endif
if (fib_rule_requires_fldissect(rule))
net->ipv4.fib_rules_require_fldissect++;
rule4->src_len = frh->src_len; rule4->src_len = frh->src_len;
rule4->srcmask = inet_make_mask(rule4->src_len); rule4->srcmask = inet_make_mask(rule4->src_len);
rule4->dst_len = frh->dst_len; rule4->dst_len = frh->dst_len;
...@@ -272,6 +286,10 @@ static int fib4_rule_delete(struct fib_rule *rule) ...@@ -272,6 +286,10 @@ static int fib4_rule_delete(struct fib_rule *rule)
net->ipv4.fib_num_tclassid_users--; net->ipv4.fib_num_tclassid_users--;
#endif #endif
net->ipv4.fib_has_custom_rules = true; net->ipv4.fib_has_custom_rules = true;
if (net->ipv4.fib_rules_require_fldissect &&
fib_rule_requires_fldissect(rule))
net->ipv4.fib_rules_require_fldissect--;
errout: errout:
return err; return err;
} }
...@@ -389,6 +407,7 @@ int __net_init fib4_rules_init(struct net *net) ...@@ -389,6 +407,7 @@ int __net_init fib4_rules_init(struct net *net)
goto fail; goto fail;
net->ipv4.rules_ops = ops; net->ipv4.rules_ops = ops;
net->ipv4.fib_has_custom_rules = false; net->ipv4.fib_has_custom_rules = false;
net->ipv4.fib_rules_require_fldissect = 0;
return 0; return 0;
fail: fail:
......
...@@ -1770,7 +1770,7 @@ void fib_select_path(struct net *net, struct fib_result *res, ...@@ -1770,7 +1770,7 @@ void fib_select_path(struct net *net, struct fib_result *res,
#ifdef CONFIG_IP_ROUTE_MULTIPATH #ifdef CONFIG_IP_ROUTE_MULTIPATH
if (res->fi->fib_nhs > 1) { if (res->fi->fib_nhs > 1) {
int h = fib_multipath_hash(res->fi, fl4, skb); int h = fib_multipath_hash(res->fi, fl4, skb, NULL);
fib_select_multipath(res, h); fib_select_multipath(res, h);
} }
......
...@@ -1783,7 +1783,7 @@ static void ip_multipath_l3_keys(const struct sk_buff *skb, ...@@ -1783,7 +1783,7 @@ static void ip_multipath_l3_keys(const struct sk_buff *skb,
/* if skb is set it will be used and fl4 can be NULL */ /* if skb is set it will be used and fl4 can be NULL */
int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4, int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4,
const struct sk_buff *skb) const struct sk_buff *skb, struct flow_keys *flkeys)
{ {
struct net *net = fi->fib_net; struct net *net = fi->fib_net;
struct flow_keys hash_keys; struct flow_keys hash_keys;
...@@ -1810,14 +1810,23 @@ int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4, ...@@ -1810,14 +1810,23 @@ int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4,
if (skb->l4_hash) if (skb->l4_hash)
return skb_get_hash_raw(skb) >> 1; return skb_get_hash_raw(skb) >> 1;
memset(&hash_keys, 0, sizeof(hash_keys)); memset(&hash_keys, 0, sizeof(hash_keys));
skb_flow_dissect_flow_keys(skb, &keys, flag);
hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; if (flkeys) {
hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src; hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst; hash_keys.addrs.v4addrs.src = flkeys->addrs.v4addrs.src;
hash_keys.ports.src = keys.ports.src; hash_keys.addrs.v4addrs.dst = flkeys->addrs.v4addrs.dst;
hash_keys.ports.dst = keys.ports.dst; hash_keys.ports.src = flkeys->ports.src;
hash_keys.basic.ip_proto = keys.basic.ip_proto; hash_keys.ports.dst = flkeys->ports.dst;
hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
} else {
skb_flow_dissect_flow_keys(skb, &keys, flag);
hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src;
hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst;
hash_keys.ports.src = keys.ports.src;
hash_keys.ports.dst = keys.ports.dst;
hash_keys.basic.ip_proto = keys.basic.ip_proto;
}
} else { } else {
memset(&hash_keys, 0, sizeof(hash_keys)); memset(&hash_keys, 0, sizeof(hash_keys));
hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
...@@ -1838,11 +1847,12 @@ int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4, ...@@ -1838,11 +1847,12 @@ int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4,
static int ip_mkroute_input(struct sk_buff *skb, static int ip_mkroute_input(struct sk_buff *skb,
struct fib_result *res, struct fib_result *res,
struct in_device *in_dev, struct in_device *in_dev,
__be32 daddr, __be32 saddr, u32 tos) __be32 daddr, __be32 saddr, u32 tos,
struct flow_keys *hkeys)
{ {
#ifdef CONFIG_IP_ROUTE_MULTIPATH #ifdef CONFIG_IP_ROUTE_MULTIPATH
if (res->fi && res->fi->fib_nhs > 1) { if (res->fi && res->fi->fib_nhs > 1) {
int h = fib_multipath_hash(res->fi, NULL, skb); int h = fib_multipath_hash(res->fi, NULL, skb, hkeys);
fib_select_multipath(res, h); fib_select_multipath(res, h);
} }
...@@ -1868,13 +1878,14 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, ...@@ -1868,13 +1878,14 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
struct fib_result *res) struct fib_result *res)
{ {
struct in_device *in_dev = __in_dev_get_rcu(dev); struct in_device *in_dev = __in_dev_get_rcu(dev);
struct flow_keys *flkeys = NULL, _flkeys;
struct net *net = dev_net(dev);
struct ip_tunnel_info *tun_info; struct ip_tunnel_info *tun_info;
struct flowi4 fl4; int err = -EINVAL;
unsigned int flags = 0; unsigned int flags = 0;
u32 itag = 0; u32 itag = 0;
struct rtable *rth; struct rtable *rth;
int err = -EINVAL; struct flowi4 fl4;
struct net *net = dev_net(dev);
bool do_cache; bool do_cache;
/* IP on this device is disabled. */ /* IP on this device is disabled. */
...@@ -1933,6 +1944,10 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, ...@@ -1933,6 +1944,10 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
fl4.daddr = daddr; fl4.daddr = daddr;
fl4.saddr = saddr; fl4.saddr = saddr;
fl4.flowi4_uid = sock_net_uid(net, NULL); fl4.flowi4_uid = sock_net_uid(net, NULL);
if (fib4_rules_early_flow_dissect(net, skb, &fl4, &_flkeys))
flkeys = &_flkeys;
err = fib_lookup(net, &fl4, res, 0); err = fib_lookup(net, &fl4, res, 0);
if (err != 0) { if (err != 0) {
if (!IN_DEV_FORWARD(in_dev)) if (!IN_DEV_FORWARD(in_dev))
...@@ -1958,7 +1973,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, ...@@ -1958,7 +1973,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
if (res->type != RTN_UNICAST) if (res->type != RTN_UNICAST)
goto martian_destination; goto martian_destination;
err = ip_mkroute_input(skb, res, in_dev, daddr, saddr, tos); err = ip_mkroute_input(skb, res, in_dev, daddr, saddr, tos, flkeys);
out: return err; out: return err;
brd_input: brd_input:
......
...@@ -223,6 +223,17 @@ static int fib6_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) ...@@ -223,6 +223,17 @@ static int fib6_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
if (r->tclass && r->tclass != ip6_tclass(fl6->flowlabel)) if (r->tclass && r->tclass != ip6_tclass(fl6->flowlabel))
return 0; return 0;
if (rule->ip_proto && (rule->ip_proto != fl6->flowi6_proto))
return 0;
if (fib_rule_port_range_set(&rule->sport_range) &&
!fib_rule_port_inrange(&rule->sport_range, fl6->fl6_sport))
return 0;
if (fib_rule_port_range_set(&rule->dport_range) &&
!fib_rule_port_inrange(&rule->dport_range, fl6->fl6_dport))
return 0;
return 1; return 1;
} }
...@@ -258,12 +269,26 @@ static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb, ...@@ -258,12 +269,26 @@ static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
rule6->dst.plen = frh->dst_len; rule6->dst.plen = frh->dst_len;
rule6->tclass = frh->tos; rule6->tclass = frh->tos;
if (fib_rule_requires_fldissect(rule))
net->ipv6.fib6_rules_require_fldissect++;
net->ipv6.fib6_has_custom_rules = true; net->ipv6.fib6_has_custom_rules = true;
err = 0; err = 0;
errout: errout:
return err; return err;
} }
static int fib6_rule_delete(struct fib_rule *rule)
{
struct net *net = rule->fr_net;
if (net->ipv6.fib6_rules_require_fldissect &&
fib_rule_requires_fldissect(rule))
net->ipv6.fib6_rules_require_fldissect--;
return 0;
}
static int fib6_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, static int fib6_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
struct nlattr **tb) struct nlattr **tb)
{ {
...@@ -323,6 +348,7 @@ static const struct fib_rules_ops __net_initconst fib6_rules_ops_template = { ...@@ -323,6 +348,7 @@ static const struct fib_rules_ops __net_initconst fib6_rules_ops_template = {
.match = fib6_rule_match, .match = fib6_rule_match,
.suppress = fib6_rule_suppress, .suppress = fib6_rule_suppress,
.configure = fib6_rule_configure, .configure = fib6_rule_configure,
.delete = fib6_rule_delete,
.compare = fib6_rule_compare, .compare = fib6_rule_compare,
.fill = fib6_rule_fill, .fill = fib6_rule_fill,
.nlmsg_payload = fib6_rule_nlmsg_payload, .nlmsg_payload = fib6_rule_nlmsg_payload,
...@@ -350,6 +376,7 @@ static int __net_init fib6_rules_net_init(struct net *net) ...@@ -350,6 +376,7 @@ static int __net_init fib6_rules_net_init(struct net *net)
goto out_fib6_rules_ops; goto out_fib6_rules_ops;
net->ipv6.fib6_rules_ops = ops; net->ipv6.fib6_rules_ops = ops;
net->ipv6.fib6_rules_require_fldissect = 0;
out: out:
return err; return err;
......
...@@ -522,7 +522,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, ...@@ -522,7 +522,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
fl6.fl6_icmp_type = type; fl6.fl6_icmp_type = type;
fl6.fl6_icmp_code = code; fl6.fl6_icmp_code = code;
fl6.flowi6_uid = sock_net_uid(net, NULL); fl6.flowi6_uid = sock_net_uid(net, NULL);
fl6.mp_hash = rt6_multipath_hash(&fl6, skb); fl6.mp_hash = rt6_multipath_hash(&fl6, skb, NULL);
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
sk = icmpv6_xmit_lock(net); sk = icmpv6_xmit_lock(net);
......
...@@ -460,7 +460,7 @@ static struct rt6_info *rt6_multipath_select(struct rt6_info *match, ...@@ -460,7 +460,7 @@ static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
* case it will always be non-zero. Otherwise now is the time to do it. * case it will always be non-zero. Otherwise now is the time to do it.
*/ */
if (!fl6->mp_hash) if (!fl6->mp_hash)
fl6->mp_hash = rt6_multipath_hash(fl6, NULL); fl6->mp_hash = rt6_multipath_hash(fl6, NULL, NULL);
if (fl6->mp_hash <= atomic_read(&match->rt6i_nh_upper_bound)) if (fl6->mp_hash <= atomic_read(&match->rt6i_nh_upper_bound))
return match; return match;
...@@ -1786,10 +1786,12 @@ struct dst_entry *ip6_route_input_lookup(struct net *net, ...@@ -1786,10 +1786,12 @@ struct dst_entry *ip6_route_input_lookup(struct net *net,
EXPORT_SYMBOL_GPL(ip6_route_input_lookup); EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
static void ip6_multipath_l3_keys(const struct sk_buff *skb, static void ip6_multipath_l3_keys(const struct sk_buff *skb,
struct flow_keys *keys) struct flow_keys *keys,
struct flow_keys *flkeys)
{ {
const struct ipv6hdr *outer_iph = ipv6_hdr(skb); const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
const struct ipv6hdr *key_iph = outer_iph; const struct ipv6hdr *key_iph = outer_iph;
struct flow_keys *_flkeys = flkeys;
const struct ipv6hdr *inner_iph; const struct ipv6hdr *inner_iph;
const struct icmp6hdr *icmph; const struct icmp6hdr *icmph;
struct ipv6hdr _inner_iph; struct ipv6hdr _inner_iph;
...@@ -1811,22 +1813,31 @@ static void ip6_multipath_l3_keys(const struct sk_buff *skb, ...@@ -1811,22 +1813,31 @@ static void ip6_multipath_l3_keys(const struct sk_buff *skb,
goto out; goto out;
key_iph = inner_iph; key_iph = inner_iph;
_flkeys = NULL;
out: out:
memset(keys, 0, sizeof(*keys)); memset(keys, 0, sizeof(*keys));
keys->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; keys->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
keys->addrs.v6addrs.src = key_iph->saddr; if (_flkeys) {
keys->addrs.v6addrs.dst = key_iph->daddr; keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src;
keys->tags.flow_label = ip6_flowinfo(key_iph); keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst;
keys->basic.ip_proto = key_iph->nexthdr; keys->tags.flow_label = _flkeys->tags.flow_label;
keys->basic.ip_proto = _flkeys->basic.ip_proto;
} else {
keys->addrs.v6addrs.src = key_iph->saddr;
keys->addrs.v6addrs.dst = key_iph->daddr;
keys->tags.flow_label = ip6_flowinfo(key_iph);
keys->basic.ip_proto = key_iph->nexthdr;
}
} }
/* if skb is set it will be used and fl6 can be NULL */ /* if skb is set it will be used and fl6 can be NULL */
u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb) u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb,
struct flow_keys *flkeys)
{ {
struct flow_keys hash_keys; struct flow_keys hash_keys;
if (skb) { if (skb) {
ip6_multipath_l3_keys(skb, &hash_keys); ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
return flow_hash_from_keys(&hash_keys) >> 1; return flow_hash_from_keys(&hash_keys) >> 1;
} }
...@@ -1847,12 +1858,17 @@ void ip6_route_input(struct sk_buff *skb) ...@@ -1847,12 +1858,17 @@ void ip6_route_input(struct sk_buff *skb)
.flowi6_mark = skb->mark, .flowi6_mark = skb->mark,
.flowi6_proto = iph->nexthdr, .flowi6_proto = iph->nexthdr,
}; };
struct flow_keys *flkeys = NULL, _flkeys;
tun_info = skb_tunnel_info(skb); tun_info = skb_tunnel_info(skb);
if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX)) if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id; fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys))
flkeys = &_flkeys;
if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6)) if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
fl6.mp_hash = rt6_multipath_hash(&fl6, skb); fl6.mp_hash = rt6_multipath_hash(&fl6, skb, flkeys);
skb_dst_drop(skb); skb_dst_drop(skb);
skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment