Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only 2 : : #include <linux/netlink.h> 3 : : #include <linux/rtnetlink.h> 4 : : #include <linux/types.h> 5 : : #include <net/ip.h> 6 : : #include <net/net_namespace.h> 7 : : #include <net/tcp.h> 8 : : 9 : 0 : static int ip_metrics_convert(struct net *net, struct nlattr *fc_mx, 10 : : int fc_mx_len, u32 *metrics, 11 : : struct netlink_ext_ack *extack) 12 : : { 13 : 0 : bool ecn_ca = false; 14 : : struct nlattr *nla; 15 : : int remaining; 16 : : 17 : 0 : if (!fc_mx) 18 : : return 0; 19 : : 20 : 0 : nla_for_each_attr(nla, fc_mx, fc_mx_len, remaining) { 21 : : int type = nla_type(nla); 22 : : u32 val; 23 : : 24 : 0 : if (!type) 25 : 0 : continue; 26 : 0 : if (type > RTAX_MAX) { 27 : 0 : NL_SET_ERR_MSG(extack, "Invalid metric type"); 28 : : return -EINVAL; 29 : : } 30 : : 31 : 0 : if (type == RTAX_CC_ALGO) { 32 : : char tmp[TCP_CA_NAME_MAX]; 33 : : 34 : 0 : nla_strlcpy(tmp, nla, sizeof(tmp)); 35 : 0 : val = tcp_ca_get_key_by_name(net, tmp, &ecn_ca); 36 : 0 : if (val == TCP_CA_UNSPEC) { 37 : 0 : NL_SET_ERR_MSG(extack, "Unknown tcp congestion algorithm"); 38 : 0 : return -EINVAL; 39 : : } 40 : : } else { 41 : 0 : if (nla_len(nla) != sizeof(u32)) { 42 : 0 : NL_SET_ERR_MSG_ATTR(extack, nla, 43 : : "Invalid attribute in metrics"); 44 : : return -EINVAL; 45 : : } 46 : : val = nla_get_u32(nla); 47 : : } 48 : 0 : if (type == RTAX_ADVMSS && val > 65535 - 40) 49 : : val = 65535 - 40; 50 : 0 : if (type == RTAX_MTU && val > 65535 - 15) 51 : : val = 65535 - 15; 52 : 0 : if (type == RTAX_HOPLIMIT && val > 255) 53 : : val = 255; 54 : 0 : if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK)) { 55 : 0 : NL_SET_ERR_MSG(extack, "Unknown flag set in feature mask in metrics attribute"); 56 : : return -EINVAL; 57 : : } 58 : 0 : metrics[type - 1] = val; 59 : : } 60 : : 61 : 0 : if (ecn_ca) 62 : 0 : metrics[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA; 63 : : 64 : : return 0; 65 : : } 66 : : 67 : 3 : struct dst_metrics *ip_fib_metrics_init(struct net *net, struct nlattr *fc_mx, 68 : : int fc_mx_len, 69 : : struct netlink_ext_ack *extack) 70 : : { 71 : : struct dst_metrics *fib_metrics; 72 : : int err; 73 : : 74 : 3 : if (!fc_mx) 75 : : return (struct dst_metrics *)&dst_default_metrics; 76 : : 77 : 0 : fib_metrics = kzalloc(sizeof(*fib_metrics), GFP_KERNEL); 78 : 0 : if (unlikely(!fib_metrics)) 79 : : return ERR_PTR(-ENOMEM); 80 : : 81 : 0 : err = ip_metrics_convert(net, fc_mx, fc_mx_len, fib_metrics->metrics, 82 : : extack); 83 : 0 : if (!err) { 84 : : refcount_set(&fib_metrics->refcnt, 1); 85 : : } else { 86 : 0 : kfree(fib_metrics); 87 : : fib_metrics = ERR_PTR(err); 88 : : } 89 : : 90 : 0 : return fib_metrics; 91 : : } 92 : : EXPORT_SYMBOL_GPL(ip_fib_metrics_init);