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