Skip to content

Commit fc51895

Browse files
Florian Westphaldavem330
authored andcommitted
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>
1 parent 5147dfb commit fc51895

File tree

9 files changed

+172
-15
lines changed

9 files changed

+172
-15
lines changed

include/net/mptcp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <linux/tcp.h>
1313
#include <linux/types.h>
1414

15+
struct seq_file;
16+
1517
/* MPTCP sk_buff extension data */
1618
struct mptcp_ext {
1719
u64 data_ack;
@@ -123,6 +125,7 @@ static inline bool mptcp_skb_can_collapse(const struct sk_buff *to,
123125

124126
bool mptcp_sk_is_subflow(const struct sock *sk);
125127

128+
void mptcp_seq_show(struct seq_file *seq);
126129
#else
127130

128131
static inline void mptcp_init(void)
@@ -194,6 +197,7 @@ static inline bool mptcp_sk_is_subflow(const struct sock *sk)
194197
return false;
195198
}
196199

200+
static inline void mptcp_seq_show(struct seq_file *seq) { }
197201
#endif /* CONFIG_MPTCP */
198202

199203
#if IS_ENABLED(CONFIG_MPTCP_IPV6)

include/net/netns/mib.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ struct netns_mib {
2727
#if IS_ENABLED(CONFIG_TLS)
2828
DEFINE_SNMP_STAT(struct linux_tls_mib, tls_statistics);
2929
#endif
30+
#ifdef CONFIG_MPTCP
31+
DEFINE_SNMP_STAT(struct mptcp_mib, mptcp_statistics);
32+
#endif
3033
};
3134

3235
#endif

net/ipv4/af_inet.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,6 +1793,10 @@ static __net_exit void ipv4_mib_exit_net(struct net *net)
17931793
free_percpu(net->mib.net_statistics);
17941794
free_percpu(net->mib.ip_statistics);
17951795
free_percpu(net->mib.tcp_statistics);
1796+
#ifdef CONFIG_MPTCP
1797+
/* allocated on demand, see mptcp_init_sock() */
1798+
free_percpu(net->mib.mptcp_statistics);
1799+
#endif
17961800
}
17971801

17981802
static __net_initdata struct pernet_operations ipv4_mib_ops = {

net/ipv4/proc.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <net/icmp.h>
3333
#include <net/protocol.h>
3434
#include <net/tcp.h>
35+
#include <net/mptcp.h>
3536
#include <net/udp.h>
3637
#include <net/udplite.h>
3738
#include <linux/bottom_half.h>
@@ -485,6 +486,7 @@ static int netstat_seq_show(struct seq_file *seq, void *v)
485486
offsetof(struct ipstats_mib, syncp)));
486487

487488
seq_putc(seq, '\n');
489+
mptcp_seq_show(seq);
488490
return 0;
489491
}
490492

net/mptcp/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# SPDX-License-Identifier: GPL-2.0
22
obj-$(CONFIG_MPTCP) += mptcp.o
33

4-
mptcp-y := protocol.o subflow.o options.o token.o crypto.o ctrl.o pm.o diag.o
4+
mptcp-y := protocol.o subflow.o options.o token.o crypto.o ctrl.o pm.o diag.o mib.o

