diff options
| author | Florian Westphal <fw@strlen.de> | 2020-03-27 14:48:50 -0700 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2020-03-29 22:14:49 -0700 | 
| commit | fc518953bc9c8d7d33c6ab261995f5038f3c87f9 (patch) | |
| tree | 9c4eebd575e693916feb8d234411d08a17ec430a /net/mptcp | |
| parent | 5147dfb5083204d6f5468d6d6d2d04b2cdc0cf2b (diff) | |
| download | linux-fc518953bc9c8d7d33c6ab261995f5038f3c87f9.tar.bz2 | |
mptcp: add and use MIB counter infrastructure
Exported via same /proc file as the Linux TCP MIB counters, so "netstat -s"
or "nstat" will show them automatically.
The MPTCP MIB counters are allocated in a distinct pcpu area in order to
avoid bloating/wasting TCP pcpu memory.
Counters are allocated once the first MPTCP socket is created in a
network namespace and free'd on exit.
If no sockets have been allocated, all-zero mptcp counters are shown.
The MIB counter list is taken from the multipath-tcp.org kernel, but
only a few counters have been picked up so far.  The counter list can
be increased at any time later on.
v2 -> v3:
 - remove 'inline' in foo.c files (David S. Miller)
Co-developed-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/mptcp')
| -rw-r--r-- | net/mptcp/Makefile | 2 | ||||
| -rw-r--r-- | net/mptcp/mib.c | 69 | ||||
| -rw-r--r-- | net/mptcp/mib.h | 40 | ||||
| -rw-r--r-- | net/mptcp/protocol.c | 30 | ||||
| -rw-r--r-- | net/mptcp/subflow.c | 33 | 
5 files changed, 159 insertions, 15 deletions
diff --git a/net/mptcp/Makefile b/net/mptcp/Makefile index 54494cf5bec0..faebe8ec9f73 100644 --- a/net/mptcp/Makefile +++ b/net/mptcp/Makefile @@ -1,4 +1,4 @@  # SPDX-License-Identifier: GPL-2.0  obj-$(CONFIG_MPTCP) += mptcp.o -mptcp-y := protocol.o subflow.o options.o token.o crypto.o ctrl.o pm.o diag.o +mptcp-y := protocol.o subflow.o options.o token.o crypto.o ctrl.o pm.o diag.o mib.o diff --git a/net/mptcp/mib.c b/net/mptcp/mib.c new file mode 100644 index 000000000000..0a6a15f3456d --- /dev/null +++ b/net/mptcp/mib.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <linux/seq_file.h> +#include <net/ip.h> +#include <net/mptcp.h> +#include <net/snmp.h> +#include <net/net_namespace.h> + +#include "mib.h" + +static const struct snmp_mib mptcp_snmp_list[] = { +	SNMP_MIB_ITEM("MPCapableSYNRX", MPTCP_MIB_MPCAPABLEPASSIVE), +	SNMP_MIB_ITEM("MPCapableACKRX", MPTCP_MIB_MPCAPABLEPASSIVEACK), +	SNMP_MIB_ITEM("MPCapableFallbackACK", MPTCP_MIB_MPCAPABLEPASSIVEFALLBACK), +	SNMP_MIB_ITEM("MPCapableFallbackSYNACK", MPTCP_MIB_MPCAPABLEACTIVEFALLBACK), +	SNMP_MIB_ITEM("MPTCPRetrans", MPTCP_MIB_RETRANSSEGS), +	SNMP_MIB_ITEM("MPJoinNoTokenFound", MPTCP_MIB_JOINNOTOKEN), +	SNMP_MIB_ITEM("MPJoinSynRx", MPTCP_MIB_JOINSYNRX), +	SNMP_MIB_ITEM("MPJoinSynAckRx", MPTCP_MIB_JOINSYNACKRX), +	SNMP_MIB_ITEM("MPJoinSynAckHMacFailure", MPTCP_MIB_JOINSYNACKMAC), +	SNMP_MIB_ITEM("MPJoinAckRx", MPTCP_MIB_JOINACKRX), +	SNMP_MIB_ITEM("MPJoinAckHMacFailure", MPTCP_MIB_JOINACKMAC), +	SNMP_MIB_ITEM("DSSNotMatching", MPTCP_MIB_DSSNOMATCH), +	SNMP_MIB_ITEM("InfiniteMapRx", MPTCP_MIB_INFINITEMAPRX), +	SNMP_MIB_SENTINEL +}; + +/* mptcp_mib_alloc - allocate percpu mib counters + * + * These are allocated when the first mptcp socket is created so + * we do not waste percpu memory if mptcp isn't in use. + */ +bool mptcp_mib_alloc(struct net *net) +{ +	struct mptcp_mib __percpu *mib = alloc_percpu(struct mptcp_mib); + +	if (!mib) +		return false; + +	if (cmpxchg(&net->mib.mptcp_statistics, NULL, mib)) +		free_percpu(mib); + +	return true; +} + +void mptcp_seq_show(struct seq_file *seq) +{ +	struct net *net = seq->private; +	int i; + +	seq_puts(seq, "MPTcpExt:"); +	for (i = 0; mptcp_snmp_list[i].name; i++) +		seq_printf(seq, " %s", mptcp_snmp_list[i].name); + +	seq_puts(seq, "\nMPTcpExt:"); + +	if (!net->mib.mptcp_statistics) { +		for (i = 0; mptcp_snmp_list[i].name; i++) +			seq_puts(seq, " 0"); + +		return; +	} + +	for (i = 0; mptcp_snmp_list[i].name; i++) +		seq_printf(seq, " %lu", +			   snmp_fold_field(net->mib.mptcp_statistics, +					   mptcp_snmp_list[i].entry)); +	seq_putc(seq, '\n'); +} diff --git a/net/mptcp/mib.h b/net/mptcp/mib.h new file mode 100644 index 000000000000..d7de340fc997 --- /dev/null +++ b/net/mptcp/mib.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +enum linux_mptcp_mib_field { +	MPTCP_MIB_NUM = 0, +	MPTCP_MIB_MPCAPABLEPASSIVE,	/* Received SYN with MP_CAPABLE */ +	MPTCP_MIB_MPCAPABLEPASSIVEACK,	/* Received third ACK with MP_CAPABLE */ +	MPTCP_MIB_MPCAPABLEPASSIVEFALLBACK,/* Server-side fallback during 3-way handshake */ +	MPTCP_MIB_MPCAPABLEACTIVEFALLBACK, /* Client-side fallback during 3-way handshake */ +	MPTCP_MIB_RETRANSSEGS,		/* Segments retransmitted at the MPTCP-level */ +	MPTCP_MIB_JOINNOTOKEN,		/* Received MP_JOIN but the token was not found */ +	MPTCP_MIB_JOINSYNRX,		/* Received a SYN + MP_JOIN */ +	MPTCP_MIB_JOINSYNACKRX,		/* Received a SYN/ACK + MP_JOIN */ +	MPTCP_MIB_JOINSYNACKMAC,	/* HMAC was wrong on SYN/ACK + MP_JOIN */ +	MPTCP_MIB_JOINACKRX,		/* Received an ACK + MP_JOIN */ +	MPTCP_MIB_JOINACKMAC,		/* HMAC was wrong on ACK + MP_JOIN */ +	MPTCP_MIB_DSSNOMATCH,		/* Received a new mapping that did not match the previous one */ +	MPTCP_MIB_INFINITEMAPRX,	/* Received an infinite mapping */ +	__MPTCP_MIB_MAX +}; + +#define LINUX_MIB_MPTCP_MAX	__MPTCP_MIB_MAX +struct mptcp_mib { +	unsigned long mibs[LINUX_MIB_MPTCP_MAX]; +}; + +static inline void MPTCP_INC_STATS(struct net *net, +				   enum linux_mptcp_mib_field field) +{ +	if (likely(net->mib.mptcp_statistics)) +		SNMP_INC_STATS(net->mib.mptcp_statistics, field); +} + +static inline void __MPTCP_INC_STATS(struct net *net, +				     enum linux_mptcp_mib_field field) +{ +	if (likely(net->mib.mptcp_statistics)) +		__SNMP_INC_STATS(net->mib.mptcp_statistics, field); +} + +bool mptcp_mib_alloc(struct net *net); diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 8d2777092390..1833bc1f4a43 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -21,6 +21,7 @@  #endif  #include <net/mptcp.h>  #include "protocol.h" +#include "mib.h"  #define MPTCP_SAME_STATE TCP_MAX_STATES @@ -1032,6 +1033,7 @@ static void mptcp_worker(struct work_struct *work)  		if (ret < 0)  			break; +		MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RETRANSSEGS);  		copied += ret;  		dfrag->data_len -= ret;  		dfrag->offset += ret; @@ -1081,17 +1083,22 @@ static int __mptcp_init_sock(struct sock *sk)  static int mptcp_init_sock(struct sock *sk)  { -	int ret = __mptcp_init_sock(sk); +	struct net *net = sock_net(sk); +	int ret; +	if (!mptcp_is_enabled(net)) +		return -ENOPROTOOPT; + +	if (unlikely(!net->mib.mptcp_statistics) && !mptcp_mib_alloc(net)) +		return -ENOMEM; + +	ret = __mptcp_init_sock(sk);  	if (ret)  		return ret;  	sk_sockets_allocated_inc(sk);  	sk->sk_sndbuf = sock_net(sk)->ipv4.sysctl_tcp_wmem[2]; -	if (!mptcp_is_enabled(sock_net(sk))) -		return -ENOPROTOOPT; -  	return 0;  } @@ -1327,7 +1334,12 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,  		list_add(&subflow->node, &msk->conn_list);  		bh_unlock_sock(new_mptcp_sock); + +		__MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPCAPABLEPASSIVEACK);  		local_bh_enable(); +	} else { +		MPTCP_INC_STATS(sock_net(sk), +				MPTCP_MIB_MPCAPABLEPASSIVEFALLBACK);  	}  	return newsk; @@ -1448,13 +1460,15 @@ void mptcp_finish_connect(struct sock *ssk)  	u64 ack_seq;  	subflow = mptcp_subflow_ctx(ssk); - -	if (!subflow->mp_capable) -		return; -  	sk = subflow->conn;  	msk = mptcp_sk(sk); +	if (!subflow->mp_capable) { +		MPTCP_INC_STATS(sock_net(sk), +				MPTCP_MIB_MPCAPABLEACTIVEFALLBACK); +		return; +	} +  	pr_debug("msk=%p, token=%u", sk, subflow->token);  	mptcp_crypto_key_sha(subflow->remote_key, NULL, &ack_seq); diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index c051db074708..b5180c81588e 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -20,6 +20,13 @@  #endif  #include <net/mptcp.h>  #include "protocol.h" +#include "mib.h" + +static void SUBFLOW_REQ_INC_STATS(struct request_sock *req, +				  enum linux_mptcp_mib_field field) +{ +	MPTCP_INC_STATS(sock_net(req_to_sk(req)), field); +}  static int subflow_rebuild_header(struct sock *sk)  { @@ -88,8 +95,7 @@ static bool subflow_token_join_request(struct request_sock *req,  	msk = mptcp_token_get_sock(subflow_req->token);  	if (!msk) { -		pr_debug("subflow_req=%p, token=%u - not found\n", -			 subflow_req, subflow_req->token); +		SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINNOTOKEN);  		return false;  	} @@ -137,8 +143,14 @@ static void subflow_init_req(struct request_sock *req,  		return;  #endif -	if (rx_opt.mptcp.mp_capable && rx_opt.mptcp.mp_join) -		return; +	if (rx_opt.mptcp.mp_capable) { +		SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_MPCAPABLEPASSIVE); + +		if (rx_opt.mptcp.mp_join) +			return; +	} else if (rx_opt.mptcp.mp_join) { +		SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINSYNRX); +	}  	if (rx_opt.mptcp.mp_capable && listener->request_mptcp) {  		int err; @@ -237,6 +249,7 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)  			 subflow, subflow->thmac,  			 subflow->remote_nonce);  		if (!subflow_thmac_valid(subflow)) { +			MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_JOINACKMAC);  			subflow->mp_join = 0;  			goto do_reset;  		} @@ -253,6 +266,7 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)  			goto do_reset;  		subflow->conn_finished = 1; +		MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_JOINSYNACKRX);  	} else {  do_reset:  		tcp_send_active_reset(sk, GFP_ATOMIC); @@ -382,8 +396,10 @@ create_msk:  		opt_rx.mptcp.mp_join = 0;  		mptcp_get_options(skb, &opt_rx);  		if (!opt_rx.mptcp.mp_join || -		    !subflow_hmac_valid(req, &opt_rx)) +		    !subflow_hmac_valid(req, &opt_rx)) { +			SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINACKMAC);  			return NULL; +		}  	}  create_child: @@ -420,6 +436,8 @@ create_child:  			ctx->conn = (struct sock *)owner;  			if (!mptcp_finish_join(child))  				goto close_child; + +			SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINACKRX);  		}  	} @@ -535,6 +553,7 @@ static enum mapping_status get_mapping_status(struct sock *ssk)  	data_len = mpext->data_len;  	if (data_len == 0) {  		pr_err("Infinite mapping not handled"); +		MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_INFINITEMAPRX);  		return MAPPING_INVALID;  	} @@ -578,8 +597,10 @@ static enum mapping_status get_mapping_status(struct sock *ssk)  		/* If this skb data are fully covered by the current mapping,  		 * the new map would need caching, which is not supported  		 */ -		if (skb_is_fully_mapped(ssk, skb)) +		if (skb_is_fully_mapped(ssk, skb)) { +			MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_DSSNOMATCH);  			return MAPPING_INVALID; +		}  		/* will validate the next map after consuming the current one */  		return MAPPING_OK;  |