162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* MPTCP socket monitoring support
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2019 Red Hat
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Author: Davide Caratti <dcaratti@redhat.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/net.h>
1162306a36Sopenharmony_ci#include <linux/inet_diag.h>
1262306a36Sopenharmony_ci#include <net/netlink.h>
1362306a36Sopenharmony_ci#include <uapi/linux/mptcp.h>
1462306a36Sopenharmony_ci#include "protocol.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic int subflow_get_info(struct sock *sk, struct sk_buff *skb)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	struct mptcp_subflow_context *sf;
1962306a36Sopenharmony_ci	struct nlattr *start;
2062306a36Sopenharmony_ci	u32 flags = 0;
2162306a36Sopenharmony_ci	bool slow;
2262306a36Sopenharmony_ci	int err;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	if (inet_sk_state_load(sk) == TCP_LISTEN)
2562306a36Sopenharmony_ci		return 0;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	start = nla_nest_start_noflag(skb, INET_ULP_INFO_MPTCP);
2862306a36Sopenharmony_ci	if (!start)
2962306a36Sopenharmony_ci		return -EMSGSIZE;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	slow = lock_sock_fast(sk);
3262306a36Sopenharmony_ci	rcu_read_lock();
3362306a36Sopenharmony_ci	sf = rcu_dereference(inet_csk(sk)->icsk_ulp_data);
3462306a36Sopenharmony_ci	if (!sf) {
3562306a36Sopenharmony_ci		err = 0;
3662306a36Sopenharmony_ci		goto nla_failure;
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (sf->mp_capable)
4062306a36Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_MCAP_REM;
4162306a36Sopenharmony_ci	if (sf->request_mptcp)
4262306a36Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_MCAP_LOC;
4362306a36Sopenharmony_ci	if (sf->mp_join)
4462306a36Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_JOIN_REM;
4562306a36Sopenharmony_ci	if (sf->request_join)
4662306a36Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_JOIN_LOC;
4762306a36Sopenharmony_ci	if (sf->backup)
4862306a36Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_BKUP_REM;
4962306a36Sopenharmony_ci	if (sf->request_bkup)
5062306a36Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_BKUP_LOC;
5162306a36Sopenharmony_ci	if (sf->fully_established)
5262306a36Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_FULLY_ESTABLISHED;
5362306a36Sopenharmony_ci	if (sf->conn_finished)
5462306a36Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_CONNECTED;
5562306a36Sopenharmony_ci	if (sf->map_valid)
5662306a36Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_MAPVALID;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_TOKEN_REM, sf->remote_token) ||
5962306a36Sopenharmony_ci	    nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_TOKEN_LOC, sf->token) ||
6062306a36Sopenharmony_ci	    nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_RELWRITE_SEQ,
6162306a36Sopenharmony_ci			sf->rel_write_seq) ||
6262306a36Sopenharmony_ci	    nla_put_u64_64bit(skb, MPTCP_SUBFLOW_ATTR_MAP_SEQ, sf->map_seq,
6362306a36Sopenharmony_ci			      MPTCP_SUBFLOW_ATTR_PAD) ||
6462306a36Sopenharmony_ci	    nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_MAP_SFSEQ,
6562306a36Sopenharmony_ci			sf->map_subflow_seq) ||
6662306a36Sopenharmony_ci	    nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_SSN_OFFSET, sf->ssn_offset) ||
6762306a36Sopenharmony_ci	    nla_put_u16(skb, MPTCP_SUBFLOW_ATTR_MAP_DATALEN,
6862306a36Sopenharmony_ci			sf->map_data_len) ||
6962306a36Sopenharmony_ci	    nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_FLAGS, flags) ||
7062306a36Sopenharmony_ci	    nla_put_u8(skb, MPTCP_SUBFLOW_ATTR_ID_REM, sf->remote_id) ||
7162306a36Sopenharmony_ci	    nla_put_u8(skb, MPTCP_SUBFLOW_ATTR_ID_LOC, subflow_get_local_id(sf))) {
7262306a36Sopenharmony_ci		err = -EMSGSIZE;
7362306a36Sopenharmony_ci		goto nla_failure;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	rcu_read_unlock();
7762306a36Sopenharmony_ci	unlock_sock_fast(sk, slow);
7862306a36Sopenharmony_ci	nla_nest_end(skb, start);
7962306a36Sopenharmony_ci	return 0;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cinla_failure:
8262306a36Sopenharmony_ci	rcu_read_unlock();
8362306a36Sopenharmony_ci	unlock_sock_fast(sk, slow);
8462306a36Sopenharmony_ci	nla_nest_cancel(skb, start);
8562306a36Sopenharmony_ci	return err;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic size_t subflow_get_info_size(const struct sock *sk)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	size_t size = 0;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	size += nla_total_size(0) +	/* INET_ULP_INFO_MPTCP */
9362306a36Sopenharmony_ci		nla_total_size(4) +	/* MPTCP_SUBFLOW_ATTR_TOKEN_REM */
9462306a36Sopenharmony_ci		nla_total_size(4) +	/* MPTCP_SUBFLOW_ATTR_TOKEN_LOC */
9562306a36Sopenharmony_ci		nla_total_size(4) +	/* MPTCP_SUBFLOW_ATTR_RELWRITE_SEQ */
9662306a36Sopenharmony_ci		nla_total_size_64bit(8) +	/* MPTCP_SUBFLOW_ATTR_MAP_SEQ */
9762306a36Sopenharmony_ci		nla_total_size(4) +	/* MPTCP_SUBFLOW_ATTR_MAP_SFSEQ */
9862306a36Sopenharmony_ci		nla_total_size(2) +	/* MPTCP_SUBFLOW_ATTR_SSN_OFFSET */
9962306a36Sopenharmony_ci		nla_total_size(2) +	/* MPTCP_SUBFLOW_ATTR_MAP_DATALEN */
10062306a36Sopenharmony_ci		nla_total_size(4) +	/* MPTCP_SUBFLOW_ATTR_FLAGS */
10162306a36Sopenharmony_ci		nla_total_size(1) +	/* MPTCP_SUBFLOW_ATTR_ID_REM */
10262306a36Sopenharmony_ci		nla_total_size(1) +	/* MPTCP_SUBFLOW_ATTR_ID_LOC */
10362306a36Sopenharmony_ci		0;
10462306a36Sopenharmony_ci	return size;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_civoid mptcp_diag_subflow_init(struct tcp_ulp_ops *ops)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	ops->get_info = subflow_get_info;
11062306a36Sopenharmony_ci	ops->get_info_size = subflow_get_info_size;
11162306a36Sopenharmony_ci}
112