net/mptcp/mib.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
3+
#include <linux/seq_file.h>
4+
#include <net/ip.h>
5+
#include <net/mptcp.h>
6+
#include <net/snmp.h>
7+
#include <net/net_namespace.h>
8+
9+
#include "mib.h"
10+
11+
static const struct snmp_mib mptcp_snmp_list[] = {
12+
SNMP_MIB_ITEM("MPCapableSYNRX", MPTCP_MIB_MPCAPABLEPASSIVE),
13+
SNMP_MIB_ITEM("MPCapableACKRX", MPTCP_MIB_MPCAPABLEPASSIVEACK),
14+
SNMP_MIB_ITEM("MPCapableFallbackACK", MPTCP_MIB_MPCAPABLEPASSIVEFALLBACK),
15+
SNMP_MIB_ITEM("MPCapableFallbackSYNACK", MPTCP_MIB_MPCAPABLEACTIVEFALLBACK),
16+
SNMP_MIB_ITEM("MPTCPRetrans", MPTCP_MIB_RETRANSSEGS),
17+
SNMP_MIB_ITEM("MPJoinNoTokenFound", MPTCP_MIB_JOINNOTOKEN),
18+
SNMP_MIB_ITEM("MPJoinSynRx", MPTCP_MIB_JOINSYNRX),
19+
SNMP_MIB_ITEM("MPJoinSynAckRx", MPTCP_MIB_JOINSYNACKRX),
20+
SNMP_MIB_ITEM("MPJoinSynAckHMacFailure", MPTCP_MIB_JOINSYNACKMAC),
21+
SNMP_MIB_ITEM("MPJoinAckRx", MPTCP_MIB_JOINACKRX),
22+
SNMP_MIB_ITEM("MPJoinAckHMacFailure", MPTCP_MIB_JOINACKMAC),
23+
SNMP_MIB_ITEM("DSSNotMatching", MPTCP_MIB_DSSNOMATCH),
24+
SNMP_MIB_ITEM("InfiniteMapRx", MPTCP_MIB_INFINITEMAPRX),
25+
SNMP_MIB_SENTINEL
26+
};
27+
28+
/* mptcp_mib_alloc - allocate percpu mib counters
29+
*
30+
* These are allocated when the first mptcp socket is created so
31+
* we do not waste percpu memory if mptcp isn't in use.
32+
*/
33+
bool mptcp_mib_alloc(struct net *net)
34+
{
35+
struct mptcp_mib __percpu *mib = alloc_percpu(struct mptcp_mib);
36+
37+
if (!mib)
38+
return false;
39+
40+
if (cmpxchg(&net->mib.mptcp_statistics, NULL, mib))
41+
free_percpu(mib);
42+
43+
return true;
44+
}
45+
46+
void mptcp_seq_show(struct seq_file *seq)
47+
{
48+
struct net *net = seq->private;
49+
int i;
50+
51+
seq_puts(seq, "MPTcpExt:");
52+
for (i = 0; mptcp_snmp_list[i].name; i++)
53+
seq_printf(seq, " %s", mptcp_snmp_list[i].name);
54+
55+
seq_puts(seq, "\nMPTcpExt:");
56+
57+
if (!net->mib.mptcp_statistics) {
58+
for (i = 0; mptcp_snmp_list[i].name; i++)
59+
seq_puts(seq, " 0");
60+
61+
return;
62+
}
63+
64+
for (i = 0; mptcp_snmp_list[i].name; i++)
65+
seq_printf(seq, " %lu",
66+
snmp_fold_field(net->mib.mptcp_statistics,
67+
mptcp_snmp_list[i].entry));
68+
seq_putc(seq, '\n');
69+
}

