Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: David Ahern <dsahern@gmail.com>
Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>

authored by

David Ahern and committed by
Daniel Borkmann
138118ec cc065a9e

+97 -2
+6
include/net/ip6_fib.h
··· 376 376 const struct sk_buff *skb, 377 377 int flags, pol_lookup_t lookup); 378 378 379 + /* called with rcu lock held; can return error pointer 380 + * caller needs to select path 381 + */ 382 + struct fib6_info *fib6_lookup(struct net *net, int oif, struct flowi6 *fl6, 383 + int flags); 384 + 379 385 /* called with rcu lock held; caller needs to select path */ 380 386 struct fib6_info *fib6_table_lookup(struct net *net, struct fib6_table *table, 381 387 int oif, struct flowi6 *fl6, int strict);
+84 -2
net/ipv6/fib6_rules.c
··· 60 60 return fib_rules_seq_read(net, AF_INET6); 61 61 } 62 62 63 + /* called with rcu lock held; no reference taken on fib6_info */ 64 + struct fib6_info *fib6_lookup(struct net *net, int oif, struct flowi6 *fl6, 65 + int flags) 66 + { 67 + struct fib6_info *f6i; 68 + int err; 69 + 70 + if (net->ipv6.fib6_has_custom_rules) { 71 + struct fib_lookup_arg arg = { 72 + .lookup_ptr = fib6_table_lookup, 73 + .lookup_data = &oif, 74 + .flags = FIB_LOOKUP_NOREF, 75 + }; 76 + 77 + l3mdev_update_flow(net, flowi6_to_flowi(fl6)); 78 + 79 + err = fib_rules_lookup(net->ipv6.fib6_rules_ops, 80 + flowi6_to_flowi(fl6), flags, &arg); 81 + if (err) 82 + return ERR_PTR(err); 83 + 84 + f6i = arg.result ? : net->ipv6.fib6_null_entry; 85 + } else { 86 + f6i = fib6_table_lookup(net, net->ipv6.fib6_local_tbl, 87 + oif, fl6, flags); 88 + if (!f6i || f6i == net->ipv6.fib6_null_entry) 89 + f6i = fib6_table_lookup(net, net->ipv6.fib6_main_tbl, 90 + oif, fl6, flags); 91 + } 92 + 93 + return f6i; 94 + } 95 + 63 96 struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6, 64 97 const struct sk_buff *skb, 65 98 int flags, pol_lookup_t lookup) ··· 154 121 return 0; 155 122 } 156 123 157 - static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp, 158 - int flags, struct fib_lookup_arg *arg) 124 + static int fib6_rule_action_alt(struct fib_rule *rule, struct flowi *flp, 125 + int flags, struct fib_lookup_arg *arg) 126 + { 127 + struct flowi6 *flp6 = &flp->u.ip6; 128 + struct net *net = rule->fr_net; 129 + struct fib6_table *table; 130 + struct fib6_info *f6i; 131 + int err = -EAGAIN, *oif; 132 + u32 tb_id; 133 + 134 + switch (rule->action) { 135 + case FR_ACT_TO_TBL: 136 + break; 137 + case FR_ACT_UNREACHABLE: 138 + return -ENETUNREACH; 139 + case FR_ACT_PROHIBIT: 140 + return -EACCES; 141 + case FR_ACT_BLACKHOLE: 142 + default: 143 + return -EINVAL; 144 + } 145 + 146 + tb_id = fib_rule_get_table(rule, arg); 147 + table = fib6_get_table(net, tb_id); 148 + if (!table) 149 + return -EAGAIN; 150 + 151 + oif = (int *)arg->lookup_data; 152 + f6i = fib6_table_lookup(net, table, *oif, flp6, flags); 153 + if (f6i != net->ipv6.fib6_null_entry) { 154 + err = fib6_rule_saddr(net, rule, flags, flp6, 155 + fib6_info_nh_dev(f6i)); 156 + 157 + if (likely(!err)) 158 + arg->result = f6i; 159 + } 160 + 161 + return err; 162 + } 163 + 164 + static int __fib6_rule_action(struct fib_rule *rule, struct flowi *flp, 165 + int flags, struct fib_lookup_arg *arg) 159 166 { 160 167 struct flowi6 *flp6 = &flp->u.ip6; 161 168 struct rt6_info *rt = NULL; ··· 253 180 out: 254 181 arg->result = rt; 255 182 return err; 183 + } 184 + 185 + static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp, 186 + int flags, struct fib_lookup_arg *arg) 187 + { 188 + if (arg->lookup_ptr == fib6_table_lookup) 189 + return fib6_rule_action_alt(rule, flp, flags, arg); 190 + 191 + return __fib6_rule_action(rule, flp, flags, arg); 256 192 } 257 193 258 194 static bool fib6_rule_suppress(struct fib_rule *rule, struct fib_lookup_arg *arg)
+7
net/ipv6/ip6_fib.c
··· 354 354 return &rt->dst; 355 355 } 356 356 357 + /* called with rcu lock held; no reference taken on fib6_info */ 358 + struct fib6_info *fib6_lookup(struct net *net, int oif, struct flowi6 *fl6, 359 + int flags) 360 + { 361 + return fib6_table_lookup(net, net->ipv6.fib6_main_tbl, oif, fl6, flags); 362 + } 363 + 357 364 static void __net_init fib6_tables_init(struct net *net) 358 365 { 359 366 fib6_link_table(net, net->ipv6.fib6_main_tbl);