18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* MPTCP socket monitoring support
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2019 Red Hat
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Author: Davide Caratti <dcaratti@redhat.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/net.h>
118c2ecf20Sopenharmony_ci#include <linux/inet_diag.h>
128c2ecf20Sopenharmony_ci#include <net/netlink.h>
138c2ecf20Sopenharmony_ci#include <uapi/linux/mptcp.h>
148c2ecf20Sopenharmony_ci#include "protocol.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic int subflow_get_info(const struct sock *sk, struct sk_buff *skb)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	struct mptcp_subflow_context *sf;
198c2ecf20Sopenharmony_ci	struct nlattr *start;
208c2ecf20Sopenharmony_ci	u32 flags = 0;
218c2ecf20Sopenharmony_ci	int err;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	start = nla_nest_start_noflag(skb, INET_ULP_INFO_MPTCP);
248c2ecf20Sopenharmony_ci	if (!start)
258c2ecf20Sopenharmony_ci		return -EMSGSIZE;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	rcu_read_lock();
288c2ecf20Sopenharmony_ci	sf = rcu_dereference(inet_csk(sk)->icsk_ulp_data);
298c2ecf20Sopenharmony_ci	if (!sf) {
308c2ecf20Sopenharmony_ci		err = 0;
318c2ecf20Sopenharmony_ci		goto nla_failure;
328c2ecf20Sopenharmony_ci	}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	if (sf->mp_capable)
358c2ecf20Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_MCAP_REM;
368c2ecf20Sopenharmony_ci	if (sf->request_mptcp)
378c2ecf20Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_MCAP_LOC;
388c2ecf20Sopenharmony_ci	if (sf->mp_join)
398c2ecf20Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_JOIN_REM;
408c2ecf20Sopenharmony_ci	if (sf->request_join)
418c2ecf20Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_JOIN_LOC;
428c2ecf20Sopenharmony_ci	if (sf->backup)
438c2ecf20Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_BKUP_REM;
448c2ecf20Sopenharmony_ci	if (sf->request_bkup)
458c2ecf20Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_BKUP_LOC;
468c2ecf20Sopenharmony_ci	if (sf->fully_established)
478c2ecf20Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_FULLY_ESTABLISHED;
488c2ecf20Sopenharmony_ci	if (sf->conn_finished)
498c2ecf20Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_CONNECTED;
508c2ecf20Sopenharmony_ci	if (sf->map_valid)
518c2ecf20Sopenharmony_ci		flags |= MPTCP_SUBFLOW_FLAG_MAPVALID;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_TOKEN_REM, sf->remote_token) ||
548c2ecf20Sopenharmony_ci	    nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_TOKEN_LOC, sf->token) ||
558c2ecf20Sopenharmony_ci	    nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_RELWRITE_SEQ,
568c2ecf20Sopenharmony_ci			sf->rel_write_seq) ||
578c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, MPTCP_SUBFLOW_ATTR_MAP_SEQ, sf->map_seq,
588c2ecf20Sopenharmony_ci			      MPTCP_SUBFLOW_ATTR_PAD) ||
598c2ecf20Sopenharmony_ci	    nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_MAP_SFSEQ,
608c2ecf20Sopenharmony_ci			sf->map_subflow_seq) ||
618c2ecf20Sopenharmony_ci	    nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_SSN_OFFSET, sf->ssn_offset) ||
628c2ecf20Sopenharmony_ci	    nla_put_u16(skb, MPTCP_SUBFLOW_ATTR_MAP_DATALEN,
638c2ecf20Sopenharmony_ci			sf->map_data_len) ||
648c2ecf20Sopenharmony_ci	    nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_FLAGS, flags) ||
658c2ecf20Sopenharmony_ci	    nla_put_u8(skb, MPTCP_SUBFLOW_ATTR_ID_REM, sf->remote_id) ||
668c2ecf20Sopenharmony_ci	    nla_put_u8(skb, MPTCP_SUBFLOW_ATTR_ID_LOC, sf->local_id)) {
678c2ecf20Sopenharmony_ci		err = -EMSGSIZE;
688c2ecf20Sopenharmony_ci		goto nla_failure;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	rcu_read_unlock();
728c2ecf20Sopenharmony_ci	nla_nest_end(skb, start);
738c2ecf20Sopenharmony_ci	return 0;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cinla_failure:
768c2ecf20Sopenharmony_ci	rcu_read_unlock();
778c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, start);
788c2ecf20Sopenharmony_ci	return err;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic size_t subflow_get_info_size(const struct sock *sk)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	size_t size = 0;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	size += nla_total_size(0) +	/* INET_ULP_INFO_MPTCP */
868c2ecf20Sopenharmony_ci		nla_total_size(4) +	/* MPTCP_SUBFLOW_ATTR_TOKEN_REM */
878c2ecf20Sopenharmony_ci		nla_total_size(4) +	/* MPTCP_SUBFLOW_ATTR_TOKEN_LOC */
888c2ecf20Sopenharmony_ci		nla_total_size(4) +	/* MPTCP_SUBFLOW_ATTR_RELWRITE_SEQ */
898c2ecf20Sopenharmony_ci		nla_total_size_64bit(8) +	/* MPTCP_SUBFLOW_ATTR_MAP_SEQ */
908c2ecf20Sopenharmony_ci		nla_total_size(4) +	/* MPTCP_SUBFLOW_ATTR_MAP_SFSEQ */
918c2ecf20Sopenharmony_ci		nla_total_size(2) +	/* MPTCP_SUBFLOW_ATTR_SSN_OFFSET */
928c2ecf20Sopenharmony_ci		nla_total_size(2) +	/* MPTCP_SUBFLOW_ATTR_MAP_DATALEN */
938c2ecf20Sopenharmony_ci		nla_total_size(4) +	/* MPTCP_SUBFLOW_ATTR_FLAGS */
948c2ecf20Sopenharmony_ci		nla_total_size(1) +	/* MPTCP_SUBFLOW_ATTR_ID_REM */
958c2ecf20Sopenharmony_ci		nla_total_size(1) +	/* MPTCP_SUBFLOW_ATTR_ID_LOC */
968c2ecf20Sopenharmony_ci		0;
978c2ecf20Sopenharmony_ci	return size;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_civoid mptcp_diag_subflow_init(struct tcp_ulp_ops *ops)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	ops->get_info = subflow_get_info;
1038c2ecf20Sopenharmony_ci	ops->get_info_size = subflow_get_info_size;
1048c2ecf20Sopenharmony_ci}
105