]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/ipv4/fib_semantics.c
Merge tag 'platform-drivers-x86-v4.13-1' of git://git.infradead.org/linux-platform...
[karo-tx-linux.git] / net / ipv4 / fib_semantics.c
index ad9ad4aab5da7c7d11c3b80edbdfcbdd3d7153fe..2221001038084af89b6f9fa0e306c2320ece4a3f 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/skbuff.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/netlink.h>
 
 #include <net/arp.h>
 #include <net/ip.h>
@@ -151,7 +152,8 @@ static void rt_fibinfo_free(struct rtable __rcu **rtp)
         * free_fib_info_rcu()
         */
 
-       dst_free(&rt->dst);
+       dst_dev_put(&rt->dst);
+       dst_release_immediate(&rt->dst);
 }
 
 static void free_nh_exceptions(struct fib_nh *nh)
@@ -193,8 +195,10 @@ static void rt_fibinfo_free_cpus(struct rtable __rcu * __percpu *rtp)
                struct rtable *rt;
 
                rt = rcu_dereference_protected(*per_cpu_ptr(rtp, cpu), 1);
-               if (rt)
-                       dst_free(&rt->dst);
+               if (rt) {
+                       dst_dev_put(&rt->dst);
+                       dst_release_immediate(&rt->dst);
+               }
        }
        free_percpu(rtp);
 }
@@ -456,7 +460,8 @@ static int fib_detect_death(struct fib_info *fi, int order,
 
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
 
-static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining)
+static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining,
+                             struct netlink_ext_ack *extack)
 {
        int nhs = 0;
 
@@ -466,22 +471,35 @@ static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining)
        }
 
        /* leftover implies invalid nexthop configuration, discard it */
-       return remaining > 0 ? 0 : nhs;
+       if (remaining > 0) {
+               NL_SET_ERR_MSG(extack,
+                              "Invalid nexthop configuration - extra data after nexthops");
+               nhs = 0;
+       }
+
+       return nhs;
 }
 
 static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh,
