Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 #include <linux/netlink.h>
0003 #include <linux/rtnetlink.h>
0004 #include <linux/types.h>
0005 #include <net/ip.h>
0006 #include <net/net_namespace.h>
0007 #include <net/tcp.h>
0008 
0009 static int ip_metrics_convert(struct net *net, struct nlattr *fc_mx,
0010                   int fc_mx_len, u32 *metrics,
0011                   struct netlink_ext_ack *extack)
0012 {
0013     bool ecn_ca = false;
0014     struct nlattr *nla;
0015     int remaining;
0016 
0017     if (!fc_mx)
0018         return 0;
0019 
0020     nla_for_each_attr(nla, fc_mx, fc_mx_len, remaining) {
0021         int type = nla_type(nla);
0022         u32 val;
0023 
0024         if (!type)
0025             continue;
0026         if (type > RTAX_MAX) {
0027             NL_SET_ERR_MSG(extack, "Invalid metric type");
0028             return -EINVAL;
0029         }
0030 
0031         if (type == RTAX_CC_ALGO) {
0032             char tmp[TCP_CA_NAME_MAX];
0033 
0034             nla_strscpy(tmp, nla, sizeof(tmp));
0035             val = tcp_ca_get_key_by_name(net, tmp, &ecn_ca);
0036             if (val == TCP_CA_UNSPEC) {
0037                 NL_SET_ERR_MSG(extack, "Unknown tcp congestion algorithm");
0038                 return -EINVAL;
0039             }
0040         } else {
0041             if (nla_len(nla) != sizeof(u32)) {
0042                 NL_SET_ERR_MSG_ATTR(extack, nla,
0043                             "Invalid attribute in metrics");
0044                 return -EINVAL;
0045             }
0046             val = nla_get_u32(nla);
0047         }
0048         if (type == RTAX_ADVMSS && val > 65535 - 40)
0049             val = 65535 - 40;
0050         if (type == RTAX_MTU && val > 65535 - 15)
0051             val = 65535 - 15;
0052         if (type == RTAX_HOPLIMIT && val > 255)
0053             val = 255;
0054         if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK)) {
0055             NL_SET_ERR_MSG(extack, "Unknown flag set in feature mask in metrics attribute");
0056             return -EINVAL;
0057         }
0058         metrics[type - 1] = val;
0059     }
0060 
0061     if (ecn_ca)
0062         metrics[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA;
0063 
0064     return 0;
0065 }
0066 
0067 struct dst_metrics *ip_fib_metrics_init(struct net *net, struct nlattr *fc_mx,
0068                     int fc_mx_len,
0069                     struct netlink_ext_ack *extack)
0070 {
0071     struct dst_metrics *fib_metrics;
0072     int err;
0073 
0074     if (!fc_mx)
0075         return (struct dst_metrics *)&dst_default_metrics;
0076 
0077     fib_metrics = kzalloc(sizeof(*fib_metrics), GFP_KERNEL);
0078     if (unlikely(!fib_metrics))
0079         return ERR_PTR(-ENOMEM);
0080 
0081     err = ip_metrics_convert(net, fc_mx, fc_mx_len, fib_metrics->metrics,
0082                  extack);
0083     if (!err) {
0084         refcount_set(&fib_metrics->refcnt, 1);
0085     } else {
0086         kfree(fib_metrics);
0087         fib_metrics = ERR_PTR(err);
0088     }
0089 
0090     return fib_metrics;
0091 }
0092 EXPORT_SYMBOL_GPL(ip_fib_metrics_init);