92f22dbef1
commit b8adb69a7d29c2d33eb327bca66476fb6066516b upstream. Since the introduction of the subflow ULP diag interface, the dump callback accessed all the subflow data with lockless. We need either to annotate all the read and write operation accordingly, or acquire the subflow socket lock. Let's do latter, even if slower, to avoid a diffstat havoc. Fixes: 5147dfb50832 ("mptcp: allow dumping subflow context to userspace") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni <pabeni@redhat.com> Reviewed-by: Mat Martineau <martineau@kernel.org> Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
108 lines
3.1 KiB
C
Executable file
108 lines
3.1 KiB
C
Executable file
// SPDX-License-Identifier: GPL-2.0
|
|
/* MPTCP socket monitoring support
|
|
*
|
|
* Copyright (c) 2019 Red Hat
|
|
*
|
|
* Author: Davide Caratti <dcaratti@redhat.com>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/net.h>
|
|
#include <linux/inet_diag.h>
|
|
#include <net/netlink.h>
|
|
#include <uapi/linux/mptcp.h>
|
|
#include "protocol.h"
|
|
|
|
static int subflow_get_info(struct sock *sk, struct sk_buff *skb)
|
|
{
|
|
struct mptcp_subflow_context *sf;
|
|
struct nlattr *start;
|
|
u32 flags = 0;
|
|
bool slow;
|
|
int err;
|
|
|
|
start = nla_nest_start_noflag(skb, INET_ULP_INFO_MPTCP);
|
|
if (!start)
|
|
return -EMSGSIZE;
|
|
|
|
slow = lock_sock_fast(sk);
|
|
rcu_read_lock();
|
|
sf = rcu_dereference(inet_csk(sk)->icsk_ulp_data);
|
|
if (!sf) {
|
|
err = 0;
|
|
goto nla_failure;
|
|
}
|
|
|
|
if (sf->mp_capable)
|
|
flags |= MPTCP_SUBFLOW_FLAG_MCAP_REM;
|
|
if (sf->request_mptcp)
|
|
flags |= MPTCP_SUBFLOW_FLAG_MCAP_LOC;
|
|
if (sf->mp_join)
|
|
flags |= MPTCP_SUBFLOW_FLAG_JOIN_REM;
|
|
if (sf->request_join)
|
|
flags |= MPTCP_SUBFLOW_FLAG_JOIN_LOC;
|
|
if (sf->backup)
|
|
flags |= MPTCP_SUBFLOW_FLAG_BKUP_REM;
|
|
if (sf->request_bkup)
|
|
flags |= MPTCP_SUBFLOW_FLAG_BKUP_LOC;
|
|
if (sf->fully_established)
|
|
flags |= MPTCP_SUBFLOW_FLAG_FULLY_ESTABLISHED;
|
|
if (sf->conn_finished)
|
|
flags |= MPTCP_SUBFLOW_FLAG_CONNECTED;
|
|
if (sf->map_valid)
|
|
flags |= MPTCP_SUBFLOW_FLAG_MAPVALID;
|
|
|
|
if (nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_TOKEN_REM, sf->remote_token) ||
|
|
nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_TOKEN_LOC, sf->token) ||
|
|
nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_RELWRITE_SEQ,
|
|
sf->rel_write_seq) ||
|
|
nla_put_u64_64bit(skb, MPTCP_SUBFLOW_ATTR_MAP_SEQ, sf->map_seq,
|
|
MPTCP_SUBFLOW_ATTR_PAD) ||
|
|
nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_MAP_SFSEQ,
|
|
sf->map_subflow_seq) ||
|
|
nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_SSN_OFFSET, sf->ssn_offset) ||
|
|
nla_put_u16(skb, MPTCP_SUBFLOW_ATTR_MAP_DATALEN,
|
|
sf->map_data_len) ||
|
|
nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_FLAGS, flags) ||
|
|
nla_put_u8(skb, MPTCP_SUBFLOW_ATTR_ID_REM, sf->remote_id) ||
|
|
nla_put_u8(skb, MPTCP_SUBFLOW_ATTR_ID_LOC, sf->local_id)) {
|
|
err = -EMSGSIZE;
|
|
goto nla_failure;
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
unlock_sock_fast(sk, slow);
|
|
nla_nest_end(skb, start);
|
|
return 0;
|
|
|
|
nla_failure:
|
|
rcu_read_unlock();
|
|
unlock_sock_fast(sk, slow);
|
|
nla_nest_cancel(skb, start);
|
|
return err;
|
|
}
|
|
|
|
static size_t subflow_get_info_size(const struct sock *sk)
|
|
{
|
|
size_t size = 0;
|
|
|
|
size += nla_total_size(0) + /* INET_ULP_INFO_MPTCP */
|
|
nla_total_size(4) + /* MPTCP_SUBFLOW_ATTR_TOKEN_REM */
|
|
nla_total_size(4) + /* MPTCP_SUBFLOW_ATTR_TOKEN_LOC */
|
|
nla_total_size(4) + /* MPTCP_SUBFLOW_ATTR_RELWRITE_SEQ */
|
|
nla_total_size_64bit(8) + /* MPTCP_SUBFLOW_ATTR_MAP_SEQ */
|
|
nla_total_size(4) + /* MPTCP_SUBFLOW_ATTR_MAP_SFSEQ */
|
|
nla_total_size(2) + /* MPTCP_SUBFLOW_ATTR_SSN_OFFSET */
|
|
nla_total_size(2) + /* MPTCP_SUBFLOW_ATTR_MAP_DATALEN */
|
|
nla_total_size(4) + /* MPTCP_SUBFLOW_ATTR_FLAGS */
|
|
nla_total_size(1) + /* MPTCP_SUBFLOW_ATTR_ID_REM */
|
|
nla_total_size(1) + /* MPTCP_SUBFLOW_ATTR_ID_LOC */
|
|
0;
|
|
return size;
|
|
}
|
|
|
|
void mptcp_diag_subflow_init(struct tcp_ulp_ops *ops)
|
|
{
|
|
ops->get_info = subflow_get_info;
|
|
ops->get_info_size = subflow_get_info_size;
|
|
}
|