mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-15 08:14:15 +08:00
Merge branch 'ipv6-Use-fib6_result-for-fib_lookups'
David Ahern says: ==================== ipv6: Use fib6_result for fib_lookups Add fib6_result as a single data structure to hold results from a fib lookup. IPv6 currently has everything in 1 data structure - a fib6_info, but with nexthop objects the fib6_nh can be in a nexthop or a nexthop can be a blackhole which affects the fib6_type and flags (REJECT). v2 - fixed 2 bugs in patch12: i. checking return from fib6_table_lookup in fib6_lookup ii. call to fib6_rule_saddr in fib6_rule_action_alt should use res->nh ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
cea29a7072
@ -190,6 +190,13 @@ struct rt6_info {
|
||||
unsigned short rt6i_nfheader_len;
|
||||
};
|
||||
|
||||
struct fib6_result {
|
||||
struct fib6_nh *nh;
|
||||
struct fib6_info *f6i;
|
||||
u32 fib6_flags;
|
||||
u8 fib6_type;
|
||||
};
|
||||
|
||||
#define for_each_fib6_node_rt_rcu(fn) \
|
||||
for (rt = rcu_dereference((fn)->leaf); rt; \
|
||||
rt = rcu_dereference(rt->fib6_next))
|
||||
@ -384,18 +391,17 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
|
||||
/* 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);
|
||||
int fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
|
||||
struct fib6_result *res, 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);
|
||||
|
||||
struct fib6_info *fib6_multipath_select(const struct net *net,
|
||||
struct fib6_info *match,
|
||||
struct flowi6 *fl6, int oif,
|
||||
const struct sk_buff *skb, int strict);
|
||||
int fib6_table_lookup(struct net *net, struct fib6_table *table,
|
||||
int oif, struct flowi6 *fl6, struct fib6_result *res,
|
||||
int strict);
|
||||
|
||||
void fib6_select_path(const struct net *net, struct fib6_result *res,
|
||||
struct flowi6 *fl6, int oif, bool have_oif_match,
|
||||
const struct sk_buff *skb, int strict);
|
||||
struct fib6_node *fib6_node_lookup(struct fib6_node *root,
|
||||
const struct in6_addr *daddr,
|
||||
const struct in6_addr *saddr);
|
||||
|
@ -302,8 +302,9 @@ static inline unsigned int ip6_dst_mtu_forward(const struct dst_entry *dst)
|
||||
return mtu;
|
||||
}
|
||||
|
||||
u32 ip6_mtu_from_fib6(struct fib6_info *f6i, struct in6_addr *daddr,
|
||||
struct in6_addr *saddr);
|
||||
u32 ip6_mtu_from_fib6(const struct fib6_result *res,
|
||||
const struct in6_addr *daddr,
|
||||
const struct in6_addr *saddr);
|
||||
|
||||
struct neighbour *ip6_neigh_lookup(const struct in6_addr *gw,
|
||||
struct net_device *dev, struct sk_buff *skb,
|
||||
|
@ -14,6 +14,7 @@
|
||||
struct fib6_info;
|
||||
struct fib6_nh;
|
||||
struct fib6_config;
|
||||
struct fib6_result;
|
||||
|
||||
/* This is ugly, ideally these symbols should be built
|
||||
* into the core kernel.
|
||||
@ -28,19 +29,17 @@ struct ipv6_stub {
|
||||
int (*ipv6_route_input)(struct sk_buff *skb);
|
||||
|
||||
struct fib6_table *(*fib6_get_table)(struct net *net, u32 id);
|
||||
struct fib6_info *(*fib6_lookup)(struct net *net, int oif,
|
||||
struct flowi6 *fl6, int flags);
|
||||
struct fib6_info *(*fib6_table_lookup)(struct net *net,
|
||||
struct fib6_table *table,
|
||||
int oif, struct flowi6 *fl6,
|
||||
int flags);
|
||||
struct fib6_info *(*fib6_multipath_select)(const struct net *net,
|
||||
struct fib6_info *f6i,
|
||||
struct flowi6 *fl6, int oif,
|
||||
const struct sk_buff *skb,
|
||||
int strict);
|
||||
u32 (*ip6_mtu_from_fib6)(struct fib6_info *f6i, struct in6_addr *daddr,
|
||||
struct in6_addr *saddr);
|
||||
int (*fib6_lookup)(struct net *net, int oif, struct flowi6 *fl6,
|
||||
struct fib6_result *res, int flags);
|
||||
int (*fib6_table_lookup)(struct net *net, struct fib6_table *table,
|
||||
int oif, struct flowi6 *fl6,
|
||||
struct fib6_result *res, int flags);
|
||||
void (*fib6_select_path)(const struct net *net, struct fib6_result *res,
|
||||
struct flowi6 *fl6, int oif, bool oif_match,
|
||||
const struct sk_buff *skb, int strict);
|
||||
u32 (*ip6_mtu_from_fib6)(const struct fib6_result *res,
|
||||
const struct in6_addr *daddr,
|
||||
const struct in6_addr *saddr);
|
||||
|
||||
int (*fib6_nh_init)(struct net *net, struct fib6_nh *fib6_nh,
|
||||
struct fib6_config *cfg, gfp_t gfp_flags,
|
||||
|
@ -12,10 +12,10 @@
|
||||
|
||||
TRACE_EVENT(fib6_table_lookup,
|
||||
|
||||
TP_PROTO(const struct net *net, const struct fib6_info *f6i,
|
||||
TP_PROTO(const struct net *net, const struct fib6_result *res,
|
||||
struct fib6_table *table, const struct flowi6 *flp),
|
||||
|
||||
TP_ARGS(net, f6i, table, flp),
|
||||
TP_ARGS(net, res, table, flp),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( u32, tb_id )
|
||||
@ -39,7 +39,7 @@ TRACE_EVENT(fib6_table_lookup,
|
||||
struct in6_addr *in6;
|
||||
|
||||
__entry->tb_id = table->tb6_id;
|
||||
__entry->err = ip6_rt_type_to_error(f6i->fib6_type);
|
||||
__entry->err = ip6_rt_type_to_error(res->fib6_type);
|
||||
__entry->oif = flp->flowi6_oif;
|
||||
__entry->iif = flp->flowi6_iif;
|
||||
__entry->tos = ip6_tclass(flp->flowlabel);
|
||||
@ -62,20 +62,20 @@ TRACE_EVENT(fib6_table_lookup,
|
||||
__entry->dport = 0;
|
||||
}
|
||||
|
||||
if (f6i->fib6_nh.fib_nh_dev) {
|
||||
__assign_str(name, f6i->fib6_nh.fib_nh_dev);
|
||||
if (res->nh && res->nh->fib_nh_dev) {
|
||||
__assign_str(name, res->nh->fib_nh_dev);
|
||||
} else {
|
||||
__assign_str(name, "-");
|
||||
}
|
||||
if (f6i == net->ipv6.fib6_null_entry) {
|
||||
if (res->f6i == net->ipv6.fib6_null_entry) {
|
||||
struct in6_addr in6_zero = {};
|
||||
|
||||
in6 = (struct in6_addr *)__entry->gw;
|
||||
*in6 = in6_zero;
|
||||
|
||||
} else if (f6i) {
|
||||
} else if (res->nh) {
|
||||
in6 = (struct in6_addr *)__entry->gw;
|
||||
*in6 = f6i->fib6_nh.fib_nh_gw6;
|
||||
*in6 = res->nh->fib_nh_gw6;
|
||||
}
|
||||
),
|
||||
|
||||
|
@ -4679,12 +4679,12 @@ static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
|
||||
struct in6_addr *src = (struct in6_addr *) params->ipv6_src;
|
||||
struct in6_addr *dst = (struct in6_addr *) params->ipv6_dst;
|
||||
struct neighbour *neigh;
|
||||
struct fib6_result res;
|
||||
struct net_device *dev;
|
||||
struct inet6_dev *idev;
|
||||
struct fib6_info *f6i;
|
||||
struct flowi6 fl6;
|
||||
int strict = 0;
|
||||
int oif;
|
||||
int oif, err;
|
||||
u32 mtu;
|
||||
|
||||
/* link local addresses are never forwarded */
|
||||
@ -4726,54 +4726,52 @@ static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
|
||||
if (unlikely(!tb))
|
||||
return BPF_FIB_LKUP_RET_NOT_FWDED;
|
||||
|
||||
f6i = ipv6_stub->fib6_table_lookup(net, tb, oif, &fl6, strict);
|
||||
err = ipv6_stub->fib6_table_lookup(net, tb, oif, &fl6, &res,
|
||||
strict);
|
||||
} else {
|
||||
fl6.flowi6_mark = 0;
|
||||
fl6.flowi6_secid = 0;
|
||||
fl6.flowi6_tun_key.tun_id = 0;
|
||||
fl6.flowi6_uid = sock_net_uid(net, NULL);
|
||||
|
||||
f6i = ipv6_stub->fib6_lookup(net, oif, &fl6, strict);
|
||||
err = ipv6_stub->fib6_lookup(net, oif, &fl6, &res, strict);
|
||||
}
|
||||
|
||||
if (unlikely(IS_ERR_OR_NULL(f6i) || f6i == net->ipv6.fib6_null_entry))
|
||||
if (unlikely(err || IS_ERR_OR_NULL(res.f6i) ||
|
||||
res.f6i == net->ipv6.fib6_null_entry))
|
||||
return BPF_FIB_LKUP_RET_NOT_FWDED;
|
||||
|
||||
if (unlikely(f6i->fib6_flags & RTF_REJECT)) {
|
||||
switch (f6i->fib6_type) {
|
||||
case RTN_BLACKHOLE:
|
||||
return BPF_FIB_LKUP_RET_BLACKHOLE;
|
||||
case RTN_UNREACHABLE:
|
||||
return BPF_FIB_LKUP_RET_UNREACHABLE;
|
||||
case RTN_PROHIBIT:
|
||||
return BPF_FIB_LKUP_RET_PROHIBIT;
|
||||
default:
|
||||
return BPF_FIB_LKUP_RET_NOT_FWDED;
|
||||
}
|
||||
switch (res.fib6_type) {
|
||||
/* only unicast is forwarded */
|
||||
case RTN_UNICAST:
|
||||
break;
|
||||
case RTN_BLACKHOLE:
|
||||
return BPF_FIB_LKUP_RET_BLACKHOLE;
|
||||
case RTN_UNREACHABLE:
|
||||
return BPF_FIB_LKUP_RET_UNREACHABLE;
|
||||
case RTN_PROHIBIT:
|
||||
return BPF_FIB_LKUP_RET_PROHIBIT;
|
||||
default:
|
||||
return BPF_FIB_LKUP_RET_NOT_FWDED;
|
||||
}
|
||||
|
||||
if (f6i->fib6_type != RTN_UNICAST)
|
||||
return BPF_FIB_LKUP_RET_NOT_FWDED;
|
||||
|
||||
if (f6i->fib6_nsiblings && fl6.flowi6_oif == 0)
|
||||
f6i = ipv6_stub->fib6_multipath_select(net, f6i, &fl6,
|
||||
fl6.flowi6_oif, NULL,
|
||||
strict);
|
||||
ipv6_stub->fib6_select_path(net, &res, &fl6, fl6.flowi6_oif,
|
||||
fl6.flowi6_oif != 0, NULL, strict);
|
||||
|
||||
if (check_mtu) {
|
||||
mtu = ipv6_stub->ip6_mtu_from_fib6(f6i, dst, src);
|
||||
mtu = ipv6_stub->ip6_mtu_from_fib6(&res, dst, src);
|
||||
if (params->tot_len > mtu)
|
||||
return BPF_FIB_LKUP_RET_FRAG_NEEDED;
|
||||
}
|
||||
|
||||
if (f6i->fib6_nh.fib_nh_lws)
|
||||
if (res.nh->fib_nh_lws)
|
||||
return BPF_FIB_LKUP_RET_UNSUPP_LWT;
|
||||
|
||||
if (f6i->fib6_nh.fib_nh_gw_family)
|
||||
*dst = f6i->fib6_nh.fib_nh_gw6;
|
||||
if (res.nh->fib_nh_gw_family)
|
||||
*dst = res.nh->fib_nh_gw6;
|
||||
|
||||
dev = f6i->fib6_nh.fib_nh_dev;
|
||||
params->rt_metric = f6i->fib6_metric;
|
||||
dev = res.nh->fib_nh_dev;
|
||||
params->rt_metric = res.f6i->fib6_metric;
|
||||
|
||||
/* xdp and cls_bpf programs are run in RCU-bh so rcu_read_lock_bh is
|
||||
* not needed here.
|
||||
|
@ -144,31 +144,32 @@ static struct fib6_table *eafnosupport_fib6_get_table(struct net *net, u32 id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct fib6_info *
|
||||
static int
|
||||
eafnosupport_fib6_table_lookup(struct net *net, struct fib6_table *table,
|
||||
int oif, struct flowi6 *fl6, int flags)
|
||||
int oif, struct flowi6 *fl6,
|
||||
struct fib6_result *res, int flags)
|
||||
{
|
||||
return NULL;
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
static struct fib6_info *
|
||||
static int
|
||||
eafnosupport_fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
|
||||
int flags)
|
||||
struct fib6_result *res, int flags)
|
||||
{
|
||||
return NULL;
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
static struct fib6_info *
|
||||
eafnosupport_fib6_multipath_select(const struct net *net, struct fib6_info *f6i,
|
||||
struct flowi6 *fl6, int oif,
|
||||
const struct sk_buff *skb, int strict)
|
||||
static void
|
||||
eafnosupport_fib6_select_path(const struct net *net, struct fib6_result *res,
|
||||
struct flowi6 *fl6, int oif, bool have_oif_match,
|
||||
const struct sk_buff *skb, int strict)
|
||||
{
|
||||
return f6i;
|
||||
}
|
||||
|
||||
static u32
|
||||
eafnosupport_ip6_mtu_from_fib6(struct fib6_info *f6i, struct in6_addr *daddr,
|
||||
struct in6_addr *saddr)
|
||||
eafnosupport_ip6_mtu_from_fib6(const struct fib6_result *res,
|
||||
const struct in6_addr *daddr,
|
||||
const struct in6_addr *saddr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -187,7 +188,7 @@ const struct ipv6_stub *ipv6_stub __read_mostly = &(struct ipv6_stub) {
|
||||
.fib6_get_table = eafnosupport_fib6_get_table,
|
||||
.fib6_table_lookup = eafnosupport_fib6_table_lookup,
|
||||
.fib6_lookup = eafnosupport_fib6_lookup,
|
||||
.fib6_multipath_select = eafnosupport_fib6_multipath_select,
|
||||
.fib6_select_path = eafnosupport_fib6_select_path,
|
||||
.ip6_mtu_from_fib6 = eafnosupport_ip6_mtu_from_fib6,
|
||||
.fib6_nh_init = eafnosupport_fib6_nh_init,
|
||||
};
|
||||
|
@ -917,7 +917,7 @@ static const struct ipv6_stub ipv6_stub_impl = {
|
||||
.fib6_get_table = fib6_get_table,
|
||||
.fib6_table_lookup = fib6_table_lookup,
|
||||
.fib6_lookup = fib6_lookup,
|
||||
.fib6_multipath_select = fib6_multipath_select,
|
||||
.fib6_select_path = fib6_select_path,
|
||||
.ip6_mtu_from_fib6 = ip6_mtu_from_fib6,
|
||||
.fib6_nh_init = fib6_nh_init,
|
||||
.fib6_nh_release = fib6_nh_release,
|
||||
|
@ -61,16 +61,16 @@ unsigned int fib6_rules_seq_read(struct net *net)
|
||||
}
|
||||
|
||||
/* 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)
|
||||
int fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
|
||||
struct fib6_result *res, 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,
|
||||
.result = res,
|
||||
.flags = FIB_LOOKUP_NOREF,
|
||||
};
|
||||
|
||||
@ -78,19 +78,15 @@ struct fib6_info *fib6_lookup(struct net *net, int oif, struct flowi6 *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);
|
||||
err = fib6_table_lookup(net, net->ipv6.fib6_local_tbl, oif,
|
||||
fl6, res, flags);
|
||||
if (err || res->f6i == net->ipv6.fib6_null_entry)
|
||||
err = fib6_table_lookup(net, net->ipv6.fib6_main_tbl,
|
||||
oif, fl6, res, flags);
|
||||
}
|
||||
|
||||
return f6i;
|
||||
return err;
|
||||
}
|
||||
|
||||
struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
|
||||
@ -157,10 +153,10 @@ static int fib6_rule_saddr(struct net *net, struct fib_rule *rule, int flags,
|
||||
static int fib6_rule_action_alt(struct fib_rule *rule, struct flowi *flp,
|
||||
int flags, struct fib_lookup_arg *arg)
|
||||
{
|
||||
struct fib6_result *res = arg->result;
|
||||
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;
|
||||
|
||||
@ -182,14 +178,10 @@ static int fib6_rule_action_alt(struct fib_rule *rule, struct flowi *flp,
|
||||
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_table_lookup(net, table, *oif, flp6, res, flags);
|
||||
if (!err && res->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;
|
||||
}
|
||||
res->nh->fib_nh_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -354,10 +354,11 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
|
||||
}
|
||||
|
||||
/* 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)
|
||||
int fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
|
||||
struct fib6_result *res, int flags)
|
||||
{
|
||||
return fib6_table_lookup(net, net->ipv6.fib6_main_tbl, oif, fl6, flags);
|
||||
return fib6_table_lookup(net, net->ipv6.fib6_main_tbl, oif, fl6,
|
||||
res, flags);
|
||||
}
|
||||
|
||||
static void __net_init fib6_tables_init(struct net *net)
|
||||
|
447
net/ipv6/route.c
447
net/ipv6/route.c
@ -110,7 +110,7 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
|
||||
struct in6_addr *dest, struct in6_addr *src,
|
||||
int iif, int type, u32 portid, u32 seq,
|
||||
unsigned int flags);
|
||||
static struct rt6_info *rt6_find_cached_rt(struct fib6_info *rt,
|
||||
static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
|
||||
struct in6_addr *daddr,
|
||||
struct in6_addr *saddr);
|
||||
|
||||
@ -428,13 +428,15 @@ static bool rt6_check_expired(const struct rt6_info *rt)
|
||||
return false;
|
||||
}
|
||||
|
||||
struct fib6_info *fib6_multipath_select(const struct net *net,
|
||||
struct fib6_info *match,
|
||||
struct flowi6 *fl6, int oif,
|
||||
const struct sk_buff *skb,
|
||||
int strict)
|
||||
void fib6_select_path(const struct net *net, struct fib6_result *res,
|
||||
struct flowi6 *fl6, int oif, bool have_oif_match,
|
||||
const struct sk_buff *skb, int strict)
|
||||
{
|
||||
struct fib6_info *sibling, *next_sibling;
|
||||
struct fib6_info *match = res->f6i;
|
||||
|
||||
if (!match->fib6_nsiblings || have_oif_match)
|
||||
goto out;
|
||||
|
||||
/* We might have already computed the hash for ICMPv6 errors. In such
|
||||
* case it will always be non-zero. Otherwise now is the time to do it.
|
||||
@ -443,7 +445,7 @@ struct fib6_info *fib6_multipath_select(const struct net *net,
|
||||
fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL);
|
||||
|
||||
if (fl6->mp_hash <= atomic_read(&match->fib6_nh.fib_nh_upper_bound))
|
||||
return match;
|
||||
goto out;
|
||||
|
||||
list_for_each_entry_safe(sibling, next_sibling, &match->fib6_siblings,
|
||||
fib6_siblings) {
|
||||
@ -459,7 +461,9 @@ struct fib6_info *fib6_multipath_select(const struct net *net,
|
||||
break;
|
||||
}
|
||||
|
||||
return match;
|
||||
out:
|
||||
res->f6i = match;
|
||||
res->nh = &match->fib6_nh;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -487,29 +491,42 @@ static bool __rt6_device_match(struct net *net, const struct fib6_nh *nh,
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct fib6_info *rt6_device_match(struct net *net,
|
||||
struct fib6_info *rt,
|
||||
const struct in6_addr *saddr,
|
||||
int oif,
|
||||
int flags)
|
||||
static void rt6_device_match(struct net *net, struct fib6_result *res,
|
||||
const struct in6_addr *saddr, int oif, int flags)
|
||||
{
|
||||
const struct fib6_nh *nh;
|
||||
struct fib6_info *sprt;
|
||||
struct fib6_info *f6i = res->f6i;
|
||||
struct fib6_info *spf6i;
|
||||
struct fib6_nh *nh;
|
||||
|
||||
if (!oif && ipv6_addr_any(saddr) &&
|
||||
!(rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD))
|
||||
return rt;
|
||||
|
||||
for (sprt = rt; sprt; sprt = rcu_dereference(sprt->fib6_next)) {
|
||||
nh = &sprt->fib6_nh;
|
||||
if (__rt6_device_match(net, nh, saddr, oif, flags))
|
||||
return sprt;
|
||||
if (!oif && ipv6_addr_any(saddr)) {
|
||||
nh = &f6i->fib6_nh;
|
||||
if (!(nh->fib_nh_flags & RTNH_F_DEAD))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (oif && flags & RT6_LOOKUP_F_IFACE)
|
||||
return net->ipv6.fib6_null_entry;
|
||||
for (spf6i = f6i; spf6i; spf6i = rcu_dereference(spf6i->fib6_next)) {
|
||||
nh = &spf6i->fib6_nh;
|
||||
if (__rt6_device_match(net, nh, saddr, oif, flags)) {
|
||||
res->f6i = spf6i;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
return rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD ? net->ipv6.fib6_null_entry : rt;
|
||||
if (oif && flags & RT6_LOOKUP_F_IFACE) {
|
||||
res->f6i = net->ipv6.fib6_null_entry;
|
||||
nh = &res->f6i->fib6_nh;
|
||||
goto out;
|
||||
}
|
||||
|
||||
nh = &f6i->fib6_nh;
|
||||
if (nh->fib_nh_flags & RTNH_F_DEAD) {
|
||||
res->f6i = net->ipv6.fib6_null_entry;
|
||||
nh = &res->f6i->fib6_nh;
|
||||
}
|
||||
out:
|
||||
res->nh = nh;
|
||||
res->fib6_type = res->f6i->fib6_type;
|
||||
res->fib6_flags = res->f6i->fib6_flags;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IPV6_ROUTER_PREF
|
||||
@ -680,66 +697,70 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __find_rr_leaf(struct fib6_info *rt_start,
|
||||
static void __find_rr_leaf(struct fib6_info *f6i_start,
|
||||
struct fib6_info *nomatch, u32 metric,
|
||||
struct fib6_info **match, struct fib6_info **cont,
|
||||
struct fib6_result *res, struct fib6_info **cont,
|
||||
int oif, int strict, bool *do_rr, int *mpri)
|
||||
{
|
||||
struct fib6_info *rt;
|
||||
struct fib6_info *f6i;
|
||||
|
||||
for (rt = rt_start;
|
||||
rt && rt != nomatch;
|
||||
rt = rcu_dereference(rt->fib6_next)) {
|
||||
for (f6i = f6i_start;
|
||||
f6i && f6i != nomatch;
|
||||
f6i = rcu_dereference(f6i->fib6_next)) {
|
||||
struct fib6_nh *nh;
|
||||
|
||||
if (cont && rt->fib6_metric != metric) {
|
||||
*cont = rt;
|
||||
if (cont && f6i->fib6_metric != metric) {
|
||||
*cont = f6i;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fib6_check_expired(rt))
|
||||
if (fib6_check_expired(f6i))
|
||||
continue;
|
||||
|
||||
nh = &rt->fib6_nh;
|
||||
if (find_match(nh, rt->fib6_flags, oif, strict, mpri, do_rr))
|
||||
*match = rt;
|
||||
nh = &f6i->fib6_nh;
|
||||
if (find_match(nh, f6i->fib6_flags, oif, strict, mpri, do_rr)) {
|
||||
res->f6i = f6i;
|
||||
res->nh = nh;
|
||||
res->fib6_flags = f6i->fib6_flags;
|
||||
res->fib6_type = f6i->fib6_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct fib6_info *find_rr_leaf(struct fib6_node *fn,
|
||||
struct fib6_info *leaf,
|
||||
struct fib6_info *rr_head,
|
||||
u32 metric, int oif, int strict,
|
||||
bool *do_rr)
|
||||
static void find_rr_leaf(struct fib6_node *fn, struct fib6_info *leaf,
|
||||
struct fib6_info *rr_head, int oif, int strict,
|
||||
bool *do_rr, struct fib6_result *res)
|
||||
{
|
||||
struct fib6_info *match = NULL, *cont = NULL;
|
||||
u32 metric = rr_head->fib6_metric;
|
||||
struct fib6_info *cont = NULL;
|
||||
int mpri = -1;
|
||||
|
||||
__find_rr_leaf(rr_head, NULL, metric, &match, &cont,
|
||||
__find_rr_leaf(rr_head, NULL, metric, res, &cont,
|
||||
oif, strict, do_rr, &mpri);
|
||||
|
||||
__find_rr_leaf(leaf, rr_head, metric, &match, &cont,
|
||||
__find_rr_leaf(leaf, rr_head, metric, res, &cont,
|
||||
oif, strict, do_rr, &mpri);
|
||||
|
||||
if (match || !cont)
|
||||
return match;
|
||||
if (res->f6i || !cont)
|
||||
return;
|
||||
|
||||
__find_rr_leaf(cont, NULL, metric, &match, NULL,
|
||||
__find_rr_leaf(cont, NULL, metric, res, NULL,
|
||||
oif, strict, do_rr, &mpri);
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
static struct fib6_info *rt6_select(struct net *net, struct fib6_node *fn,
|
||||
int oif, int strict)
|
||||
static void rt6_select(struct net *net, struct fib6_node *fn, int oif,
|
||||
struct fib6_result *res, int strict)
|
||||
{
|
||||
struct fib6_info *leaf = rcu_dereference(fn->leaf);
|
||||
struct fib6_info *match, *rt0;
|
||||
struct fib6_info *rt0;
|
||||
bool do_rr = false;
|
||||
int key_plen;
|
||||
|
||||
/* make sure this function or its helpers sets f6i */
|
||||
res->f6i = NULL;
|
||||
|
||||
if (!leaf || leaf == net->ipv6.fib6_null_entry)
|
||||
return net->ipv6.fib6_null_entry;
|
||||
goto out;
|
||||
|
||||
rt0 = rcu_dereference(fn->rr_ptr);
|
||||
if (!rt0)
|
||||
@ -756,11 +777,9 @@ static struct fib6_info *rt6_select(struct net *net, struct fib6_node *fn,
|
||||
key_plen = rt0->fib6_src.plen;
|
||||
#endif
|
||||
if (fn->fn_bit != key_plen)
|
||||
return net->ipv6.fib6_null_entry;
|
||||
|
||||
match = find_rr_leaf(fn, leaf, rt0, rt0->fib6_metric, oif, strict,
|
||||
&do_rr);
|
||||
goto out;
|
||||
|
||||
find_rr_leaf(fn, leaf, rt0, oif, strict, &do_rr, res);
|
||||
if (do_rr) {
|
||||
struct fib6_info *next = rcu_dereference(rt0->fib6_next);
|
||||
|
||||
@ -777,12 +796,19 @@ static struct fib6_info *rt6_select(struct net *net, struct fib6_node *fn,
|
||||
}
|
||||
}
|
||||
|
||||
return match ? match : net->ipv6.fib6_null_entry;
|
||||
out:
|
||||
if (!res->f6i) {
|
||||
res->f6i = net->ipv6.fib6_null_entry;
|
||||
res->nh = &res->f6i->fib6_nh;
|
||||
res->fib6_flags = res->f6i->fib6_flags;
|
||||
res->fib6_type = res->f6i->fib6_type;
|
||||
}
|
||||
}
|
||||
|
||||
static bool rt6_is_gw_or_nonexthop(const struct fib6_info *rt)
|
||||
static bool rt6_is_gw_or_nonexthop(const struct fib6_result *res)
|
||||
{
|
||||
return (rt->fib6_flags & RTF_NONEXTHOP) || rt->fib6_nh.fib_nh_gw_family;
|
||||
return (res->f6i->fib6_flags & RTF_NONEXTHOP) ||
|
||||
res->nh->fib_nh_gw_family;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IPV6_ROUTE_INFO
|
||||
@ -866,17 +892,17 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
|
||||
*/
|
||||
|
||||
/* called with rcu_lock held */
|
||||
static struct net_device *ip6_rt_get_dev_rcu(struct fib6_info *rt)
|
||||
static struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res)
|
||||
{
|
||||
struct net_device *dev = rt->fib6_nh.fib_nh_dev;
|
||||
struct net_device *dev = res->nh->fib_nh_dev;
|
||||
|
||||
if (rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) {
|
||||
if (res->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) {
|
||||
/* for copies of local routes, dst->dev needs to be the
|
||||
* device if it is a master device, the master device if
|
||||
* device is enslaved, and the loopback as the default
|
||||
*/
|
||||
if (netif_is_l3_slave(dev) &&
|
||||
!rt6_need_strict(&rt->fib6_dst.addr))
|
||||
!rt6_need_strict(&res->f6i->fib6_dst.addr))
|
||||
dev = l3mdev_master_dev_rcu(dev);
|
||||
else if (!netif_is_l3_master(dev))
|
||||
dev = dev_net(dev)->loopback_dev;
|
||||
@ -922,11 +948,11 @@ static unsigned short fib6_info_dst_flags(struct fib6_info *rt)
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void ip6_rt_init_dst_reject(struct rt6_info *rt, struct fib6_info *ort)
|
||||
static void ip6_rt_init_dst_reject(struct rt6_info *rt, u8 fib6_type)
|
||||
{
|
||||
rt->dst.error = ip6_rt_type_to_error(ort->fib6_type);
|
||||
rt->dst.error = ip6_rt_type_to_error(fib6_type);
|
||||
|
||||
switch (ort->fib6_type) {
|
||||
switch (fib6_type) {
|
||||
case RTN_BLACKHOLE:
|
||||
rt->dst.output = dst_discard_out;
|
||||
rt->dst.input = dst_discard;
|
||||
@ -944,26 +970,28 @@ static void ip6_rt_init_dst_reject(struct rt6_info *rt, struct fib6_info *ort)
|
||||
}
|
||||
}
|
||||
|
||||
static void ip6_rt_init_dst(struct rt6_info *rt, struct fib6_info *ort)
|
||||
static void ip6_rt_init_dst(struct rt6_info *rt, const struct fib6_result *res)
|
||||
{
|
||||
if (ort->fib6_flags & RTF_REJECT) {
|
||||
ip6_rt_init_dst_reject(rt, ort);
|
||||
struct fib6_info *f6i = res->f6i;
|
||||
|
||||
if (res->fib6_flags & RTF_REJECT) {
|
||||
ip6_rt_init_dst_reject(rt, res->fib6_type);
|
||||
return;
|
||||
}
|
||||
|
||||
rt->dst.error = 0;
|
||||
rt->dst.output = ip6_output;
|
||||
|
||||
if (ort->fib6_type == RTN_LOCAL || ort->fib6_type == RTN_ANYCAST) {
|
||||
if (res->fib6_type == RTN_LOCAL || res->fib6_type == RTN_ANYCAST) {
|
||||
rt->dst.input = ip6_input;
|
||||
} else if (ipv6_addr_type(&ort->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
|
||||
} else if (ipv6_addr_type(&f6i->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
|
||||
rt->dst.input = ip6_mc_input;
|
||||
} else {
|
||||
rt->dst.input = ip6_forward;
|
||||
}
|
||||
|
||||
if (ort->fib6_nh.fib_nh_lws) {
|
||||
rt->dst.lwtstate = lwtstate_get(ort->fib6_nh.fib_nh_lws);
|
||||
if (res->nh->fib_nh_lws) {
|
||||
rt->dst.lwtstate = lwtstate_get(res->nh->fib_nh_lws);
|
||||
lwtunnel_set_redirect(&rt->dst);
|
||||
}
|
||||
|
||||
@ -978,23 +1006,25 @@ static void rt6_set_from(struct rt6_info *rt, struct fib6_info *from)
|
||||
ip_dst_init_metrics(&rt->dst, from->fib6_metrics);
|
||||
}
|
||||
|
||||
/* Caller must already hold reference to @ort */
|
||||
static void ip6_rt_copy_init(struct rt6_info *rt, struct fib6_info *ort)
|
||||
/* Caller must already hold reference to f6i in result */
|
||||
static void ip6_rt_copy_init(struct rt6_info *rt, const struct fib6_result *res)
|
||||
{
|
||||
struct net_device *dev = fib6_info_nh_dev(ort);
|
||||
const struct fib6_nh *nh = res->nh;
|
||||
const struct net_device *dev = nh->fib_nh_dev;
|
||||
struct fib6_info *f6i = res->f6i;
|
||||
|
||||
ip6_rt_init_dst(rt, ort);
|
||||
ip6_rt_init_dst(rt, res);
|
||||
|
||||
rt->rt6i_dst = ort->fib6_dst;
|
||||
rt->rt6i_dst = f6i->fib6_dst;
|
||||
rt->rt6i_idev = dev ? in6_dev_get(dev) : NULL;
|
||||
rt->rt6i_flags = ort->fib6_flags;
|
||||
if (ort->fib6_nh.fib_nh_gw_family) {
|
||||
rt->rt6i_gateway = ort->fib6_nh.fib_nh_gw6;
|
||||
rt->rt6i_flags = res->fib6_flags;
|
||||
if (nh->fib_nh_gw_family) {
|
||||
rt->rt6i_gateway = nh->fib_nh_gw6;
|
||||
rt->rt6i_flags |= RTF_GATEWAY;
|
||||
}
|
||||
rt6_set_from(rt, ort);
|
||||
rt6_set_from(rt, f6i);
|
||||
#ifdef CONFIG_IPV6_SUBTREES
|
||||
rt->rt6i_src = ort->fib6_src;
|
||||
rt->rt6i_src = f6i->fib6_src;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1033,22 +1063,24 @@ static bool ip6_hold_safe(struct net *net, struct rt6_info **prt)
|
||||
}
|
||||
|
||||
/* called with rcu_lock held */
|
||||
static struct rt6_info *ip6_create_rt_rcu(struct fib6_info *rt)
|
||||
static struct rt6_info *ip6_create_rt_rcu(const struct fib6_result *res)
|
||||
{
|
||||
unsigned short flags = fib6_info_dst_flags(rt);
|
||||
struct net_device *dev = rt->fib6_nh.fib_nh_dev;
|
||||
struct net_device *dev = res->nh->fib_nh_dev;
|
||||
struct fib6_info *f6i = res->f6i;
|
||||
unsigned short flags;
|
||||
struct rt6_info *nrt;
|
||||
|
||||
if (!fib6_info_hold_safe(rt))
|
||||
if (!fib6_info_hold_safe(f6i))
|
||||
goto fallback;
|
||||
|
||||
flags = fib6_info_dst_flags(f6i);
|
||||
nrt = ip6_dst_alloc(dev_net(dev), dev, flags);
|
||||
if (!nrt) {
|
||||
fib6_info_release(rt);
|
||||
fib6_info_release(f6i);
|
||||
goto fallback;
|
||||
}
|
||||
|
||||
ip6_rt_copy_init(nrt, rt);
|
||||
ip6_rt_copy_init(nrt, res);
|
||||
return nrt;
|
||||
|
||||
fallback:
|
||||
@ -1063,7 +1095,7 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net,
|
||||
const struct sk_buff *skb,
|
||||
int flags)
|
||||
{
|
||||
struct fib6_info *f6i;
|
||||
struct fib6_result res = {};
|
||||
struct fib6_node *fn;
|
||||
struct rt6_info *rt;
|
||||
|
||||
@ -1073,14 +1105,14 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net,
|
||||
rcu_read_lock();
|
||||
fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
|
||||
restart:
|
||||
f6i = rcu_dereference(fn->leaf);
|
||||
if (!f6i)
|
||||
f6i = net->ipv6.fib6_null_entry;
|
||||
res.f6i = rcu_dereference(fn->leaf);
|
||||
if (!res.f6i)
|
||||
res.f6i = net->ipv6.fib6_null_entry;
|
||||
else
|
||||
f6i = rt6_device_match(net, f6i, &fl6->saddr,
|
||||
fl6->flowi6_oif, flags);
|
||||
rt6_device_match(net, &res, &fl6->saddr, fl6->flowi6_oif,
|
||||
flags);
|
||||
|
||||
if (f6i == net->ipv6.fib6_null_entry) {
|
||||
if (res.f6i == net->ipv6.fib6_null_entry) {
|
||||
fn = fib6_backtrack(fn, &fl6->saddr);
|
||||
if (fn)
|
||||
goto restart;
|
||||
@ -1090,20 +1122,20 @@ restart:
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (f6i->fib6_nsiblings && fl6->flowi6_oif == 0)
|
||||
f6i = fib6_multipath_select(net, f6i, fl6, fl6->flowi6_oif, skb,
|
||||
flags);
|
||||
fib6_select_path(net, &res, fl6, fl6->flowi6_oif,
|
||||
fl6->flowi6_oif != 0, skb, flags);
|
||||
|
||||
/* Search through exception table */
|
||||
rt = rt6_find_cached_rt(f6i, &fl6->daddr, &fl6->saddr);
|
||||
rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
|
||||
if (rt) {
|
||||
if (ip6_hold_safe(net, &rt))
|
||||
dst_use_noref(&rt->dst, jiffies);
|
||||
} else {
|
||||
rt = ip6_create_rt_rcu(f6i);
|
||||
rt = ip6_create_rt_rcu(&res);
|
||||
}
|
||||
|
||||
out:
|
||||
trace_fib6_table_lookup(net, f6i, table, fl6);
|
||||
trace_fib6_table_lookup(net, &res, table, fl6);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
@ -1170,10 +1202,11 @@ int ip6_ins_rt(struct net *net, struct fib6_info *rt)
|
||||
return __ip6_ins_rt(rt, &info, NULL);
|
||||
}
|
||||
|
||||
static struct rt6_info *ip6_rt_cache_alloc(struct fib6_info *ort,
|
||||
static struct rt6_info *ip6_rt_cache_alloc(const struct fib6_result *res,
|
||||
const struct in6_addr *daddr,
|
||||
const struct in6_addr *saddr)
|
||||
{
|
||||
struct fib6_info *f6i = res->f6i;
|
||||
struct net_device *dev;
|
||||
struct rt6_info *rt;
|
||||
|
||||
@ -1181,25 +1214,25 @@ static struct rt6_info *ip6_rt_cache_alloc(struct fib6_info *ort,
|
||||
* Clone the route.
|
||||
*/
|
||||
|
||||
if (!fib6_info_hold_safe(ort))
|
||||
if (!fib6_info_hold_safe(f6i))
|
||||
return NULL;
|
||||
|
||||
dev = ip6_rt_get_dev_rcu(ort);
|
||||
dev = ip6_rt_get_dev_rcu(res);
|
||||
rt = ip6_dst_alloc(dev_net(dev), dev, 0);
|
||||
if (!rt) {
|
||||
fib6_info_release(ort);
|
||||
fib6_info_release(f6i);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ip6_rt_copy_init(rt, ort);
|
||||
ip6_rt_copy_init(rt, res);
|
||||
rt->rt6i_flags |= RTF_CACHE;
|
||||
rt->dst.flags |= DST_HOST;
|
||||
rt->rt6i_dst.addr = *daddr;
|
||||
rt->rt6i_dst.plen = 128;
|
||||
|
||||
if (!rt6_is_gw_or_nonexthop(ort)) {
|
||||
if (ort->fib6_dst.plen != 128 &&
|
||||
ipv6_addr_equal(&ort->fib6_dst.addr, daddr))
|
||||
if (!rt6_is_gw_or_nonexthop(res)) {
|
||||
if (f6i->fib6_dst.plen != 128 &&
|
||||
ipv6_addr_equal(&f6i->fib6_dst.addr, daddr))
|
||||
rt->rt6i_flags |= RTF_ANYCAST;
|
||||
#ifdef CONFIG_IPV6_SUBTREES
|
||||
if (rt->rt6i_src.plen && saddr) {
|
||||
@ -1212,34 +1245,35 @@ static struct rt6_info *ip6_rt_cache_alloc(struct fib6_info *ort,
|
||||
return rt;
|
||||
}
|
||||
|
||||
static struct rt6_info *ip6_rt_pcpu_alloc(struct fib6_info *rt)
|
||||
static struct rt6_info *ip6_rt_pcpu_alloc(const struct fib6_result *res)
|
||||
{
|
||||
unsigned short flags = fib6_info_dst_flags(rt);
|
||||
struct fib6_info *f6i = res->f6i;
|
||||
unsigned short flags = fib6_info_dst_flags(f6i);
|
||||
struct net_device *dev;
|
||||
struct rt6_info *pcpu_rt;
|
||||
|
||||
if (!fib6_info_hold_safe(rt))
|
||||
if (!fib6_info_hold_safe(f6i))
|
||||
return NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
dev = ip6_rt_get_dev_rcu(rt);
|
||||
dev = ip6_rt_get_dev_rcu(res);
|
||||
pcpu_rt = ip6_dst_alloc(dev_net(dev), dev, flags);
|
||||
rcu_read_unlock();
|
||||
if (!pcpu_rt) {
|
||||
fib6_info_release(rt);
|
||||
fib6_info_release(f6i);
|
||||
return NULL;
|
||||
}
|
||||
ip6_rt_copy_init(pcpu_rt, rt);
|
||||
ip6_rt_copy_init(pcpu_rt, res);
|
||||
pcpu_rt->rt6i_flags |= RTF_PCPU;
|
||||
return pcpu_rt;
|
||||
}
|
||||
|
||||
/* It should be called with rcu_read_lock() acquired */
|
||||
static struct rt6_info *rt6_get_pcpu_route(struct fib6_info *rt)
|
||||
static struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res)
|
||||
{
|
||||
struct rt6_info *pcpu_rt, **p;
|
||||
|
||||
p = this_cpu_ptr(rt->rt6i_pcpu);
|
||||
p = this_cpu_ptr(res->f6i->rt6i_pcpu);
|
||||
pcpu_rt = *p;
|
||||
|
||||
if (pcpu_rt)
|
||||
@ -1249,18 +1283,18 @@ static struct rt6_info *rt6_get_pcpu_route(struct fib6_info *rt)
|
||||
}
|
||||
|
||||
static struct rt6_info *rt6_make_pcpu_route(struct net *net,
|
||||
struct fib6_info *rt)
|
||||
const struct fib6_result *res)
|
||||
{
|
||||
struct rt6_info *pcpu_rt, *prev, **p;
|
||||
|
||||
pcpu_rt = ip6_rt_pcpu_alloc(rt);
|
||||
pcpu_rt = ip6_rt_pcpu_alloc(res);
|
||||
if (!pcpu_rt) {
|
||||
dst_hold(&net->ipv6.ip6_null_entry->dst);
|
||||
return net->ipv6.ip6_null_entry;
|
||||
}
|
||||
|
||||
dst_hold(&pcpu_rt->dst);
|
||||
p = this_cpu_ptr(rt->rt6i_pcpu);
|
||||
p = this_cpu_ptr(res->f6i->rt6i_pcpu);
|
||||
prev = cmpxchg(p, NULL, pcpu_rt);
|
||||
BUG_ON(prev);
|
||||
|
||||
@ -1403,14 +1437,15 @@ __rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static unsigned int fib6_mtu(const struct fib6_info *rt)
|
||||
static unsigned int fib6_mtu(const struct fib6_result *res)
|
||||
{
|
||||
const struct fib6_nh *nh = res->nh;
|
||||
unsigned int mtu;
|
||||
|
||||
if (rt->fib6_pmtu) {
|
||||
mtu = rt->fib6_pmtu;
|
||||
if (res->f6i->fib6_pmtu) {
|
||||
mtu = res->f6i->fib6_pmtu;
|
||||
} else {
|
||||
struct net_device *dev = fib6_info_nh_dev(rt);
|
||||
struct net_device *dev = nh->fib_nh_dev;
|
||||
struct inet6_dev *idev;
|
||||
|
||||
rcu_read_lock();
|
||||
@ -1421,26 +1456,27 @@ static unsigned int fib6_mtu(const struct fib6_info *rt)
|
||||
|
||||
mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
|
||||
|
||||
return mtu - lwtunnel_headroom(rt->fib6_nh.fib_nh_lws, mtu);
|
||||
return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
|
||||
}
|
||||
|
||||
static int rt6_insert_exception(struct rt6_info *nrt,
|
||||
struct fib6_info *ort)
|
||||
const struct fib6_result *res)
|
||||
{
|
||||
struct net *net = dev_net(nrt->dst.dev);
|
||||
struct rt6_exception_bucket *bucket;
|
||||
struct in6_addr *src_key = NULL;
|
||||
struct rt6_exception *rt6_ex;
|
||||
struct fib6_info *f6i = res->f6i;
|
||||
int err = 0;
|
||||
|
||||
spin_lock_bh(&rt6_exception_lock);
|
||||
|
||||
if (ort->exception_bucket_flushed) {
|
||||
if (f6i->exception_bucket_flushed) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bucket = rcu_dereference_protected(ort->rt6i_exception_bucket,
|
||||
bucket = rcu_dereference_protected(f6i->rt6i_exception_bucket,
|
||||
lockdep_is_held(&rt6_exception_lock));
|
||||
if (!bucket) {
|
||||
bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket),
|
||||
@ -1449,24 +1485,24 @@ static int rt6_insert_exception(struct rt6_info *nrt,
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
rcu_assign_pointer(ort->rt6i_exception_bucket, bucket);
|
||||
rcu_assign_pointer(f6i->rt6i_exception_bucket, bucket);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IPV6_SUBTREES
|
||||
/* rt6i_src.plen != 0 indicates ort is in subtree
|
||||
/* fib6_src.plen != 0 indicates f6i is in subtree
|
||||
* and exception table is indexed by a hash of
|
||||
* both rt6i_dst and rt6i_src.
|
||||
* both fib6_dst and fib6_src.
|
||||
* Otherwise, the exception table is indexed by
|
||||
* a hash of only rt6i_dst.
|
||||
* a hash of only fib6_dst.
|
||||
*/
|
||||
if (ort->fib6_src.plen)
|
||||
if (f6i->fib6_src.plen)
|
||||
src_key = &nrt->rt6i_src.addr;
|
||||
#endif
|
||||
/* rt6_mtu_change() might lower mtu on ort.
|
||||
/* rt6_mtu_change() might lower mtu on f6i.
|
||||
* Only insert this exception route if its mtu
|
||||
* is less than ort's mtu value.
|
||||
* is less than f6i's mtu value.
|
||||
*/
|
||||
if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(ort)) {
|
||||
if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(res)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@ -1495,9 +1531,9 @@ out:
|
||||
|
||||
/* Update fn->fn_sernum to invalidate all cached dst */
|
||||
if (!err) {
|
||||
spin_lock_bh(&ort->fib6_table->tb6_lock);
|
||||
fib6_update_sernum(net, ort);
|
||||
spin_unlock_bh(&ort->fib6_table->tb6_lock);
|
||||
spin_lock_bh(&f6i->fib6_table->tb6_lock);
|
||||
fib6_update_sernum(net, f6i);
|
||||
spin_unlock_bh(&f6i->fib6_table->tb6_lock);
|
||||
fib6_force_start_gc(net);
|
||||
}
|
||||
|
||||
@ -1534,33 +1570,33 @@ out:
|
||||
/* Find cached rt in the hash table inside passed in rt
|
||||
* Caller has to hold rcu_read_lock()
|
||||
*/
|
||||
static struct rt6_info *rt6_find_cached_rt(struct fib6_info *rt,
|
||||
static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
|
||||
struct in6_addr *daddr,
|
||||
struct in6_addr *saddr)
|
||||
{
|
||||
struct rt6_exception_bucket *bucket;
|
||||
struct in6_addr *src_key = NULL;
|
||||
struct rt6_exception *rt6_ex;
|
||||
struct rt6_info *res = NULL;
|
||||
struct rt6_info *ret = NULL;
|
||||
|
||||
bucket = rcu_dereference(rt->rt6i_exception_bucket);
|
||||
bucket = rcu_dereference(res->f6i->rt6i_exception_bucket);
|
||||
|
||||
#ifdef CONFIG_IPV6_SUBTREES
|
||||
/* rt6i_src.plen != 0 indicates rt is in subtree
|
||||
/* fib6i_src.plen != 0 indicates f6i is in subtree
|
||||
* and exception table is indexed by a hash of
|
||||
* both rt6i_dst and rt6i_src.
|
||||
* both fib6_dst and fib6_src.
|
||||
* Otherwise, the exception table is indexed by
|
||||
* a hash of only rt6i_dst.
|
||||
* a hash of only fib6_dst.
|
||||
*/
|
||||
if (rt->fib6_src.plen)
|
||||
if (res->f6i->fib6_src.plen)
|
||||
src_key = saddr;
|
||||
#endif
|
||||
rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
|
||||
|
||||
if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
|
||||
res = rt6_ex->rt6i;
|
||||
ret = rt6_ex->rt6i;
|
||||
|
||||
return res;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Remove the passed in cached rt from the hash table that contains it */
|
||||
@ -1808,11 +1844,10 @@ void rt6_age_exceptions(struct fib6_info *rt,
|
||||
}
|
||||
|
||||
/* must be called with rcu lock held */
|
||||
struct fib6_info *fib6_table_lookup(struct net *net, struct fib6_table *table,
|
||||
int oif, struct flowi6 *fl6, int strict)
|
||||
int fib6_table_lookup(struct net *net, struct fib6_table *table, int oif,
|
||||
struct flowi6 *fl6, struct fib6_result *res, int strict)
|
||||
{
|
||||
struct fib6_node *fn, *saved_fn;
|
||||
struct fib6_info *f6i;
|
||||
|
||||
fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
|
||||
saved_fn = fn;
|
||||
@ -1821,8 +1856,8 @@ struct fib6_info *fib6_table_lookup(struct net *net, struct fib6_table *table,
|
||||
oif = 0;
|
||||
|
||||
redo_rt6_select:
|
||||
f6i = rt6_select(net, fn, oif, strict);
|
||||
if (f6i == net->ipv6.fib6_null_entry) {
|
||||
rt6_select(net, fn, oif, res, strict);
|
||||
if (res->f6i == net->ipv6.fib6_null_entry) {
|
||||
fn = fib6_backtrack(fn, &fl6->saddr);
|
||||
if (fn)
|
||||
goto redo_rt6_select;
|
||||
@ -1834,16 +1869,16 @@ redo_rt6_select:
|
||||
}
|
||||
}
|
||||
|
||||
trace_fib6_table_lookup(net, f6i, table, fl6);
|
||||
trace_fib6_table_lookup(net, res, table, fl6);
|
||||
|
||||
return f6i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
|
||||
int oif, struct flowi6 *fl6,
|
||||
const struct sk_buff *skb, int flags)
|
||||
{
|
||||
struct fib6_info *f6i;
|
||||
struct fib6_result res = {};
|
||||
struct rt6_info *rt;
|
||||
int strict = 0;
|
||||
|
||||
@ -1854,19 +1889,18 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
f6i = fib6_table_lookup(net, table, oif, fl6, strict);
|
||||
if (f6i == net->ipv6.fib6_null_entry) {
|
||||
fib6_table_lookup(net, table, oif, fl6, &res, strict);
|
||||
if (res.f6i == net->ipv6.fib6_null_entry) {
|
||||
rt = net->ipv6.ip6_null_entry;
|
||||
rcu_read_unlock();
|
||||
dst_hold(&rt->dst);
|
||||
return rt;
|
||||
}
|
||||
|
||||
if (f6i->fib6_nsiblings)
|
||||
f6i = fib6_multipath_select(net, f6i, fl6, oif, skb, strict);
|
||||
fib6_select_path(net, &res, fl6, oif, false, skb, strict);
|
||||
|
||||
/*Search through exception table */
|
||||
rt = rt6_find_cached_rt(f6i, &fl6->daddr, &fl6->saddr);
|
||||
rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
|
||||
if (rt) {
|
||||
if (ip6_hold_safe(net, &rt))
|
||||
dst_use_noref(&rt->dst, jiffies);
|
||||
@ -1874,7 +1908,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
|
||||
rcu_read_unlock();
|
||||
return rt;
|
||||
} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
|
||||
!f6i->fib6_nh.fib_nh_gw_family)) {
|
||||
!res.nh->fib_nh_gw_family)) {
|
||||
/* Create a RTF_CACHE clone which will not be
|
||||
* owned by the fib6 tree. It is for the special case where
|
||||
* the daddr in the skb during the neighbor look-up is different
|
||||
@ -1882,7 +1916,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
|
||||
*/
|
||||
struct rt6_info *uncached_rt;
|
||||
|
||||
uncached_rt = ip6_rt_cache_alloc(f6i, &fl6->daddr, NULL);
|
||||
uncached_rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
@ -1904,10 +1938,10 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
|
||||
struct rt6_info *pcpu_rt;
|
||||
|
||||
local_bh_disable();
|
||||
pcpu_rt = rt6_get_pcpu_route(f6i);
|
||||
pcpu_rt = rt6_get_pcpu_route(&res);
|
||||
|
||||
if (!pcpu_rt)
|
||||
pcpu_rt = rt6_make_pcpu_route(net, f6i);
|
||||
pcpu_rt = rt6_make_pcpu_route(net, &res);
|
||||
|
||||
local_bh_enable();
|
||||
rcu_read_unlock();
|
||||
@ -2326,19 +2360,23 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
|
||||
if (rt6->rt6i_flags & RTF_CACHE)
|
||||
rt6_update_exception_stamp_rt(rt6);
|
||||
} else if (daddr) {
|
||||
struct fib6_info *from;
|
||||
struct fib6_result res = {};
|
||||
struct rt6_info *nrt6;
|
||||
|
||||
rcu_read_lock();
|
||||
from = rcu_dereference(rt6->from);
|
||||
if (!from) {
|
||||
res.f6i = rcu_dereference(rt6->from);
|
||||
if (!res.f6i) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
nrt6 = ip6_rt_cache_alloc(from, daddr, saddr);
|
||||
res.nh = &res.f6i->fib6_nh;
|
||||
res.fib6_flags = res.f6i->fib6_flags;
|
||||
res.fib6_type = res.f6i->fib6_type;
|
||||
|
||||
nrt6 = ip6_rt_cache_alloc(&res, daddr, saddr);
|
||||
if (nrt6) {
|
||||
rt6_do_update_pmtu(nrt6, mtu);
|
||||
if (rt6_insert_exception(nrt6, from))
|
||||
if (rt6_insert_exception(nrt6, &res))
|
||||
dst_release_immediate(&nrt6->dst);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
@ -2411,12 +2449,13 @@ void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static bool ip6_redirect_nh_match(struct fib6_info *f6i,
|
||||
struct fib6_nh *nh,
|
||||
static bool ip6_redirect_nh_match(const struct fib6_result *res,
|
||||
struct flowi6 *fl6,
|
||||
const struct in6_addr *gw,
|
||||
struct rt6_info **ret)
|
||||
{
|
||||
const struct fib6_nh *nh = res->nh;
|
||||
|
||||
if (nh->fib_nh_flags & RTNH_F_DEAD || !nh->fib_nh_gw_family ||
|
||||
fl6->flowi6_oif != nh->fib_nh_dev->ifindex)
|
||||
return false;
|
||||
@ -2429,7 +2468,7 @@ static bool ip6_redirect_nh_match(struct fib6_info *f6i,
|
||||
if (!ipv6_addr_equal(gw, &nh->fib_nh_gw6)) {
|
||||
struct rt6_info *rt_cache;
|
||||
|
||||
rt_cache = rt6_find_cached_rt(f6i, &fl6->daddr, &fl6->saddr);
|
||||
rt_cache = rt6_find_cached_rt(res, &fl6->daddr, &fl6->saddr);
|
||||
if (rt_cache &&
|
||||
ipv6_addr_equal(gw, &rt_cache->rt6i_gateway)) {
|
||||
*ret = rt_cache;
|
||||
@ -2454,6 +2493,7 @@ static struct rt6_info *__ip6_route_redirect(struct net *net,
|
||||
{
|
||||
struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
|
||||
struct rt6_info *ret = NULL;
|
||||
struct fib6_result res = {};
|
||||
struct fib6_info *rt;
|
||||
struct fib6_node *fn;
|
||||
|
||||
@ -2471,12 +2511,14 @@ static struct rt6_info *__ip6_route_redirect(struct net *net,
|
||||
fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
|
||||
restart:
|
||||
for_each_fib6_node_rt_rcu(fn) {
|
||||
res.f6i = rt;
|
||||
res.nh = &rt->fib6_nh;
|
||||
|
||||
if (fib6_check_expired(rt))
|
||||
continue;
|
||||
if (rt->fib6_flags & RTF_REJECT)
|
||||
break;
|
||||
if (ip6_redirect_nh_match(rt, &rt->fib6_nh, fl6,
|
||||
&rdfl->gateway, &ret))
|
||||
if (ip6_redirect_nh_match(&res, fl6, &rdfl->gateway, &ret))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -2493,15 +2535,20 @@ restart:
|
||||
goto restart;
|
||||
}
|
||||
|
||||
res.f6i = rt;
|
||||
res.nh = &rt->fib6_nh;
|
||||
out:
|
||||
if (ret)
|
||||
if (ret) {
|
||||
ip6_hold_safe(net, &ret);
|
||||
else
|
||||
ret = ip6_create_rt_rcu(rt);
|
||||
} else {
|
||||
res.fib6_flags = res.f6i->fib6_flags;
|
||||
res.fib6_type = res.f6i->fib6_type;
|
||||
ret = ip6_create_rt_rcu(&res);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
trace_fib6_table_lookup(net, rt, table, fl6);
|
||||
trace_fib6_table_lookup(net, &res, table, fl6);
|
||||
return ret;
|
||||
};
|
||||
|
||||
@ -2619,12 +2666,15 @@ out:
|
||||
* based on ip6_dst_mtu_forward and exception logic of
|
||||
* rt6_find_cached_rt; called with rcu_read_lock
|
||||
*/
|
||||
u32 ip6_mtu_from_fib6(struct fib6_info *f6i, struct in6_addr *daddr,
|
||||
struct in6_addr *saddr)
|
||||
u32 ip6_mtu_from_fib6(const struct fib6_result *res,
|
||||
const struct in6_addr *daddr,
|
||||
const struct in6_addr *saddr)
|
||||
{
|
||||
struct rt6_exception_bucket *bucket;
|
||||
const struct fib6_nh *nh = res->nh;
|
||||
struct fib6_info *f6i = res->f6i;
|
||||
const struct in6_addr *src_key;
|
||||
struct rt6_exception *rt6_ex;
|
||||
struct in6_addr *src_key;
|
||||
struct inet6_dev *idev;
|
||||
u32 mtu = 0;
|
||||
|
||||
@ -2646,7 +2696,7 @@ u32 ip6_mtu_from_fib6(struct fib6_info *f6i, struct in6_addr *daddr,
|
||||
mtu = dst_metric_raw(&rt6_ex->rt6i->dst, RTAX_MTU);
|
||||
|
||||
if (likely(!mtu)) {
|
||||
struct net_device *dev = fib6_info_nh_dev(f6i);
|
||||
struct net_device *dev = nh->fib_nh_dev;
|
||||
|
||||
mtu = IPV6_MIN_MTU;
|
||||
idev = __in6_dev_get(dev);
|
||||
@ -2656,7 +2706,7 @@ u32 ip6_mtu_from_fib6(struct fib6_info *f6i, struct in6_addr *daddr,
|
||||
|
||||
mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
|
||||
out:
|
||||
return mtu - lwtunnel_headroom(fib6_info_nh_lwt(f6i), mtu);
|
||||
return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
|
||||
}
|
||||
|
||||
struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
|
||||
@ -3308,9 +3358,13 @@ static int ip6_route_del(struct fib6_config *cfg,
|
||||
struct fib6_nh *nh;
|
||||
|
||||
if (cfg->fc_flags & RTF_CACHE) {
|
||||
struct fib6_result res = {
|
||||
.f6i = rt,
|
||||
};
|
||||
int rc;
|
||||
|
||||
rt_cache = rt6_find_cached_rt(rt, &cfg->fc_dst,
|
||||
rt_cache = rt6_find_cached_rt(&res,
|
||||
&cfg->fc_dst,
|
||||
&cfg->fc_src);
|
||||
if (rt_cache) {
|
||||
rc = ip6_del_cached_rt(rt_cache, cfg);
|
||||
@ -3354,10 +3408,10 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
|
||||
{
|
||||
struct netevent_redirect netevent;
|
||||
struct rt6_info *rt, *nrt = NULL;
|
||||
struct fib6_result res = {};
|
||||
struct ndisc_options ndopts;
|
||||
struct inet6_dev *in6_dev;
|
||||
struct neighbour *neigh;
|
||||
struct fib6_info *from;
|
||||
struct rd_msg *msg;
|
||||
int optlen, on_link;
|
||||
u8 *lladdr;
|
||||
@ -3440,14 +3494,17 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
|
||||
NDISC_REDIRECT, &ndopts);
|
||||
|
||||
rcu_read_lock();
|
||||
from = rcu_dereference(rt->from);
|
||||
res.f6i = rcu_dereference(rt->from);
|
||||
/* This fib6_info_hold() is safe here because we hold reference to rt
|
||||
* and rt already holds reference to fib6_info.
|
||||
*/
|
||||
fib6_info_hold(from);
|
||||
fib6_info_hold(res.f6i);
|
||||
rcu_read_unlock();
|
||||
|
||||
nrt = ip6_rt_cache_alloc(from, &msg->dest, NULL);
|
||||
res.nh = &res.f6i->fib6_nh;
|
||||
res.fib6_flags = res.f6i->fib6_flags;
|
||||
res.fib6_type = res.f6i->fib6_type;
|
||||
nrt = ip6_rt_cache_alloc(&res, &msg->dest, NULL);
|
||||
if (!nrt)
|
||||
goto out;
|
||||
|
||||
@ -3461,7 +3518,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
|
||||
* a cached route because rt6_insert_exception() will
|
||||
* takes care of it
|
||||
*/
|
||||
if (rt6_insert_exception(nrt, from)) {
|
||||
if (rt6_insert_exception(nrt, &res)) {
|
||||
dst_release_immediate(&nrt->dst);
|
||||
goto out;
|
||||
}
|
||||
@ -3473,7 +3530,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
|
||||
call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
|
||||
|
||||
out:
|
||||
fib6_info_release(from);
|
||||
fib6_info_release(res.f6i);
|
||||
neigh_release(neigh);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user