Commit ea697639 authored by Daniel Borkmann's avatar Daniel Borkmann Committed by David S. Miller

net: tcp: add RTAX_CC_ALGO fib handling

This patch adds the minimum necessary for the RTAX_CC_ALGO congestion
control metric to be set up and dumped back to user space.

While the internal representation of RTAX_CC_ALGO is handled as a u32
key, we avoided to expose this implementation detail to user space, thus
instead, we chose the netlink attribute that is being exchanged between
user space to be the actual congestion control algorithm name, similarly
as in the setsockopt(2) API in order to allow for maximum flexibility,
even for 3rd party modules.

It is a bit unfortunate that RTAX_QUICKACK used up a whole RTAX slot as
it should have been stored in RTAX_FEATURES instead, we first thought
about reusing it for the congestion control key, but it brings more
complications and/or confusion than worth it.

Joint work with Florian Westphal.
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarDaniel Borkmann <dborkman@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c5c6a8ab
...@@ -846,7 +846,14 @@ extern struct tcp_congestion_ops tcp_reno; ...@@ -846,7 +846,14 @@ extern struct tcp_congestion_ops tcp_reno;
struct tcp_congestion_ops *tcp_ca_find_key(u32 key); struct tcp_congestion_ops *tcp_ca_find_key(u32 key);
u32 tcp_ca_get_key_by_name(const char *name); u32 tcp_ca_get_key_by_name(const char *name);
#ifdef CONFIG_INET
char *tcp_ca_get_name_by_key(u32 key, char *buffer); char *tcp_ca_get_name_by_key(u32 key, char *buffer);
#else
static inline char *tcp_ca_get_name_by_key(u32 key, char *buffer)
{
return NULL;
}
#endif
static inline bool tcp_ca_needs_ecn(const struct sock *sk) static inline bool tcp_ca_needs_ecn(const struct sock *sk)
{ {
......
...@@ -389,6 +389,8 @@ enum { ...@@ -389,6 +389,8 @@ enum {
#define RTAX_INITRWND RTAX_INITRWND #define RTAX_INITRWND RTAX_INITRWND
RTAX_QUICKACK, RTAX_QUICKACK,
#define RTAX_QUICKACK RTAX_QUICKACK #define RTAX_QUICKACK RTAX_QUICKACK
RTAX_CC_ALGO,
#define RTAX_CC_ALGO RTAX_CC_ALGO
__RTAX_MAX __RTAX_MAX
}; };
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#include <net/arp.h> #include <net/arp.h>
#include <net/route.h> #include <net/route.h>
#include <net/udp.h> #include <net/udp.h>
#include <net/tcp.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/pkt_sched.h> #include <net/pkt_sched.h>
#include <net/fib_rules.h> #include <net/fib_rules.h>
...@@ -669,9 +670,19 @@ int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics) ...@@ -669,9 +670,19 @@ int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics)
for (i = 0; i < RTAX_MAX; i++) { for (i = 0; i < RTAX_MAX; i++) {
if (metrics[i]) { if (metrics[i]) {
if (i == RTAX_CC_ALGO - 1) {
char tmp[TCP_CA_NAME_MAX], *name;
name = tcp_ca_get_name_by_key(metrics[i], tmp);
if (!name)
continue;
if (nla_put_string(skb, i + 1, name))
goto nla_put_failure;
} else {
if (nla_put_u32(skb, i + 1, metrics[i]))
goto nla_put_failure;
}
valid++; valid++;
if (nla_put_u32(skb, i+1, metrics[i]))
goto nla_put_failure;
} }
} }
......
...@@ -298,7 +298,8 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct nlattr *att ...@@ -298,7 +298,8 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct nlattr *att
int type = nla_type(attr); int type = nla_type(attr);
if (type) { if (type) {
if (type > RTAX_MAX || nla_len(attr) < 4) if (type > RTAX_MAX || type == RTAX_CC_ALGO ||
nla_len(attr) < 4)
goto err_inval; goto err_inval;
fi->fib_metrics[type-1] = nla_get_u32(attr); fi->fib_metrics[type-1] = nla_get_u32(attr);
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/route.h> /* RTF_xxx */ #include <linux/route.h> /* RTF_xxx */
#include <net/neighbour.h> #include <net/neighbour.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/tcp.h>
#include <net/dst.h> #include <net/dst.h>
#include <net/flow.h> #include <net/flow.h>
#include <net/fib_rules.h> #include <net/fib_rules.h>
...@@ -273,7 +274,8 @@ static inline size_t dn_fib_nlmsg_size(struct dn_fib_info *fi) ...@@ -273,7 +274,8 @@ static inline size_t dn_fib_nlmsg_size(struct dn_fib_info *fi)
size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg)) size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg))
+ nla_total_size(4) /* RTA_TABLE */ + nla_total_size(4) /* RTA_TABLE */
+ nla_total_size(2) /* RTA_DST */ + nla_total_size(2) /* RTA_DST */
+ nla_total_size(4); /* RTA_PRIORITY */ + nla_total_size(4) /* RTA_PRIORITY */
+ nla_total_size(TCP_CA_NAME_MAX); /* RTAX_CC_ALGO */
/* space for nested metrics */ /* space for nested metrics */
payload += nla_total_size((RTAX_MAX * nla_total_size(4))); payload += nla_total_size((RTAX_MAX * nla_total_size(4)));
......
...@@ -360,7 +360,8 @@ static inline size_t fib_nlmsg_size(struct fib_info *fi) ...@@ -360,7 +360,8 @@ static inline size_t fib_nlmsg_size(struct fib_info *fi)
+ nla_total_size(4) /* RTA_TABLE */ + nla_total_size(4) /* RTA_TABLE */
+ nla_total_size(4) /* RTA_DST */ + nla_total_size(4) /* RTA_DST */
+ nla_total_size(4) /* RTA_PRIORITY */ + nla_total_size(4) /* RTA_PRIORITY */
+ nla_total_size(4); /* RTA_PREFSRC */ + nla_total_size(4) /* RTA_PREFSRC */
+ nla_total_size(TCP_CA_NAME_MAX); /* RTAX_CC_ALGO */
/* space for nested metrics */ /* space for nested metrics */
payload += nla_total_size((RTAX_MAX * nla_total_size(4))); payload += nla_total_size((RTAX_MAX * nla_total_size(4)));
...@@ -859,7 +860,16 @@ struct fib_info *fib_create_info(struct fib_config *cfg) ...@@ -859,7 +860,16 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
if (type > RTAX_MAX) if (type > RTAX_MAX)
goto err_inval; goto err_inval;
val = nla_get_u32(nla); if (type == RTAX_CC_ALGO) {
char tmp[TCP_CA_NAME_MAX];
nla_strlcpy(tmp, nla, sizeof(tmp));
val = tcp_ca_get_key_by_name(tmp);
if (val == TCP_CA_UNSPEC)
goto err_inval;
} else {
val = nla_get_u32(nla);
}
if (type == RTAX_ADVMSS && val > 65535 - 40) if (type == RTAX_ADVMSS && val > 65535 - 40)
val = 65535 - 40; val = 65535 - 40;
if (type == RTAX_MTU && val > 65535 - 15) if (type == RTAX_MTU && val > 65535 - 15)
......
...@@ -1488,10 +1488,22 @@ static int ip6_convert_metrics(struct mx6_config *mxc, ...@@ -1488,10 +1488,22 @@ static int ip6_convert_metrics(struct mx6_config *mxc,
int type = nla_type(nla); int type = nla_type(nla);
if (type) { if (type) {
u32 val;
if (unlikely(type > RTAX_MAX)) if (unlikely(type > RTAX_MAX))
goto err; goto err;
if (type == RTAX_CC_ALGO) {
char tmp[TCP_CA_NAME_MAX];
nla_strlcpy(tmp, nla, sizeof(tmp));
val = tcp_ca_get_key_by_name(tmp);
if (val == TCP_CA_UNSPEC)
goto err;
} else {
val = nla_get_u32(nla);
}
mp[type - 1] = nla_get_u32(nla); mp[type - 1] = val;
__set_bit(type - 1, mxc->mx_valid); __set_bit(type - 1, mxc->mx_valid);
} }
} }
...@@ -2571,7 +2583,8 @@ static inline size_t rt6_nlmsg_size(void) ...@@ -2571,7 +2583,8 @@ static inline size_t rt6_nlmsg_size(void)
+ nla_total_size(4) /* RTA_OIF */ + nla_total_size(4) /* RTA_OIF */
+ nla_total_size(4) /* RTA_PRIORITY */ + nla_total_size(4) /* RTA_PRIORITY */
+ RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
+ nla_total_size(sizeof(struct rta_cacheinfo)); + nla_total_size(sizeof(struct rta_cacheinfo))
+ nla_total_size(TCP_CA_NAME_MAX); /* RTAX_CC_ALGO */
} }
static int rt6_fill_node(struct net *net, static int rt6_fill_node(struct net *net,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment