diff options
author | David Ahern <dsahern@gmail.com> | 2018-05-09 23:34:23 -0400 |
---|---|---|
committer | Daniel Borkmann <daniel@iogearbox.net> | 2018-05-10 18:10:56 -0400 |
commit | 138118ec96cbfc303c1d7cc05fbb2caf8382c95b (patch) | |
tree | f047e24bb0444ca0f79fdbc802317f34e13533b6 /net/ipv6/fib6_rules.c | |
parent | cc065a9eb96f7f2a29a04ca49331a9ccb1cfcfa2 (diff) |
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>
Diffstat (limited to 'net/ipv6/fib6_rules.c')
-rw-r--r-- | net/ipv6/fib6_rules.c | 86 |
1 files changed, 84 insertions, 2 deletions
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) | |||
60 | return fib_rules_seq_read(net, AF_INET6); | 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 | struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6, | 96 | struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6, |
64 | const struct sk_buff *skb, | 97 | const struct sk_buff *skb, |
65 | int flags, pol_lookup_t lookup) | 98 | int flags, pol_lookup_t lookup) |
@@ -121,8 +154,48 @@ static int fib6_rule_saddr(struct net *net, struct fib_rule *rule, int flags, | |||
121 | return 0; | 154 | return 0; |
122 | } | 155 | } |
123 | 156 | ||
124 | static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp, | 157 | static int fib6_rule_action_alt(struct fib_rule *rule, struct flowi *flp, |
125 | int flags, struct fib_lookup_arg *arg) | 158 | int flags, struct fib_lookup_arg *arg) |
159 | { | ||
160 | struct flowi6 *flp6 = &flp->u.ip6; | ||
161 | struct net *net = rule->fr_net; | ||
162 | struct fib6_table *table; | ||
163 | struct fib6_info *f6i; | ||
164 | int err = -EAGAIN, *oif; | ||
165 | u32 tb_id; | ||
166 | |||
167 | switch (rule->action) { | ||
168 | case FR_ACT_TO_TBL: | ||
169 | break; | ||
170 | case FR_ACT_UNREACHABLE: | ||
171 | return -ENETUNREACH; | ||
172 | case FR_ACT_PROHIBIT: | ||
173 | return -EACCES; | ||
174 | case FR_ACT_BLACKHOLE: | ||
175 | default: | ||
176 | return -EINVAL; | ||
177 | } | ||
178 | |||
179 | tb_id = fib_rule_get_table(rule, arg); | ||
180 | table = fib6_get_table(net, tb_id); | ||
181 | if (!table) | ||
182 | return -EAGAIN; | ||
183 | |||
184 | oif = (int *)arg->lookup_data; | ||
185 | f6i = fib6_table_lookup(net, table, *oif, flp6, flags); | ||
186 | if (f6i != net->ipv6.fib6_null_entry) { | ||
187 | err = fib6_rule_saddr(net, rule, flags, flp6, | ||
188 | fib6_info_nh_dev(f6i)); | ||
189 | |||
190 | if (likely(!err)) | ||
191 | arg->result = f6i; | ||
192 | } | ||
193 | |||
194 | return err; | ||
195 | } | ||
196 | |||
197 | static int __fib6_rule_action(struct fib_rule *rule, struct flowi *flp, | ||
198 | int flags, struct fib_lookup_arg *arg) | ||
126 | { | 199 | { |
127 | struct flowi6 *flp6 = &flp->u.ip6; | 200 | struct flowi6 *flp6 = &flp->u.ip6; |
128 | struct rt6_info *rt = NULL; | 201 | struct rt6_info *rt = NULL; |
@@ -182,6 +255,15 @@ out: | |||
182 | return err; | 255 | return err; |
183 | } | 256 | } |
184 | 257 | ||
258 | static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp, | ||
259 | int flags, struct fib_lookup_arg *arg) | ||
260 | { | ||
261 | if (arg->lookup_ptr == fib6_table_lookup) | ||
262 | return fib6_rule_action_alt(rule, flp, flags, arg); | ||
263 | |||
264 | return __fib6_rule_action(rule, flp, flags, arg); | ||
265 | } | ||
266 | |||
185 | static bool fib6_rule_suppress(struct fib_rule *rule, struct fib_lookup_arg *arg) | 267 | static bool fib6_rule_suppress(struct fib_rule *rule, struct fib_lookup_arg *arg) |
186 | { | 268 | { |
187 | struct rt6_info *rt = (struct rt6_info *) arg->result; | 269 | struct rt6_info *rt = (struct rt6_info *) arg->result; |