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

Reply via email to