Commit d4ead6b3 authored by David Ahern's avatar David Ahern Committed by David S. Miller

net/ipv6: move metrics from dst to rt6_info

Similar to IPv4, add fib metrics to the fib struct, which at the moment
is rt6_info. Will be moved to fib6_info in a later patch. Copy metrics
into dst by reference using refcount.

To make the transition:
- add dst_metrics to rt6_info. Default to dst_default_metrics if no
  metrics are passed during route add. No need for a separate pmtu
  entry; it can reference the MTU slot in fib6_metrics

- ip6_convert_metrics allocates memory in the FIB entry and uses
  ip_metrics_convert to copy from netlink attribute to metrics entry

- the convert metrics call is done in ip6_route_info_create simplifying
  the route add path
  + fib6_commit_metrics and fib6_copy_metrics and the temporary
    mx6_config are no longer needed

- add fib6_metric_set helper to change the value of a metric in the
  fib entry since dst_metric_set can no longer be used

- cow_metrics for IPv6 can drop to dst_cow_metrics_generic

- rt6_dst_from_metrics_check is no longer needed

- rt6_fill_node needs the FIB entry and dst as separate arguments to
  keep compatibility with existing output. Current dst address is
  renamed to dest.
  (to be consistent with IPv4 rt6_fill_node really should be split
  into 2 functions similar to fib_dump_info and rt_fill_info)

