From: David Ahern <dsah...@gmail.com> Add nexthop reference to fib_info along with a list_head for tracking the association of nexthop back to the fib_info.
Add helpers to take a fib_info and return a fib_nh, a nexthop device and nexthop gateway. Add helper to validate a nexthop works with a fib_info. Signed-off-by: David Ahern <dsah...@gmail.com> --- include/net/ip_fib.h | 4 ++++ include/net/nexthop.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/nexthop.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 0b40c59b8a5f..e39f55f3c3d8 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -103,9 +103,12 @@ struct fib_nh { * This structure contains data shared by many of routes. */ +struct nexthop; + struct fib_info { struct hlist_node fib_hash; struct hlist_node fib_lhash; + struct list_head nh_list; struct net *fib_net; int fib_treeref; refcount_t fib_clntref; @@ -122,6 +125,7 @@ struct fib_info { #define fib_window fib_metrics->metrics[RTAX_WINDOW-1] #define fib_rtt fib_metrics->metrics[RTAX_RTT-1] #define fib_advmss fib_metrics->metrics[RTAX_ADVMSS-1] + struct nexthop *nh; int fib_nhs; struct rcu_head rcu; struct fib_nh fib_nh[0]; diff --git a/include/net/nexthop.h b/include/net/nexthop.h index 1c59d04d1da6..c149fe8394ab 100644 --- a/include/net/nexthop.h +++ b/include/net/nexthop.h @@ -118,4 +118,50 @@ static inline bool nexthop_is_blackhole(struct nexthop *nh) nhi = rcu_dereference(nh->nh_info); return !!nhi->reject_nh; } + +static inline struct fib_nh *nexthop_fib_nh(struct nexthop *nh, int nhsel) +{ + struct nh_info *nhi; + + nhi = rcu_dereference(nh->nh_info); + if (nhi->family == AF_INET || + nhi->family == AF_UNSPEC) /* dev only re-uses IPv4 struct */ + return &nhi->fib_nh; + + return NULL; +} + +static inline struct fib_nh *fib_info_nh(struct fib_info *fi, int nhsel) +{ + if (fi->nh) + return nexthop_fib_nh(fi->nh, 0); + + WARN_ON(nhsel > fi->fib_nhs); + return &fi->fib_nh[nhsel]; +} + +/* return fib_nh for fib_info; for historical reasons + * returns first nexthop only + */ +static inline struct net_device *fib_info_nh_dev(struct fib_info *fi) +{ + struct fib_nh *fib_nh = fib_info_nh(fi, 0); + + return fib_nh->nh_dev; +} + +/* return gateway for fib_info; for historical reasons + * returns gateway for first nexthop if multipath + */ +static inline __be32 fib_info_nh_gw(struct fib_info *fi) +{ + struct fib_nh *fib_nh = fib_info_nh(fi, 0); + + return fib_nh ? fib_nh->nh_gw : 0; +} + +int fib_check_nexthop(struct fib_info *fi, struct fib_config *cfg, + struct netlink_ext_ack *extack); + +bool nexthop_uses_dev(const struct nexthop *nh, const struct net_device *dev); #endif diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index 24c4aa383c9d..d1fc3d21af86 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -315,6 +315,21 @@ static void nexthop_notify(int event, struct nexthop *nh, struct nl_info *info) rtnl_set_sk_err(info->nl_net, RTNLGRP_IPV4_ROUTE, err); } +static void __remove_nexthop_fib(struct net *net, struct nexthop *nh) +{ + struct fib_info *fi; + bool do_flush; + + do_flush = false; + list_for_each_entry(fi, &nh->fi_list, nh_list) { + fi->fib_flags |= RTNH_F_DEAD; + do_flush = true; + } + + if (do_flush) + fib_flush(net); +} + /* called on insert failure too */ static void __remove_nexthop(struct net *net, struct nexthop *nh, bool skip_fib, struct nl_info *nlinfo) @@ -326,6 +341,8 @@ static void __remove_nexthop(struct net *net, struct nexthop *nh, dev = nh_info_dev(nhi); if (dev) hlist_del(&nhi->dev_hash); + if (!skip_fib) + __remove_nexthop_fib(net, nh); } static void remove_nexthop(struct net *net, struct nexthop *nh, @@ -461,6 +478,28 @@ static void flush_all_nexthops(struct net *net) } } +/* invoked by fib add code to verify nexthop by id is ok with + * config for prefix; parts of fib_check_nh not done when nexthop + * is created + */ +int fib_check_nexthop(struct fib_info *fi, struct fib_config *cfg, + struct netlink_ext_ack *extack) +{ + struct nexthop *nh = fi->nh; + struct nh_info *nhi; + + nhi = rtnl_dereference(nh->nh_info); + if (nhi->family != AF_UNSPEC) { + if (nh->nh_flags & RTNH_F_ONLINK && + cfg->fc_scope >= RT_SCOPE_LINK) { + NL_SET_ERR_MSG(extack, "Scope mismatch with nexthop"); + return -EINVAL; + } + } + + return 0; +} + static int nh_check_attr(struct nhmsg *nhm, struct nlattr *tb[], struct net *net, struct netlink_ext_ack *extack) { -- 2.11.0