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