-                      int remaining, struct fib_config *cfg)
+                      int remaining, struct fib_config *cfg,
+                      struct netlink_ext_ack *extack)
 {
        int ret;
 
        change_nexthops(fi) {
                int attrlen;
 
-               if (!rtnh_ok(rtnh, remaining))
+               if (!rtnh_ok(rtnh, remaining)) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Invalid nexthop configuration - extra data after nexthop");
                        return -EINVAL;
+               }
 
-               if (rtnh->rtnh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
+               if (rtnh->rtnh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Invalid flags for nexthop - can not contain DEAD or LINKDOWN");
                        return -EINVAL;
+               }
 
                nexthop_nh->nh_flags =
                        (cfg->fc_flags & ~0xFF) | rtnh->rtnh_flags;
@@ -507,13 +525,17 @@ static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh,
 
                                nla_entype = nla_find(attrs, attrlen,
                                                      RTA_ENCAP_TYPE);
-                               if (!nla_entype)
+                               if (!nla_entype) {
+                                       NL_SET_BAD_ATTR(extack, nla);
+                                       NL_SET_ERR_MSG(extack,
+                                                      "Encap type is missing");
                                        goto err_inval;
+                               }
 
                                ret = lwtunnel_build_state(nla_get_u16(
                                                           nla_entype),
                                                           nla,  AF_INET, cfg,
-                                                          &lwtstate);
+                                                          &lwtstate, extack);
                                if (ret)
                                        goto errout;
                                nexthop_nh->nh_lwtstate =
@@ -595,7 +617,8 @@ static inline void fib_add_weight(struct fib_info *fi,
 static int fib_encap_match(u16 encap_type,
                           struct nlattr *encap,
                           const struct fib_nh *nh,
-                          const struct fib_config *cfg)
+                          const struct fib_config *cfg,
+                          struct netlink_ext_ack *extack)
 {
        struct lwtunnel_state *lwtstate;
        int ret, result = 0;
@@ -603,8 +626,8 @@ static int fib_encap_match(u16 encap_type,
        if (encap_type == LWTUNNEL_ENCAP_NONE)
                return 0;
 
-       ret = lwtunnel_build_state(encap_type, encap,
-                                  AF_INET, cfg, &lwtstate);
+       ret = lwtunnel_build_state(encap_type, encap, AF_INET,
+                                  cfg, &lwtstate, extack);
        if (!ret) {
                result = lwtunnel_cmp_encap(lwtstate, nh->nh_lwtstate);
                lwtstate_free(lwtstate);
@@ -613,7 +636,8 @@ static int fib_encap_match(u16 encap_type,
        return result;
 }
 
-int fib_nh_match(struct fib_config *cfg, struct fib_info *fi)
+int fib_nh_match(struct fib_config *cfg, struct fib_info *fi,
+                struct netlink_ext_ack *extack)
 {
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
        struct rtnexthop *rtnh;
@@ -625,9 +649,9 @@ int fib_nh_match(struct fib_config *cfg, struct fib_info *fi)
 
        if (cfg->fc_oif || cfg->fc_gw) {
                if (cfg->fc_encap) {
-                       if (fib_encap_match(cfg->fc_encap_type,
-                                           cfg->fc_encap, fi->fib_nh, cfg))
-                           return 1;
+                       if (fib_encap_match(cfg->fc_encap_type, cfg->fc_encap,
+                                           fi->fib_nh, cfg, extack))
+                               return 1;
                }
                if ((!cfg->fc_oif || cfg->fc_oif == fi->fib_nh->nh_oif) &&
                    (!cfg->fc_gw  || cfg->fc_gw == fi->fib_nh->nh_gw))
@@ -716,7 +740,7 @@ int fib_nh_match(struct fib_config *cfg, struct fib_info *fi)
  *                                     |-> {local prefix} (terminal node)
  */
 static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
-                       struct fib_nh *nh)
+                       struct fib_nh *nh, struct netlink_ext_ack *extack)
 {
        int err = 0;
        struct net *net;
@@ -729,16 +753,25 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
                if (nh->nh_flags & RTNH_F_ONLINK) {
                        unsigned int addr_type;
 
-                       if (cfg->fc_scope >= RT_SCOPE_LINK)
+                       if (cfg->fc_scope >= RT_SCOPE_LINK) {
+                               NL_SET_ERR_MSG(extack,
+                                              "Nexthop has invalid scope");
                                return -EINVAL;
+                       }
                        dev = __dev_get_by_index(net, nh->nh_oif);
                        if (!dev)
                                return -ENODEV;
-                       if (!(dev->flags & IFF_UP))
+                       if (!(dev->flags & IFF_UP)) {
+                               NL_SET_ERR_MSG(extack,
+                                              "Nexthop device is not up");
                                return -ENETDOWN;
+                       }
                        addr_type = inet_addr_type_dev_table(net, dev, nh->nh_gw);
-                       if (addr_type != RTN_UNICAST)
+                       if (addr_type != RTN_UNICAST) {
+                               NL_SET_ERR_MSG(extack,
+                                              "Nexthop has invalid gateway");
                                return -EINVAL;
+                       }
                        if (!netif_carrier_ok(dev))
                                nh->nh_flags |= RTNH_F_LINKDOWN;
                        nh->nh_dev = dev;
@@ -778,18 +811,25 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
                        }
 
                        if (err) {
+                               NL_SET_ERR_MSG(extack,
+                                              "Nexthop has invalid gateway");
                                rcu_read_unlock();
                                return err;
                        }
                }
                err = -EINVAL;
-               if (res.type != RTN_UNICAST && res.type != RTN_LOCAL)
+               if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) {
+                       NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway");
                        goto out;
+               }
                nh->nh_scope = res.scope;
                nh->nh_oif = FIB_RES_OIF(res);
                nh->nh_dev = dev = FIB_RES_DEV(res);
-               if (!dev)
+               if (!dev) {
+                       NL_SET_ERR_MSG(extack,
+                                      "No egress device for nexthop gateway");
                        goto out;
+               }
                dev_hold(dev);
                if (!netif_carrier_ok(dev))
                        nh->nh_flags |= RTNH_F_LINKDOWN;
@@ -797,17 +837,21 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
        } else {
                struct in_device *in_dev;
 
-               if (nh->nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK))
+               if (nh->nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK)) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Invalid flags for nexthop - PERVASIVE and ONLINK can not be set");
                        return -EINVAL;
-
+               }
                rcu_read_lock();
                err = -ENODEV;
                in_dev = inetdev_by_index(net, nh->nh_oif);
                if (!in_dev)
                        goto out;
                err = -ENETDOWN;
-               if (!(in_dev->dev->flags & IFF_UP))
+               if (!(in_dev->dev->flags & IFF_UP)) {
+                       NL_SET_ERR_MSG(extack, "Device for nexthop is not up");
                        goto out;
+               }
                nh->nh_dev = in_dev->dev;
                dev_hold(nh->nh_dev);
                nh->nh_scope = RT_SCOPE_HOST;
@@ -982,7 +1026,8 @@ fib_convert_metrics(struct fib_info *fi, const struct fib_config *cfg)
        return 0;
 }
 
