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 0 : struct nlattr *nla; 15 0 : 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 0 : int type = nla_type(nla); 22 0 : 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 0 : return -EINVAL; 29 : } 30 : 31 0 : if (type == RTAX_CC_ALGO) { 32 0 : char tmp[TCP_CA_NAME_MAX]; 33 : 34 0 : nla_strscpy(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 0 : return -EINVAL; 45 : } 46 0 : val = nla_get_u32(nla); 47 : } 48 0 : if (type == RTAX_ADVMSS && val > 65535 - 40) 49 0 : val = 65535 - 40; 50 0 : if (type == RTAX_MTU && val > 65535 - 15) 51 0 : val = 65535 - 15; 52 0 : if (type == RTAX_HOPLIMIT && val > 255) 53 0 : 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 0 : 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 12 : 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 12 : struct dst_metrics *fib_metrics; 72 12 : int err; 73 : 74 12 : 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 12 : 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 0 : refcount_set(&fib_metrics->refcnt, 1); 85 : } else { 86 0 : kfree(fib_metrics); 87 0 : fib_metrics = ERR_PTR(err); 88 : } 89 : 90 : return fib_metrics; 91 : } 92 : EXPORT_SYMBOL_GPL(ip_fib_metrics_init);