Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 138118ec authored by David Ahern's avatar David Ahern Committed by Daniel Borkmann
Browse files

net/ipv6: Add fib6_lookup



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: default avatarDavid Ahern <dsahern@gmail.com>
Acked-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parent cc065a9e
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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);
+84 −2
Original line number Diff line number Diff line
@@ -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,7 +154,47 @@ 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,
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;
@@ -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;
+7 −0
Original line number Diff line number Diff line
@@ -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);