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