Add IPv6 equivalent to fib_lookup. Does a fib lookup, including rules,
but returns a FIB entry, fib6_info, rather than a dst based rt6_info.
fib6_lookup is any where from 140% (MULTIPLE_TABLES config disabled)
to 60% faster than any of the dst based lookup methods (without custom
rules) and 25% faster with custom rules (e.g., l3mdev rule).

Since the lookup function has a completely different signature,
fib6_rule_action is split into 2 paths: the existing one is
renamed __fib6_rule_action and a new one for the fib6_info path
is added. fib6_rule_action decides which to call based on the
lookup_ptr. If it is fib6_table_lookup then the new path is taken.

Caller must hold rcu lock as no reference is taken on the returned
fib entry.

Signed-off-by: David Ahern <dsah...@gmail.com>
---
 include/net/ip6_fib.h |  6 ++++
 net/ipv6/fib6_rules.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++--
 net/ipv6/ip6_fib.c    |  7 +++++
 3 files changed, 97 insertions(+), 2 deletions(-)

diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
index 4f7b8f59ea6d..d920dd00139b 100644
--- a/include/net/ip6_fib.h
+++ b/include/net/ip6_fib.h
@@ -376,6 +376,12 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct 
flowi6 *fl6,
                                   const struct sk_buff *skb,
                                   int flags, pol_lookup_t lookup);
 
+/* called with rcu lock held; can return error pointer
+ * caller needs to select path
+ */
+struct fib6_info *fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
+                             int flags);
+
 /* called with rcu lock held; caller needs to select path */
 struct fib6_info *fib6_table_lookup(struct net *net, struct fib6_table *table,
                                    int oif, struct flowi6 *fl6, int strict);
diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c
index d040c4bff3a0..f590446595d8 100644
--- a/net/ipv6/fib6_rules.c
+++ b/net/ipv6/fib6_rules.c
@@ -60,6 +60,39 @@ unsigned int fib6_rules_seq_read(struct net *net)
        return fib_rules_seq_read(net, AF_INET6);
 }
 
+/* called with rcu lock held; no reference taken on fib6_info */
+struct fib6_info *fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
+                             int flags)
+{
+       struct fib6_info *f6i;
+       int err;
+
+       if (net->ipv6.fib6_has_custom_rules) {
+               struct fib_lookup_arg arg = {
+                       .lookup_ptr = fib6_table_lookup,
+                       .lookup_data = &oif,
+                       .flags = FIB_LOOKUP_NOREF,
+               };
+
+               l3mdev_update_flow(net, flowi6_to_flowi(fl6));
+
+               err = fib_rules_lookup(net->ipv6.fib6_rules_ops,
+                                      flowi6_to_flowi(fl6), flags, &arg);
+               if (err)
+                       return ERR_PTR(err);
+
+               f6i = arg.result ? : net->ipv6.fib6_null_entry;
+       } else {
+               f6i = fib6_table_lookup(net, net->ipv6.fib6_local_tbl,
+                                       oif, fl6, flags);
+               if (!f6i || f6i == net->ipv6.fib6_null_entry)
+                       f6i = fib6_table_lookup(net, net->ipv6.fib6_main_tbl,
+                                               oif, fl6, flags);
+       }
+
+       return f6i;
+}
+
 struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
                                   const struct sk_buff *skb,
                                   int flags, pol_lookup_t lookup)
@@ -121,8 +154,48 @@ static int fib6_rule_saddr(struct net *net, struct 
fib_rule *rule, int flags,
        return 0;
 }
 
-static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp,
-                           int flags, struct fib_lookup_arg *arg)
+static int fib6_rule_action_alt(struct fib_rule *rule, struct flowi *flp,
+                               int flags, struct fib_lookup_arg *arg)
+{
+       struct flowi6 *flp6 = &flp->u.ip6;
+       struct net *net = rule->fr_net;
+       struct fib6_table *table;
+       struct fib6_info *f6i;
+       int err = -EAGAIN, *oif;
+       u32 tb_id;
+
+       switch (rule->action) {
+       case FR_ACT_TO_TBL:
+               break;
+       case FR_ACT_UNREACHABLE:
+               return -ENETUNREACH;
+       case FR_ACT_PROHIBIT:
+               return -EACCES;
+       case FR_ACT_BLACKHOLE:
+       default:
+               return -EINVAL;
+       }
+
+       tb_id = fib_rule_get_table(rule, arg);
+       table = fib6_get_table(net, tb_id);
+       if (!table)
+               return -EAGAIN;
+
+       oif = (int *)arg->lookup_data;
+       f6i = fib6_table_lookup(net, table, *oif, flp6, flags);
+       if (f6i != net->ipv6.fib6_null_entry) {
+               err = fib6_rule_saddr(net, rule, flags, flp6,
+                                     fib6_info_nh_dev(f6i));
+
+               if (likely(!err))
+                       arg->result = f6i;
+       }
+
+       return err;
+}
+
+static int __fib6_rule_action(struct fib_rule *rule, struct flowi *flp,
+                             int flags, struct fib_lookup_arg *arg)
 {
        struct flowi6 *flp6 = &flp->u.ip6;
        struct rt6_info *rt = NULL;
@@ -182,6 +255,15 @@ static int fib6_rule_action(struct fib_rule *rule, struct 
flowi *flp,
        return err;
 }
 
+static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp,
+                           int flags, struct fib_lookup_arg *arg)
+{
+       if (arg->lookup_ptr == fib6_table_lookup)
+               return fib6_rule_action_alt(rule, flp, flags, arg);
+
+       return __fib6_rule_action(rule, flp, flags, arg);
+}
+
 static bool fib6_rule_suppress(struct fib_rule *rule, struct fib_lookup_arg 
*arg)
 {
        struct rt6_info *rt = (struct rt6_info *) arg->result;
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 4cfffa0f676e..0b94c0a631cb 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -354,6 +354,13 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct 
flowi6 *fl6,
        return &rt->dst;
 }
 
+/* called with rcu lock held; no reference taken on fib6_info */
+struct fib6_info *fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
+                             int flags)
+{
+       return fib6_table_lookup(net, net->ipv6.fib6_main_tbl, oif, fl6, flags);
+}
+
 static void __net_init fib6_tables_init(struct net *net)
 {
        fib6_link_table(net, net->ipv6.fib6_main_tbl);
-- 
2.11.0

Reply via email to