2019-05-19 15:08:55 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2018-04-18 03:33:07 +03:00
# include <linux/netlink.h>
# include <linux/rtnetlink.h>
# include <linux/types.h>
# include <net/ip.h>
# include <net/net_namespace.h>
# include <net/tcp.h>
2018-10-05 06:07:51 +03:00
static int ip_metrics_convert ( struct net * net , struct nlattr * fc_mx ,
2018-11-06 23:51:15 +03:00
int fc_mx_len , u32 * metrics ,
struct netlink_ext_ack * extack )
2018-04-18 03:33:07 +03:00
{
bool ecn_ca = false ;
struct nlattr * nla ;
int remaining ;
nla_for_each_attr ( nla , fc_mx , fc_mx_len , remaining ) {
int type = nla_type ( nla ) ;
u32 val ;
if ( ! type )
continue ;
2018-11-06 23:51:15 +03:00
if ( type > RTAX_MAX ) {
NL_SET_ERR_MSG ( extack , " Invalid metric type " ) ;
2018-04-18 03:33:07 +03:00
return - EINVAL ;
2018-11-06 23:51:15 +03:00
}
2018-04-18 03:33:07 +03:00
if ( type = = RTAX_CC_ALGO ) {
char tmp [ TCP_CA_NAME_MAX ] ;
2020-11-15 20:08:06 +03:00
nla_strscpy ( tmp , nla , sizeof ( tmp ) ) ;
2018-04-18 03:33:07 +03:00
val = tcp_ca_get_key_by_name ( net , tmp , & ecn_ca ) ;
2018-11-06 23:51:15 +03:00
if ( val = = TCP_CA_UNSPEC ) {
NL_SET_ERR_MSG ( extack , " Unknown tcp congestion algorithm " ) ;
2018-04-18 03:33:07 +03:00
return - EINVAL ;
2018-11-06 23:51:15 +03:00
}
2018-04-18 03:33:07 +03:00
} else {
2018-11-06 23:51:15 +03:00
if ( nla_len ( nla ) ! = sizeof ( u32 ) ) {
NL_SET_ERR_MSG_ATTR ( extack , nla ,
" Invalid attribute in metrics " ) ;
2018-06-05 16:06:19 +03:00
return - EINVAL ;
2018-11-06 23:51:15 +03:00
}
2018-04-18 03:33:07 +03:00
val = nla_get_u32 ( nla ) ;
}
if ( type = = RTAX_ADVMSS & & val > 65535 - 40 )
val = 65535 - 40 ;
if ( type = = RTAX_MTU & & val > 65535 - 15 )
val = 65535 - 15 ;
if ( type = = RTAX_HOPLIMIT & & val > 255 )
val = 255 ;
2018-11-06 23:51:15 +03:00
if ( type = = RTAX_FEATURES & & ( val & ~ RTAX_FEATURE_MASK ) ) {
NL_SET_ERR_MSG ( extack , " Unknown flag set in feature mask in metrics attribute " ) ;
2018-04-18 03:33:07 +03:00
return - EINVAL ;
2018-11-06 23:51:15 +03:00
}
2018-04-18 03:33:07 +03:00
metrics [ type - 1 ] = val ;
}
if ( ecn_ca )
metrics [ RTAX_FEATURES - 1 ] | = DST_FEATURE_ECN_CA ;
return 0 ;
}
2018-10-05 06:07:51 +03:00
struct dst_metrics * ip_fib_metrics_init ( struct net * net , struct nlattr * fc_mx ,
2018-11-06 23:51:15 +03:00
int fc_mx_len ,
struct netlink_ext_ack * extack )
2018-10-05 06:07:51 +03:00
{
struct dst_metrics * fib_metrics ;
int err ;
if ( ! fc_mx )
return ( struct dst_metrics * ) & dst_default_metrics ;
fib_metrics = kzalloc ( sizeof ( * fib_metrics ) , GFP_KERNEL ) ;
if ( unlikely ( ! fib_metrics ) )
return ERR_PTR ( - ENOMEM ) ;
2018-11-06 23:51:15 +03:00
err = ip_metrics_convert ( net , fc_mx , fc_mx_len , fib_metrics - > metrics ,
extack ) ;
2018-10-05 06:07:51 +03:00
if ( ! err ) {
refcount_set ( & fib_metrics - > refcnt , 1 ) ;
} else {
kfree ( fib_metrics ) ;
fib_metrics = ERR_PTR ( err ) ;
}
return fib_metrics ;
}
EXPORT_SYMBOL_GPL ( ip_fib_metrics_init ) ;