net/mptcp/mib.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* SPDX-License-Identifier: GPL-2.0-or-later */
2+
3+
enum linux_mptcp_mib_field {
4+
MPTCP_MIB_NUM = 0,
5+
MPTCP_MIB_MPCAPABLEPASSIVE, /* Received SYN with MP_CAPABLE */
6+
MPTCP_MIB_MPCAPABLEPASSIVEACK, /* Received third ACK with MP_CAPABLE */
7+
MPTCP_MIB_MPCAPABLEPASSIVEFALLBACK,/* Server-side fallback during 3-way handshake */
8+
MPTCP_MIB_MPCAPABLEACTIVEFALLBACK, /* Client-side fallback during 3-way handshake */
9+
MPTCP_MIB_RETRANSSEGS, /* Segments retransmitted at the MPTCP-level */
10+
MPTCP_MIB_JOINNOTOKEN, /* Received MP_JOIN but the token was not found */
11+
MPTCP_MIB_JOINSYNRX, /* Received a SYN + MP_JOIN */
12+
MPTCP_MIB_JOINSYNACKRX, /* Received a SYN/ACK + MP_JOIN */
13+
MPTCP_MIB_JOINSYNACKMAC, /* HMAC was wrong on SYN/ACK + MP_JOIN */
14+
MPTCP_MIB_JOINACKRX, /* Received an ACK + MP_JOIN */
15+
MPTCP_MIB_JOINACKMAC, /* HMAC was wrong on ACK + MP_JOIN */
16+
MPTCP_MIB_DSSNOMATCH, /* Received a new mapping that did not match the previous one */
17+
MPTCP_MIB_INFINITEMAPRX, /* Received an infinite mapping */
18+
__MPTCP_MIB_MAX
19+
};
20+
21+
#define LINUX_MIB_MPTCP_MAX __MPTCP_MIB_MAX
22+
struct mptcp_mib {
23+
unsigned long mibs[LINUX_MIB_MPTCP_MAX];
24+
};
25+
26+
static inline void MPTCP_INC_STATS(struct net *net,
27+
enum linux_mptcp_mib_field field)
28+
{
29+
if (likely(net->mib.mptcp_statistics))
30+
SNMP_INC_STATS(net->mib.mptcp_statistics, field);
31+
}
32+
33+
static inline void __MPTCP_INC_STATS(struct net *net,
34+
enum linux_mptcp_mib_field field)
35+
{
36+
if (likely(net->mib.mptcp_statistics))
37+
__SNMP_INC_STATS(net->mib.mptcp_statistics, field);
38+
}
39+
40+
bool mptcp_mib_alloc(struct net *net);

net/mptcp/protocol.c

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#endif
2222
#include <net/mptcp.h>
2323
#include "protocol.h"
24+
#include "mib.h"
2425

2526
#define MPTCP_SAME_STATE TCP_MAX_STATES
2627

@@ -1032,6 +1033,7 @@ static void mptcp_worker(struct work_struct *work)
10321033
if (ret < 0)
10331034
break;
10341035

1036+
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RETRANSSEGS);
10351037
copied += ret;
10361038
dfrag->data_len -= ret;
10371039
dfrag->offset += ret;
@@ -1081,17 +1083,22 @@ static int __mptcp_init_sock(struct sock *sk)
10811083

10821084
static int mptcp_init_sock(struct sock *sk)
10831085
{
1084-
int ret = __mptcp_init_sock(sk);
1086+
struct net *net = sock_net(sk);
1087+
int ret;
10851088

1089+
if (!mptcp_is_enabled(net))
1090+
return -ENOPROTOOPT;
1091+
1092+
if (unlikely(!net->mib.mptcp_statistics) && !mptcp_mib_alloc(net))
1093+
return -ENOMEM;
1094+
1095+
ret = __mptcp_init_sock(sk);
10861096
if (ret)
10871097
return ret;
10881098

10891099
sk_sockets_allocated_inc(sk);
10901100
sk->sk_sndbuf = sock_net(sk)->ipv4.sysctl_tcp_wmem[2];
10911101

1092-
if (!mptcp_is_enabled(sock_net(sk)))
1093-
return -ENOPROTOOPT;
1094-
10951102
return 0;
10961103
}
10971104

@@ -1327,7 +1334,12 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,
13271334
list_add(&subflow->node, &msk->conn_list);
13281335

13291336
bh_unlock_sock(new_mptcp_sock);
1337+
1338+
__MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPCAPABLEPASSIVEACK);
13301339
local_bh_enable();
1340+
} else {
1341+
MPTCP_INC_STATS(sock_net(sk),
1342+
MPTCP_MIB_MPCAPABLEPASSIVEFALLBACK);
13311343
}
13321344

13331345
return newsk;
@@ -1448,13 +1460,15 @@ void mptcp_finish_connect(struct sock *ssk)
14481460
u64 ack_seq;
14491461

14501462
subflow = mptcp_subflow_ctx(ssk);
1451-
1452-
if (!subflow->mp_capable)
1453-
return;
1454-
14551463
sk = subflow->conn;
14561464
msk = mptcp_sk(sk);
14571465