- rt6_fill_node no longer needs the temporary metrics variable
Signed-off-by: default avatarDavid Ahern <dsahern@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6edb3c96
...@@ -94,11 +94,6 @@ struct fib6_gc_args { ...@@ -94,11 +94,6 @@ struct fib6_gc_args {
#define FIB6_SUBTREE(fn) (rcu_dereference_protected((fn)->subtree, 1)) #define FIB6_SUBTREE(fn) (rcu_dereference_protected((fn)->subtree, 1))
#endif #endif
struct mx6_config {
const u32 *mx;
DECLARE_BITMAP(mx_valid, RTAX_MAX);
};
/* /*
* routing information * routing information
* *
...@@ -176,7 +171,6 @@ struct rt6_info { ...@@ -176,7 +171,6 @@ struct rt6_info {
struct rt6_exception_bucket __rcu *rt6i_exception_bucket; struct rt6_exception_bucket __rcu *rt6i_exception_bucket;
u32 rt6i_metric; u32 rt6i_metric;
u32 rt6i_pmtu;
/* more non-fragment space at head required */ /* more non-fragment space at head required */
unsigned short rt6i_nfheader_len; unsigned short rt6i_nfheader_len;
u8 rt6i_protocol; u8 rt6i_protocol;
...@@ -185,6 +179,8 @@ struct rt6_info { ...@@ -185,6 +179,8 @@ struct rt6_info {
should_flush:1, should_flush:1,
unused:6; unused:6;
struct dst_metrics *fib6_metrics;
#define fib6_pmtu fib6_metrics->metrics[RTAX_MTU-1]
struct fib6_nh fib6_nh; struct fib6_nh fib6_nh;
}; };
...@@ -390,8 +386,7 @@ void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg), ...@@ -390,8 +386,7 @@ void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg),
void *arg); void *arg);
int fib6_add(struct fib6_node *root, struct rt6_info *rt, int fib6_add(struct fib6_node *root, struct rt6_info *rt,
struct nl_info *info, struct mx6_config *mxc, struct nl_info *info, struct netlink_ext_ack *extack);
struct netlink_ext_ack *extack);
int fib6_del(struct rt6_info *rt, struct nl_info *info); int fib6_del(struct rt6_info *rt, struct nl_info *info);
void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info, void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info,
...@@ -420,6 +415,12 @@ int fib6_tables_dump(struct net *net, struct notifier_block *nb); ...@@ -420,6 +415,12 @@ int fib6_tables_dump(struct net *net, struct notifier_block *nb);
void fib6_update_sernum(struct net *net, struct rt6_info *rt); void fib6_update_sernum(struct net *net, struct rt6_info *rt);
void fib6_update_sernum_upto_root(struct net *net, struct rt6_info *rt); void fib6_update_sernum_upto_root(struct net *net, struct rt6_info *rt);
void fib6_metric_set(struct rt6_info *f6i, int metric, u32 val);
static inline bool fib6_metric_locked(struct rt6_info *f6i, int metric)
{
return !!(f6i->fib6_metrics->metrics[RTAX_LOCK - 1] & (1 << metric));
}
#ifdef CONFIG_IPV6_MULTIPLE_TABLES #ifdef CONFIG_IPV6_MULTIPLE_TABLES
int fib6_rules_init(void); int fib6_rules_init(void);
void fib6_rules_cleanup(void); void fib6_rules_cleanup(void);
......
...@@ -58,6 +58,7 @@ const struct dst_metrics dst_default_metrics = { ...@@ -58,6 +58,7 @@ const struct dst_metrics dst_default_metrics = {
*/ */
.refcnt = REFCOUNT_INIT(1), .refcnt = REFCOUNT_INIT(1),
}; };
EXPORT_SYMBOL(dst_default_metrics);
void dst_init(struct dst_entry *dst, struct dst_ops *ops, void dst_init(struct dst_entry *dst, struct dst_ops *ops,
struct net_device *dev, int initial_ref, int initial_obsolete, struct net_device *dev, int initial_ref, int initial_obsolete,
......
...@@ -578,6 +578,24 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -578,6 +578,24 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
return res; return res;
} }
void fib6_metric_set(struct rt6_info *f6i, int metric, u32 val)
{
if (!f6i)
return;
if (f6i->fib6_metrics == &dst_default_metrics) {
struct dst_metrics *p = kzalloc(sizeof(*p), GFP_ATOMIC);
if (!p)
return;
refcount_set(&p->refcnt, 1);
f6i->fib6_metrics = p;
}
f6i->fib6_metrics->metrics[metric - 1] = val;
}
/* /*
* Routing Table * Routing Table
* *
...@@ -801,38 +819,6 @@ static struct fib6_node *fib6_add_1(struct net *net, ...@@ -801,38 +819,6 @@ static struct fib6_node *fib6_add_1(struct net *net,
return ln; return ln;
} }
static void fib6_copy_metrics(u32 *mp, const struct mx6_config *mxc)
{
int i;
for (i = 0; i < RTAX_MAX; i++) {
if (test_bit(i, mxc->mx_valid))
mp[i] = mxc->mx[i];
}
}
static int fib6_commit_metrics(struct dst_entry *dst, struct mx6_config *mxc)
{
if (!mxc->mx)
return 0;
if (dst->flags & DST_HOST) {
u32 *mp = dst_metrics_write_ptr(dst);
if (unlikely(!mp))
return -ENOMEM;
fib6_copy_metrics(mp, mxc);
} else {
dst_init_metrics(dst, mxc->mx, false);
/* We've stolen mx now. */
mxc->mx = NULL;
}
return 0;
}
static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn, static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn,
struct net *net) struct net *net)
{ {
...@@ -866,7 +852,7 @@ static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn, ...@@ -866,7 +852,7 @@ static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn,
*/ */
static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
struct nl_info *info, struct mx6_config *mxc, struct nl_info *info,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct rt6_info *leaf = rcu_dereference_protected(fn->leaf, struct rt6_info *leaf = rcu_dereference_protected(fn->leaf,
...@@ -923,7 +909,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, ...@@ -923,7 +909,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
rt6_clean_expires(iter); rt6_clean_expires(iter);
else else
rt6_set_expires(iter, rt->dst.expires); rt6_set_expires(iter, rt->dst.expires);
iter->rt6i_pmtu = rt->rt6i_pmtu; fib6_metric_set(iter, RTAX_MTU, rt->fib6_pmtu);
return -EEXIST; return -EEXIST;
} }
/* If we have the same destination and the same metric, /* If we have the same destination and the same metric,
...@@ -1002,9 +988,6 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, ...@@ -1002,9 +988,6 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
add: add:
nlflags |= NLM_F_CREATE; nlflags |= NLM_F_CREATE;
err = fib6_commit_metrics(&rt->dst, mxc);
if (err)
return err;
err = call_fib6_entry_notifiers(info->nl_net, err = call_fib6_entry_notifiers(info->nl_net,
FIB_EVENT_ENTRY_ADD, FIB_EVENT_ENTRY_ADD,
...@@ -1035,10 +1018,6 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, ...@@ -1035,10 +1018,6 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
return -ENOENT; return -ENOENT;
} }
err = fib6_commit_metrics(&rt->dst, mxc);
if (err)
return err;
err = call_fib6_entry_notifiers(info->nl_net, err = call_fib6_entry_notifiers(info->nl_net,
FIB_EVENT_ENTRY_REPLACE, FIB_EVENT_ENTRY_REPLACE,
rt, extack); rt, extack);
...@@ -1135,8 +1114,7 @@ void fib6_update_sernum_upto_root(struct net *net, struct rt6_info *rt) ...@@ -1135,8 +1114,7 @@ void fib6_update_sernum_upto_root(struct net *net, struct rt6_info *rt)
*/ */
int fib6_add(struct fib6_node *root, struct rt6_info *rt, int fib6_add(struct fib6_node *root, struct rt6_info *rt,
struct nl_info *info, struct mx6_config *mxc, struct nl_info *info, struct netlink_ext_ack *extack)
struct netlink_ext_ack *extack)
{ {
struct fib6_table *table = rt->rt6i_table; struct fib6_table *table = rt->rt6i_table;
struct fib6_node *fn, *pn = NULL; struct fib6_node *fn, *pn = NULL;
...@@ -1244,7 +1222,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, ...@@ -1244,7 +1222,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
} }
#endif #endif
err = fib6_add_rt2node(fn, rt, info, mxc, extack); err = fib6_add_rt2node(fn, rt, info, extack);
if (!err) { if (!err) {
__fib6_update_sernum_upto_root(rt, sernum); __fib6_update_sernum_upto_root(rt, sernum);
fib6_start_gc(info->nl_net, rt); fib6_start_gc(info->nl_net, rt);
......
...@@ -1323,8 +1323,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) ...@@ -1323,8 +1323,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
ra_msg->icmph.icmp6_hop_limit) { ra_msg->icmph.icmp6_hop_limit) {
if (in6_dev->cnf.accept_ra_min_hop_limit <= ra_msg->icmph.icmp6_hop_limit) { if (in6_dev->cnf.accept_ra_min_hop_limit <= ra_msg->icmph.icmp6_hop_limit) {
in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit; in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
if (rt) fib6_metric_set(rt, RTAX_HOPLIMIT,
dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
ra_msg->icmph.icmp6_hop_limit); ra_msg->icmph.icmp6_hop_limit);
} else { } else {
ND_PRINTK(2, warn, "RA: Got route advertisement with lower hop_limit than minimum\n"); ND_PRINTK(2, warn, "RA: Got route advertisement with lower hop_limit than minimum\n");
...@@ -1477,10 +1476,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) ...@@ -1477,10 +1476,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu); ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
} else if (in6_dev->cnf.mtu6 != mtu) { } else if (in6_dev->cnf.mtu6 != mtu) {
in6_dev->cnf.mtu6 = mtu; in6_dev->cnf.mtu6 = mtu;
fib6_metric_set(rt, RTAX_MTU, mtu);
if (rt)
dst_metric_set(&rt->dst, RTAX_MTU, mtu);
rt6_mtu_change(skb->dev, mtu); rt6_mtu_change(skb->dev, mtu);
} }
} }
......
...@@ -96,12 +96,11 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, ...@@ -96,12 +96,11 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
struct sk_buff *skb, u32 mtu); struct sk_buff *skb, u32 mtu);
static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
struct sk_buff *skb); struct sk_buff *skb);
static void rt6_dst_from_metrics_check(struct rt6_info *rt);
static int rt6_score_route(struct rt6_info *rt, int oif, int strict); static int rt6_score_route(struct rt6_info *rt, int oif, int strict);
static size_t rt6_nlmsg_size(struct rt6_info *rt); static size_t rt6_nlmsg_size(struct rt6_info *rt);
static int rt6_fill_node(struct net *net, static int rt6_fill_node(struct net *net, struct sk_buff *skb,
struct sk_buff *skb, struct rt6_info *rt, struct rt6_info *rt, struct dst_entry *dst,
struct in6_addr *dst, struct in6_addr *src, struct in6_addr *dest, struct in6_addr *src,
int iif, int type, u32 portid, u32 seq, int iif, int type, u32 portid, u32 seq,
unsigned int flags); unsigned int flags);
static struct rt6_info *rt6_find_cached_rt(struct rt6_info *rt, static struct rt6_info *rt6_find_cached_rt(struct rt6_info *rt,
...@@ -183,23 +182,6 @@ static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) ...@@ -183,23 +182,6 @@ static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
} }
} }
static u32 *rt6_pcpu_cow_metrics(struct rt6_info *rt)
{
return dst_metrics_write_ptr(&rt->from->dst);
}
static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
{
struct rt6_info *rt = (struct rt6_info *)dst;
if (rt->rt6i_flags & RTF_PCPU)
return rt6_pcpu_cow_metrics(rt);
else if (rt->rt6i_flags & RTF_CACHE)
return NULL;
else
return dst_cow_metrics_generic(dst, old);
}
static inline const void *choose_neigh_daddr(struct rt6_info *rt, static inline const void *choose_neigh_daddr(struct rt6_info *rt,
struct sk_buff *skb, struct sk_buff *skb,
const void *daddr) const void *daddr)
...@@ -249,7 +231,7 @@ static struct dst_ops ip6_dst_ops_template = { ...@@ -249,7 +231,7 @@ static struct dst_ops ip6_dst_ops_template = {
.check = ip6_dst_check, .check = ip6_dst_check,
.default_advmss = ip6_default_advmss, .default_advmss = ip6_default_advmss,
.mtu = ip6_mtu, .mtu = ip6_mtu,
.cow_metrics = ipv6_cow_metrics, .cow_metrics = dst_cow_metrics_generic,
.destroy = ip6_dst_destroy, .destroy = ip6_dst_destroy,
.ifdown = ip6_dst_ifdown, .ifdown = ip6_dst_ifdown,
.negative_advice = ip6_negative_advice, .negative_advice = ip6_negative_advice,
...@@ -353,6 +335,7 @@ static void rt6_info_init(struct rt6_info *rt) ...@@ -353,6 +335,7 @@ static void rt6_info_init(struct rt6_info *rt)
memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
INIT_LIST_HEAD(&rt->rt6i_siblings); INIT_LIST_HEAD(&rt->rt6i_siblings);
INIT_LIST_HEAD(&rt->rt6i_uncached); INIT_LIST_HEAD(&rt->rt6i_uncached);
rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics;
} }
/* allocate dst with ip6_dst_ops */ /* allocate dst with ip6_dst_ops */
...@@ -395,6 +378,7 @@ static void ip6_dst_destroy(struct dst_entry *dst) ...@@ -395,6 +378,7 @@ static void ip6_dst_destroy(struct dst_entry *dst)
struct rt6_exception_bucket *bucket; struct rt6_exception_bucket *bucket;
struct rt6_info *from = rt->from; struct rt6_info *from = rt->from;
struct inet6_dev *idev; struct inet6_dev *idev;
struct dst_metrics *m;
dst_destroy_metrics_generic(dst); dst_destroy_metrics_generic(dst);
free_percpu(rt->rt6i_pcpu); free_percpu(rt->rt6i_pcpu);
...@@ -411,6 +395,10 @@ static void ip6_dst_destroy(struct dst_entry *dst) ...@@ -411,6 +395,10 @@ static void ip6_dst_destroy(struct dst_entry *dst)
kfree(bucket); kfree(bucket);
} }
m = rt->fib6_metrics;
if (m != &dst_default_metrics && refcount_dec_and_test(&m->refcnt))
kfree(m);
rt->from = NULL; rt->from = NULL;
dst_release(&from->dst); dst_release(&from->dst);
} }
...@@ -996,7 +984,11 @@ static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) ...@@ -996,7 +984,11 @@ static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from)
rt->rt6i_flags &= ~RTF_EXPIRES; rt->rt6i_flags &= ~RTF_EXPIRES;
dst_hold(&from->dst); dst_hold(&from->dst);
rt->from = from; rt->from = from;
dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true); dst_init_metrics(&rt->dst, from->fib6_metrics->metrics, true);
if (from->fib6_metrics != &dst_default_metrics) {
rt->dst._metrics |= DST_METRICS_REFCOUNTED;
refcount_inc(&from->fib6_metrics->refcnt);
}
} }
static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort) static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
...@@ -1140,7 +1132,6 @@ EXPORT_SYMBOL(rt6_lookup); ...@@ -1140,7 +1132,6 @@ EXPORT_SYMBOL(rt6_lookup);
*/ */
static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
struct mx6_config *mxc,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
int err; int err;
...@@ -1148,7 +1139,7 @@ static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, ...@@ -1148,7 +1139,7 @@ static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
table = rt->rt6i_table; table = rt->rt6i_table;
spin_lock_bh(&table->tb6_lock); spin_lock_bh(&table->tb6_lock);
err = fib6_add(&table->tb6_root, rt, info, mxc, extack); err = fib6_add(&table->tb6_root, rt, info, extack);
spin_unlock_bh(&table->tb6_lock); spin_unlock_bh(&table->tb6_lock);
return err; return err;
...@@ -1157,11 +1148,10 @@ static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, ...@@ -1157,11 +1148,10 @@ static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info,
int ip6_ins_rt(struct net *net, struct rt6_info *rt) int ip6_ins_rt(struct net *net, struct rt6_info *rt)
{ {
struct nl_info info = { .nl_net = net, }; struct nl_info info = { .nl_net = net, };
struct mx6_config mxc = { .mx = NULL, };
/* Hold dst to account for the reference from the fib6 tree */ /* Hold dst to account for the reference from the fib6 tree */
dst_hold(&rt->dst); dst_hold(&rt->dst);
return __ip6_ins_rt(rt, &info, &mxc, NULL); return __ip6_ins_rt(rt, &info, NULL);
} }
static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort, static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
...@@ -1232,8 +1222,8 @@ static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt) ...@@ -1232,8 +1222,8 @@ static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt)
p = this_cpu_ptr(rt->rt6i_pcpu); p = this_cpu_ptr(rt->rt6i_pcpu);
pcpu_rt = *p; pcpu_rt = *p;
if (pcpu_rt && ip6_hold_safe(NULL, &pcpu_rt, false)) if (pcpu_rt)
rt6_dst_from_metrics_check(pcpu_rt); ip6_hold_safe(NULL, &pcpu_rt, false);
return pcpu_rt; return pcpu_rt;
} }
...@@ -1254,7 +1244,6 @@ static struct rt6_info *rt6_make_pcpu_route(struct net *net, ...@@ -1254,7 +1244,6 @@ static struct rt6_info *rt6_make_pcpu_route(struct net *net,
prev = cmpxchg(p, NULL, pcpu_rt); prev = cmpxchg(p, NULL, pcpu_rt);
BUG_ON(prev); BUG_ON(prev);
rt6_dst_from_metrics_check(pcpu_rt);
return pcpu_rt; return pcpu_rt;
} }
...@@ -1384,6 +1373,16 @@ __rt6_find_exception_rcu(struct rt6_exception_bucket **bucket, ...@@ -1384,6 +1373,16 @@ __rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
return NULL; return NULL;
} }
static unsigned int fib6_mtu(const struct rt6_info *rt)
{
unsigned int mtu;
mtu = rt->fib6_pmtu ? : rt->rt6i_idev->cnf.mtu6;
mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
return mtu - lwtunnel_headroom(rt->fib6_nh.nh_lwtstate, mtu);
}
static int rt6_insert_exception(struct rt6_info *nrt, static int rt6_insert_exception(struct rt6_info *nrt,
struct rt6_info *ort) struct rt6_info *ort)
{ {
...@@ -1436,7 +1435,7 @@ static int rt6_insert_exception(struct rt6_info *nrt, ...@@ -1436,7 +1435,7 @@ static int rt6_insert_exception(struct rt6_info *nrt,
* Only insert this exception route if its mtu * Only insert this exception route if its mtu
* is less than ort's mtu value. * is less than ort's mtu value.
*/ */
if (nrt->rt6i_pmtu >= dst_mtu(&ort->dst)) { if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(ort)) {
err = -EINVAL; err = -EINVAL;
goto out; goto out;
} }
...@@ -1673,12 +1672,12 @@ static void rt6_exceptions_update_pmtu(struct inet6_dev *idev, ...@@ -1673,12 +1672,12 @@ static void rt6_exceptions_update_pmtu(struct inet6_dev *idev,
struct rt6_info *entry = rt6_ex->rt6i; struct rt6_info *entry = rt6_ex->rt6i;
/* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected /* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected
* route), the metrics of its rt->dst.from have already * route), the metrics of its rt->from have already
* been updated. * been updated.
*/ */
if (entry->rt6i_pmtu && if (dst_metric_raw(&entry->dst, RTAX_MTU) &&
rt6_mtu_change_route_allowed(idev, entry, mtu)) rt6_mtu_change_route_allowed(idev, entry, mtu))
entry->rt6i_pmtu = mtu; dst_metric_set(&entry->dst, RTAX_MTU, mtu);
} }
bucket++; bucket++;
} }
...@@ -1844,10 +1843,9 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, ...@@ -1844,10 +1843,9 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
trace_fib6_table_lookup(net, rt, table, fl6); trace_fib6_table_lookup(net, rt, table, fl6);
return rt; return rt;
} else if (rt->rt6i_flags & RTF_CACHE) { } else if (rt->rt6i_flags & RTF_CACHE) {
if (ip6_hold_safe(net, &rt, true)) { if (ip6_hold_safe(net, &rt, true))
dst_use_noref(&rt->dst, jiffies); dst_use_noref(&rt->dst, jiffies);
rt6_dst_from_metrics_check(rt);
}
rcu_read_unlock(); rcu_read_unlock();
trace_fib6_table_lookup(net, rt, table, fl6); trace_fib6_table_lookup(net, rt, table, fl6);
return rt; return rt;
...@@ -2147,13 +2145,6 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori ...@@ -2147,13 +2145,6 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori
* Destination cache support functions * Destination cache support functions
*/ */
static void rt6_dst_from_metrics_check(struct rt6_info *rt)
{
if (rt->from &&
dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(&rt->from->dst))
dst_init_metrics(&rt->dst, dst_metrics_ptr(&rt->from->dst), true);
}
static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie) static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
{ {
u32 rt_cookie = 0; u32 rt_cookie = 0;
...@@ -2188,8 +2179,6 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) ...@@ -2188,8 +2179,6 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
* into this function always. * into this function always.
*/ */
rt6_dst_from_metrics_check(rt);
if (rt->rt6i_flags & RTF_PCPU || if (rt->rt6i_flags & RTF_PCPU ||
(unlikely(!list_empty(&rt->rt6i_uncached)) && rt->from)) (unlikely(!list_empty(&rt->rt6i_uncached)) && rt->from))
return rt6_dst_from_check(rt, cookie); return rt6_dst_from_check(rt, cookie);
...@@ -2242,8 +2231,8 @@ static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) ...@@ -2242,8 +2231,8 @@ static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
{ {
struct net *net = dev_net(rt->dst.dev); struct net *net = dev_net(rt->dst.dev);
dst_metric_set(&rt->dst, RTAX_MTU, mtu);
rt->rt6i_flags |= RTF_MODIFIED; rt->rt6i_flags |= RTF_MODIFIED;
rt->rt6i_pmtu = mtu;
rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
} }
...@@ -2289,10 +2278,10 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, ...@@ -2289,10 +2278,10 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
} else if (daddr) { } else if (daddr) {
struct rt6_info *nrt6; struct rt6_info *nrt6;
nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr); nrt6 = ip6_rt_cache_alloc(rt6->from, daddr, saddr);
if (nrt6) { if (nrt6) {
rt6_do_update_pmtu(nrt6, mtu); rt6_do_update_pmtu(nrt6, mtu);
if (rt6_insert_exception(nrt6, rt6)) if (rt6_insert_exception(nrt6, rt6->from))
dst_release_immediate(&nrt6->dst); dst_release_immediate(&nrt6->dst);
} }
} }
...@@ -2533,12 +2522,8 @@ static unsigned int ip6_default_advmss(const struct dst_entry *dst) ...@@ -2533,12 +2522,8 @@ static unsigned int ip6_default_advmss(const struct dst_entry *dst)
static unsigned int ip6_mtu(const struct dst_entry *dst) static unsigned int ip6_mtu(const struct dst_entry *dst)
{ {
const struct rt6_info *rt = (const struct rt6_info *)dst;
unsigned int mtu = rt->rt6i_pmtu;
struct inet6_dev *idev; struct inet6_dev *idev;
unsigned int mtu;
if (mtu)
goto out;
mtu = dst_metric_raw(dst, RTAX_MTU); mtu = dst_metric_raw(dst, RTAX_MTU);
if (mtu) if (mtu)
...@@ -2622,60 +2607,24 @@ static int ip6_dst_gc(struct dst_ops *ops) ...@@ -2622,60 +2607,24 @@ static int ip6_dst_gc(struct dst_ops *ops)
return entries > rt_max_size; return entries > rt_max_size;
} }
static int ip6_convert_metrics(struct mx6_config *mxc, static int ip6_convert_metrics(struct net *net, struct rt6_info *rt,
const struct fib6_config *cfg) struct fib6_config *cfg)
{ {
struct net *net = cfg->fc_nlinfo.nl_net; int err = 0;
bool ecn_ca = false;
struct nlattr *nla;
int remaining;
u32 *mp;
if (!cfg->fc_mx)
return 0;
mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); if (cfg->fc_mx) {
if (unlikely(!mp)) rt->fib6_metrics = kzalloc(sizeof(*rt->fib6_metrics),
GFP_KERNEL);
if (unlikely(!rt->fib6_metrics))
return -ENOMEM; return -ENOMEM;
nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { refcount_set(&rt->fib6_metrics->refcnt, 1);
int type = nla_type(nla);
u32 val;
if (!type)
continue;
if (unlikely(type > RTAX_MAX))
goto err;
if (type == RTAX_CC_ALGO) {
char tmp[TCP_CA_NAME_MAX];
nla_strlcpy(tmp, nla, sizeof(tmp));
val = tcp_ca_get_key_by_name(net, tmp, &ecn_ca);
if (val == TCP_CA_UNSPEC)
goto err;
} else {
val = nla_get_u32(nla);
}
if (type == RTAX_HOPLIMIT && val > 255)
val = 255;
if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK))
goto err;
mp[type - 1] = val;
__set_bit(type - 1, mxc->mx_valid);
}
if (ecn_ca) { err = ip_metrics_convert(net, cfg->fc_mx, cfg->fc_mx_len,
__set_bit(RTAX_FEATURES - 1, mxc->mx_valid); rt->fib6_metrics->metrics);
mp[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA;
} }
mxc->mx = mp; return err;
return 0;
err:
kfree(mp);
return -EINVAL;
} }
static struct rt6_info *ip6_nh_lookup_table(struct net *net, static struct rt6_info *ip6_nh_lookup_table(struct net *net,
...@@ -2955,6 +2904,10 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, ...@@ -2955,6 +2904,10 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
goto out; goto out;
} }
err = ip6_convert_metrics(net, rt, cfg);
if (err < 0)
goto out;
if (cfg->fc_flags & RTF_EXPIRES) if (cfg->fc_flags & RTF_EXPIRES)
rt6_set_expires(rt, jiffies + rt6_set_expires(rt, jiffies +
clock_t_to_jiffies(cfg->fc_expires)); clock_t_to_jiffies(cfg->fc_expires));
...@@ -3078,32 +3031,16 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, ...@@ -3078,32 +3031,16 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
return ERR_PTR(err); return ERR_PTR(err);
} }
int ip6_route_add(struct fib6_config *cfg, int ip6_route_add(struct fib6_config *cfg, struct netlink_ext_ack *extack)
struct netlink_ext_ack *extack)
{ {
struct mx6_config mxc = { .mx = NULL, };
struct rt6_info *rt; struct rt6_info *rt;
int err; int err;
rt = ip6_route_info_create(cfg, extack); rt = ip6_route_info_create(cfg, extack);
if (IS_ERR(rt)) { if (IS_ERR(rt))
err = PTR_ERR(rt); return PTR_ERR(rt);
rt = NULL;
goto out;
}
err = ip6_convert_metrics(&mxc, cfg);
if (err)
goto out;
err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc, extack);
kfree(mxc.mx); err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack);
return err;
out:
if (rt)
dst_release_immediate(&rt->dst);
return err; return err;
} }
...@@ -3157,7 +3094,7 @@ static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg) ...@@ -3157,7 +3094,7 @@ static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg)
if (skb) { if (skb) {
u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
if (rt6_fill_node(net, skb, rt, if (rt6_fill_node(net, skb, rt, NULL,
NULL, NULL, 0, RTM_DELROUTE, NULL, NULL, 0, RTM_DELROUTE,
info->portid, seq, 0) < 0) { info->portid, seq, 0) < 0) {
kfree_skb(skb); kfree_skb(skb);
...@@ -3348,7 +3285,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu ...@@ -3348,7 +3285,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
* a cached route because rt6_insert_exception() will * a cached route because rt6_insert_exception() will
* takes care of it * takes care of it
*/ */
if (rt6_insert_exception(nrt, rt)) { if (rt6_insert_exception(nrt, rt->from)) {
dst_release_immediate(&nrt->dst); dst_release_immediate(&nrt->dst);
goto out; goto out;
} }
...@@ -4018,11 +3955,14 @@ static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) ...@@ -4018,11 +3955,14 @@ static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
update PMTU increase is a MUST. (i.e. jumbo frame) update PMTU increase is a MUST. (i.e. jumbo frame)
*/ */
if (rt->fib6_nh.nh_dev == arg->dev && if (rt->fib6_nh.nh_dev == arg->dev &&
!dst_metric_locked(&rt->dst, RTAX_MTU)) { !fib6_metric_locked(rt, RTAX_MTU)) {
u32 mtu = rt->fib6_pmtu;
if (mtu >= arg->mtu ||
(mtu < arg->mtu && mtu == idev->cnf.mtu6))
fib6_metric_set(rt, RTAX_MTU, arg->mtu);
spin_lock_bh(&rt6_exception_lock); spin_lock_bh(&rt6_exception_lock);
if (dst_metric_raw(&rt->dst, RTAX_MTU) &&
rt6_mtu_change_route_allowed(idev, rt, arg->mtu))
dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu);
rt6_exceptions_update_pmtu(idev, rt, arg->mtu); rt6_exceptions_update_pmtu(idev, rt, arg->mtu);
spin_unlock_bh(&rt6_exception_lock); spin_unlock_bh(&rt6_exception_lock);
} }
...@@ -4183,7 +4123,6 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -4183,7 +4123,6 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
struct rt6_nh { struct rt6_nh {
struct rt6_info *rt6_info; struct rt6_info *rt6_info;
struct fib6_config r_cfg; struct fib6_config r_cfg;
struct mx6_config mxc;
struct list_head next; struct list_head next;
}; };
...@@ -4198,7 +4137,8 @@ static void ip6_print_replace_route_err(struct list_head *rt6_nh_list) ...@@ -4198,7 +4137,8 @@ static void ip6_print_replace_route_err(struct list_head *rt6_nh_list)
} }
} }
static int ip6_route_info_append(struct list_head *rt6_nh_list, static int ip6_route_info_append(struct net *net,
struct list_head *rt6_nh_list,
struct rt6_info *rt, struct fib6_config *r_cfg) struct rt6_info *rt, struct fib6_config *r_cfg)
{ {
struct rt6_nh *nh; struct rt6_nh *nh;
...@@ -4214,7 +4154,7 @@ static int ip6_route_info_append(struct list_head *rt6_nh_list, ...@@ -4214,7 +4154,7 @@ static int ip6_route_info_append(struct list_head *rt6_nh_list,
if (!nh) if (!nh)
return -ENOMEM; return -ENOMEM;
nh->rt6_info = rt; nh->rt6_info = rt;
err = ip6_convert_metrics(&nh->mxc, r_cfg); err = ip6_convert_metrics(net, rt, r_cfg);
if (err) { if (err) {
kfree(nh); kfree(nh);
return err; return err;
...@@ -4305,7 +4245,8 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, ...@@ -4305,7 +4245,8 @@ static int ip6_route_multipath_add(struct fib6_config *cfg,
rt->fib6_nh.nh_weight = rtnh->rtnh_hops + 1; rt->fib6_nh.nh_weight = rtnh->rtnh_hops + 1;
err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg); err = ip6_route_info_append(info->nl_net, &rt6_nh_list,
rt, &r_cfg);
if (err) { if (err) {
dst_release_immediate(&rt->dst); dst_release_immediate(&rt->dst);
goto cleanup; goto cleanup;
...@@ -4323,7 +4264,7 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, ...@@ -4323,7 +4264,7 @@ static int ip6_route_multipath_add(struct fib6_config *cfg,
err_nh = NULL; err_nh = NULL;
list_for_each_entry(nh, &rt6_nh_list, next) { list_for_each_entry(nh, &rt6_nh_list, next) {
rt_last = nh->rt6_info; rt_last = nh->rt6_info;
err = __ip6_ins_rt(nh->rt6_info, info, &nh->mxc, extack); err = __ip6_ins_rt(nh->rt6_info, info, extack);
/* save reference to first route for notification */ /* save reference to first route for notification */
if (!rt_notif && !err) if (!rt_notif && !err)
rt_notif = nh->rt6_info; rt_notif = nh->rt6_info;
...@@ -4372,7 +4313,6 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, ...@@ -4372,7 +4313,6 @@ static int ip6_route_multipath_add(struct fib6_config *cfg,
list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) { list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
if (nh->rt6_info) if (nh->rt6_info)
dst_release_immediate(&nh->rt6_info->dst); dst_release_immediate(&nh->rt6_info->dst);
kfree(nh->mxc.mx);
list_del(&nh->next); list_del(&nh->next);
kfree(nh); kfree(nh);
} }
...@@ -4546,16 +4486,16 @@ static int rt6_add_nexthop(struct sk_buff *skb, struct rt6_info *rt) ...@@ -4546,16 +4486,16 @@ static int rt6_add_nexthop(struct sk_buff *skb, struct rt6_info *rt)
return -EMSGSIZE; return -EMSGSIZE;
} }
static int rt6_fill_node(struct net *net, static int rt6_fill_node(struct net *net, struct sk_buff *skb,
struct sk_buff *skb, struct rt6_info *rt, struct rt6_info *rt, struct dst_entry *dst,
struct in6_addr *dst, struct in6_addr *src, struct in6_addr *dest, struct in6_addr *src,
int iif, int type, u32 portid, u32 seq, int iif, int type, u32 portid, u32 seq,
unsigned int flags) unsigned int flags)
{ {
u32 metrics[RTAX_MAX];
struct rtmsg *rtm; struct rtmsg *rtm;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
long expires; long expires = 0;
u32 *pmetrics;
u32 table; u32 table;
nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
...@@ -4583,8 +4523,8 @@ static int rt6_fill_node(struct net *net, ...@@ -4583,8 +4523,8 @@ static int rt6_fill_node(struct net *net,
if (rt->rt6i_flags & RTF_CACHE) if (rt->rt6i_flags & RTF_CACHE)
rtm->rtm_flags |= RTM_F_CLONED; rtm->rtm_flags |= RTM_F_CLONED;
if (dst) { if (dest) {
if (nla_put_in6_addr(skb, RTA_DST, dst)) if (nla_put_in6_addr(skb, RTA_DST, dest))
goto nla_put_failure; goto nla_put_failure;
rtm->rtm_dst_len = 128; rtm->rtm_dst_len = 128;
} else if (rtm->rtm_dst_len) } else if (rtm->rtm_dst_len)
...@@ -4612,9 +4552,9 @@ static int rt6_fill_node(struct net *net, ...@@ -4612,9 +4552,9 @@ static int rt6_fill_node(struct net *net,
#endif #endif
if (nla_put_u32(skb, RTA_IIF, iif)) if (nla_put_u32(skb, RTA_IIF, iif))
goto nla_put_failure; goto nla_put_failure;
} else if (dst) { } else if (dest) {
struct in6_addr saddr_buf; struct in6_addr saddr_buf;
if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 &&
nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
goto nla_put_failure; goto nla_put_failure;
} }
...@@ -4626,10 +4566,8 @@ static int rt6_fill_node(struct net *net, ...@@ -4626,10 +4566,8 @@ static int rt6_fill_node(struct net *net,
goto nla_put_failure; goto nla_put_failure;
} }
memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics)); pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics;
if (rt->rt6i_pmtu) if (rtnetlink_put_metrics(skb, pmetrics) < 0)
metrics[RTAX_MTU - 1] = rt->rt6i_pmtu;
if (rtnetlink_put_metrics(skb, metrics) < 0)
goto nla_put_failure; goto nla_put_failure;
if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
...@@ -4661,9 +4599,10 @@ static int rt6_fill_node(struct net *net, ...@@ -4661,9 +4599,10 @@ static int rt6_fill_node(struct net *net,
goto nla_put_failure; goto nla_put_failure;
} }
expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; if (rt->rt6i_flags & RTF_EXPIRES && dst)
expires = dst->expires - jiffies;
if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
goto nla_put_failure; goto nla_put_failure;
if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags))) if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
...@@ -4697,10 +4636,9 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg) ...@@ -4697,10 +4636,9 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg)
} }
} }
return rt6_fill_node(net, return rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL, 0,
arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, RTM_NEWROUTE, NETLINK_CB(arg->cb->skb).portid,
NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, arg->cb->nlh->nlmsg_seq, NLM_F_MULTI);
NLM_F_MULTI);
} }
static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
...@@ -4814,13 +4752,14 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, ...@@ -4814,13 +4752,14 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
skb_dst_set(skb, &rt->dst); skb_dst_set(skb, &rt->dst);
if (fibmatch) if (fibmatch)
err = rt6_fill_node(net, skb, rt, NULL, NULL, iif, err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, iif,
RTM_NEWROUTE, NETLINK_CB(in_skb).portid, RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
nlh->nlmsg_seq, 0); nlh->nlmsg_seq, 0);
else else
err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, err = rt6_fill_node(net, skb, rt, dst, &fl6.daddr, &fl6.saddr,
RTM_NEWROUTE, NETLINK_CB(in_skb).portid, iif, RTM_NEWROUTE,
nlh->nlmsg_seq, 0); NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
0);
if (err < 0) { if (err < 0) {
kfree_skb(skb); kfree_skb(skb);
goto errout; goto errout;
...@@ -4846,7 +4785,7 @@ void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info, ...@@ -4846,7 +4785,7 @@ void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info,
if (!skb) if (!skb)
goto errout; goto errout;
err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
event, info->portid, seq, nlm_flags); event, info->portid, seq, nlm_flags);
if (err < 0) { if (err < 0) {
/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment