tcp: use dctcp if enabled on the route to the initiator
[firefly-linux-kernel-4.4.55.git] / net / ipv4 / fib_semantics.c
index 85e9a8abf15c274a4214057e35ebc1835525bed0..992a9597daf80d7d734d8e0245c8410dc334511b 100644 (file)
@@ -511,7 +511,8 @@ static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh,
                                        dev = __dev_get_by_index(net, cfg->fc_oif);
                                ret = lwtunnel_build_state(dev, nla_get_u16(
                                                           nla_entype),
-                                                          nla, &lwtstate);
+                                                          nla,  AF_INET, cfg,
+                                                          &lwtstate);
                                if (ret)
                                        goto errout;
                                nexthop_nh->nh_lwtstate =
@@ -533,25 +534,28 @@ errout:
 
 #endif
 
-int fib_encap_match(struct net *net, u16 encap_type,
-                   struct nlattr *encap,
-                   int oif, const struct fib_nh *nh)
+static int fib_encap_match(struct net *net, u16 encap_type,
+                          struct nlattr *encap,
+                          int oif, const struct fib_nh *nh,
+                          const struct fib_config *cfg)
 {
        struct lwtunnel_state *lwtstate;
        struct net_device *dev = NULL;
-       int ret;
+       int ret, result = 0;
 
        if (encap_type == LWTUNNEL_ENCAP_NONE)
                return 0;
 
        if (oif)
                dev = __dev_get_by_index(net, oif);
-       ret = lwtunnel_build_state(dev, encap_type,
-                                  encap, &lwtstate);
-       if (!ret)
-               return lwtunnel_cmp_encap(lwtstate, nh->nh_lwtstate);
+       ret = lwtunnel_build_state(dev, encap_type, encap,
+                                  AF_INET, cfg, &lwtstate);
+       if (!ret) {
+               result = lwtunnel_cmp_encap(lwtstate, nh->nh_lwtstate);
+               lwtstate_free(lwtstate);
+       }
 
-       return 0;
+       return result;
 }
 
 int fib_nh_match(struct fib_config *cfg, struct fib_info *fi)
@@ -569,7 +573,7 @@ int fib_nh_match(struct fib_config *cfg, struct fib_info *fi)
                if (cfg->fc_encap) {
                        if (fib_encap_match(net, cfg->fc_encap_type,
                                            cfg->fc_encap, cfg->fc_oif,
-                                           fi->fib_nh))
+                                           fi->fib_nh, cfg))
                            return 1;
                }
                if ((!cfg->fc_oif || cfg->fc_oif == fi->fib_nh->nh_oif) &&
@@ -661,7 +665,7 @@ int fib_nh_match(struct fib_config *cfg, struct fib_info *fi)
 static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
                        struct fib_nh *nh)
 {
-       int err;
+       int err = 0;
        struct net *net;
        struct net_device *dev;
 
@@ -691,6 +695,7 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
                }
                rcu_read_lock();
                {
+                       struct fib_table *tbl = NULL;
                        struct flowi4 fl4 = {
                                .daddr = nh->nh_gw,
                                .flowi4_scope = cfg->fc_scope + 1,
@@ -701,8 +706,24 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
                        /* It is not necessary, but requires a bit of thinking */
                        if (fl4.flowi4_scope < RT_SCOPE_LINK)
                                fl4.flowi4_scope = RT_SCOPE_LINK;
-                       err = fib_lookup(net, &fl4, &res,
-                                        FIB_LOOKUP_IGNORE_LINKSTATE);
+
+                       if (cfg->fc_table)
+                               tbl = fib_get_table(net, cfg->fc_table);
+
+                       if (tbl)
+                               err = fib_table_lookup(tbl, &fl4, &res,
+                                                      FIB_LOOKUP_IGNORE_LINKSTATE |
+                                                      FIB_LOOKUP_NOREF);
+
+                       /* on error or if no table given do full lookup. This
+                        * is needed for example when nexthops are in the local
+                        * table rather than the given table
+                        */
+                       if (!tbl || err) {
+                               err = fib_lookup(net, &fl4, &res,
+                                                FIB_LOOKUP_IGNORE_LINKSTATE);
+                       }
+
                        if (err) {
                                rcu_read_unlock();
                                return err;
@@ -855,6 +876,50 @@ static bool fib_valid_prefsrc(struct fib_config *cfg, __be32 fib_prefsrc)
        return true;
 }
 
+static int
+fib_convert_metrics(struct fib_info *fi, const struct fib_config *cfg)
+{
+       bool ecn_ca = false;
+       struct nlattr *nla;
+       int remaining;
+
+       if (!cfg->fc_mx)
+               return 0;
+
+       nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
+               int type = nla_type(nla);
+               u32 val;
+
+               if (!type)
+                       continue;
+               if (type > RTAX_MAX)
+                       return -EINVAL;
+
+               if (type == RTAX_CC_ALGO) {
+                       char tmp[TCP_CA_NAME_MAX];
+
+                       nla_strlcpy(tmp, nla, sizeof(tmp));
+                       val = tcp_ca_get_key_by_name(tmp, &ecn_ca);
+                       if (val == TCP_CA_UNSPEC)
+                               return -EINVAL;
+               } else {
+                       val = nla_get_u32(nla);
+               }
+               if (type == RTAX_ADVMSS && val > 65535 - 40)
+                       val = 65535 - 40;
+               if (type == RTAX_MTU && val > 65535 - 15)
+                       val = 65535 - 15;
+               if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK))
+                       return -EINVAL;
+               fi->fib_metrics[type - 1] = val;
+       }
+
+       if (ecn_ca)
+               fi->fib_metrics[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA;
+
+       return 0;
+}
+
 struct fib_info *fib_create_info(struct fib_config *cfg)
 {
        int err;
@@ -927,36 +992,9 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
                        goto failure;
        } endfor_nexthops(fi)
 
-       if (cfg->fc_mx) {
-               struct nlattr *nla;
-               int remaining;
-
-               nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
-                       int type = nla_type(nla);
-
-                       if (type) {
-                               u32 val;
-
-                               if (type > RTAX_MAX)
-                                       goto err_inval;
-                               if (type == RTAX_CC_ALGO) {
-                                       char tmp[TCP_CA_NAME_MAX];
-
-                                       nla_strlcpy(tmp, nla, sizeof(tmp));
-                                       val = tcp_ca_get_key_by_name(tmp);
-                                       if (val == TCP_CA_UNSPEC)
-                                               goto err_inval;
-                               } else {
-                                       val = nla_get_u32(nla);
-                               }
-                               if (type == RTAX_ADVMSS && val > 65535 - 40)
-                                       val = 65535 - 40;
-                               if (type == RTAX_MTU && val > 65535 - 15)
-                                       val = 65535 - 15;
-                               fi->fib_metrics[type - 1] = val;
-                       }
-               }
-       }
+       err = fib_convert_metrics(fi, cfg);
+       if (err)
+               goto failure;
 
        if (cfg->fc_mp) {
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
@@ -986,7 +1024,8 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
                        if (cfg->fc_oif)
                                dev = __dev_get_by_index(net, cfg->fc_oif);
                        err = lwtunnel_build_state(dev, cfg->fc_encap_type,
-                                                  cfg->fc_encap, &lwtstate);
+                                                  cfg->fc_encap, AF_INET, cfg,
+                                                  &lwtstate);
                        if (err)
                                goto failure;