1466+
if (!subflow->mp_capable) {
1467+
MPTCP_INC_STATS(sock_net(sk),
1468+
MPTCP_MIB_MPCAPABLEACTIVEFALLBACK);
1469+
return;
1470+
}
1471+
14581472
pr_debug("msk=%p, token=%u", sk, subflow->token);
14591473

14601474
mptcp_crypto_key_sha(subflow->remote_key, NULL, &ack_seq);

net/mptcp/subflow.c

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
#endif
2121
#include <net/mptcp.h>
2222
#include "protocol.h"
23+
#include "mib.h"
24+
25+
static void SUBFLOW_REQ_INC_STATS(struct request_sock *req,
26+
enum linux_mptcp_mib_field field)
27+
{
28+
MPTCP_INC_STATS(sock_net(req_to_sk(req)), field);
29+
}
2330

2431
static int subflow_rebuild_header(struct sock *sk)
2532
{
@@ -88,8 +95,7 @@ static bool subflow_token_join_request(struct request_sock *req,
8895

8996
msk = mptcp_token_get_sock(subflow_req->token);
9097
if (!msk) {
91-
pr_debug("subflow_req=%p, token=%u - not found\n",
92-
subflow_req, subflow_req->token);
98+
SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINNOTOKEN);
9399
return false;
94100
}
95101

@@ -137,8 +143,14 @@ static void subflow_init_req(struct request_sock *req,
137143
return;
138144
#endif
139145

140-
if (rx_opt.mptcp.mp_capable && rx_opt.mptcp.mp_join)
141-
return;
146+
if (rx_opt.mptcp.mp_capable) {
147+
SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_MPCAPABLEPASSIVE);
148+
149+
if (rx_opt.mptcp.mp_join)
150+
return;
151+
} else if (rx_opt.mptcp.mp_join) {
152+
SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINSYNRX);
153+
}
142154

143155
if (rx_opt.mptcp.mp_capable && listener->request_mptcp) {
144156
int err;
@@ -237,6 +249,7 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)
237249
subflow, subflow->thmac,
238250
subflow->remote_nonce);
239251
if (!subflow_thmac_valid(subflow)) {
252+
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_JOINACKMAC);
240253
subflow->mp_join = 0;
241254
goto do_reset;
242255
}
@@ -253,6 +266,7 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)
253266
goto do_reset;
254267

255268
subflow->conn_finished = 1;
269+
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_JOINSYNACKRX);
256270
} else {
257271
do_reset:
258272
tcp_send_active_reset(sk, GFP_ATOMIC);
@@ -382,8 +396,10 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
382396
opt_rx.mptcp.mp_join = 0;
383397
mptcp_get_options(skb, &opt_rx);
384398
if (!opt_rx.mptcp.mp_join ||
385-
!subflow_hmac_valid(req, &opt_rx))
399+
!subflow_hmac_valid(req, &opt_rx)) {
400+
SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINACKMAC);
386401
return NULL;
402+
}
387403
}
388404

389405
create_child:
@@ -420,6 +436,8 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
420436
ctx->conn = (struct sock *)owner;
421437
if (!mptcp_finish_join(child))
422438
goto close_child;
439+
440+
SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINACKRX);
423441
}
424442
}
425443

@@ -535,6 +553,7 @@ static enum mapping_status get_mapping_status(struct sock *ssk)
535553
data_len = mpext->data_len;
536554
if (data_len == 0) {
537555
pr_err("Infinite mapping not handled");
556+
MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_INFINITEMAPRX);
538557
return MAPPING_INVALID;
539558
}
540559

@@ -578,8 +597,10 @@ static enum mapping_status get_mapping_status(struct sock *ssk)
578597
/* If this skb data are fully covered by the current mapping,
579598
* the new map would need caching, which is not supported
580599
*/
581-
if (skb_is_fully_mapped(ssk, skb))
600+
if (skb_is_fully_mapped(ssk, skb)) {
601+
MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_DSSNOMATCH);
582602
return MAPPING_INVALID;
603+
}
583604

584605
/* will validate the next map after consuming the current one */
585606
return MAPPING_OK;

0 commit comments

Comments
 (0)