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:
David S. Miller 2019-04-17 23:11:52 -07:00
commit cea29a7072
10 changed files with 350 additions and 295 deletions

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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;
}
),

View File

@ -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.

View File

@ -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,
};

View File

@ -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,

View File

@ -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;
}

View File

@ -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)

View File

@ -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);
}