|
@@ -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 @@ out:
|
|
|
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;
|