-struct fib_info *fib_create_info(struct fib_config *cfg)
+struct fib_info *fib_create_info(struct fib_config *cfg,
+                                struct netlink_ext_ack *extack)
 {
        int err;
        struct fib_info *fi = NULL;
@@ -994,15 +1039,20 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
                goto err_inval;
 
        /* Fast check to catch the most weird cases */
-       if (fib_props[cfg->fc_type].scope > cfg->fc_scope)
+       if (fib_props[cfg->fc_type].scope > cfg->fc_scope) {
+               NL_SET_ERR_MSG(extack, "Invalid scope");
                goto err_inval;
+       }
 
-       if (cfg->fc_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
+       if (cfg->fc_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) {
+               NL_SET_ERR_MSG(extack,
+                              "Invalid rtm_flags - can not contain DEAD or LINKDOWN");
                goto err_inval;
+       }
 
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
        if (cfg->fc_mp) {
-               nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len);
+               nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len, extack);
                if (nhs == 0)
                        goto err_inval;
        }
@@ -1065,18 +1115,29 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
 
        if (cfg->fc_mp) {
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
-               err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg);
+               err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg, extack);
                if (err != 0)
                        goto failure;
-               if (cfg->fc_oif && fi->fib_nh->nh_oif != cfg->fc_oif)
+               if (cfg->fc_oif && fi->fib_nh->nh_oif != cfg->fc_oif) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Nexthop device index does not match RTA_OIF");
                        goto err_inval;
-               if (cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw)
+               }
+               if (cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Nexthop gateway does not match RTA_GATEWAY");
                        goto err_inval;
+               }
 #ifdef CONFIG_IP_ROUTE_CLASSID
-               if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow)
+               if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Nexthop class id does not match RTA_FLOW");
                        goto err_inval;
+               }
 #endif
 #else
+               NL_SET_ERR_MSG(extack,
+                              "Multipath support not enabled in kernel");
                goto err_inval;
 #endif
        } else {
@@ -1085,11 +1146,14 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
                if (cfg->fc_encap) {
                        struct lwtunnel_state *lwtstate;
 
-                       if (cfg->fc_encap_type == LWTUNNEL_ENCAP_NONE)
+                       if (cfg->fc_encap_type == LWTUNNEL_ENCAP_NONE) {
+                               NL_SET_ERR_MSG(extack,
+                                              "LWT encap type not specified");
                                goto err_inval;
+                       }
                        err = lwtunnel_build_state(cfg->fc_encap_type,
                                                   cfg->fc_encap, AF_INET, cfg,
-                                                  &lwtstate);
+                                                  &lwtstate, extack);
                        if (err)
                                goto failure;
 
@@ -1109,8 +1173,11 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
        }
 
        if (fib_props[cfg->fc_type].error) {
-               if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp)
+               if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Gateway, device and multipath can not be specified for this route type");
                        goto err_inval;
+               }
                goto link_it;
        } else {
                switch (cfg->fc_type) {
@@ -1121,19 +1188,30 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
                case RTN_MULTICAST:
                        break;
                default:
+                       NL_SET_ERR_MSG(extack, "Invalid route type");
                        goto err_inval;
                }
        }
 
-       if (cfg->fc_scope > RT_SCOPE_HOST)
+       if (cfg->fc_scope > RT_SCOPE_HOST) {
+               NL_SET_ERR_MSG(extack, "Invalid scope");
                goto err_inval;
+       }
 
        if (cfg->fc_scope == RT_SCOPE_HOST) {
                struct fib_nh *nh = fi->fib_nh;
 
                /* Local address is added. */
-               if (nhs != 1 || nh->nh_gw)
+               if (nhs != 1) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Route with host scope can not have multiple nexthops");
                        goto err_inval;
+               }
+               if (nh->nh_gw) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Route with host scope can not have a gateway");
+                       goto err_inval;
+               }
                nh->nh_scope = RT_SCOPE_NOWHERE;
                nh->nh_dev = dev_get_by_index(net, fi->fib_nh->nh_oif);
                err = -ENODEV;
@@ -1143,7 +1221,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
                int linkdown = 0;
 
                change_nexthops(fi) {
-                       err = fib_check_nh(cfg, fi, nexthop_nh);
+                       err = fib_check_nh(cfg, fi, nexthop_nh, extack);
                        if (err != 0)
                                goto failure;
                        if (nexthop_nh->nh_flags & RTNH_F_LINKDOWN)
@@ -1153,8 +1231,10 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
                        fi->fib_flags |= RTNH_F_LINKDOWN;
        }
 
-       if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc))
+       if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) {
+               NL_SET_ERR_MSG(extack, "Invalid prefsrc address");
                goto err_inval;
+       }
 
        change_nexthops(fi) {
                fib_info_update_nh_saddr(net, nexthop_nh);
@@ -1173,7 +1253,7 @@ link_it:
        }
 
        fi->fib_treeref++;
-       atomic_inc(&fi->fib_clntref);
+       refcount_set(&fi->fib_clntref, 1);
        spin_lock_bh(&fib_info_lock);
        hlist_add_head(&fi->fib_hash,
                       &fib_info_hash[fib_info_hashfn(fi)]);