18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Shared Memory Communications over RDMA (SMC-R) and RoCE
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Monitoring SMC transport protocol sockets
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2016
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Author(s):  Ursula Braun <ubraun@linux.vnet.ibm.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/types.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/sock_diag.h>
178c2ecf20Sopenharmony_ci#include <linux/inet_diag.h>
188c2ecf20Sopenharmony_ci#include <linux/smc_diag.h>
198c2ecf20Sopenharmony_ci#include <net/netlink.h>
208c2ecf20Sopenharmony_ci#include <net/smc.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "smc.h"
238c2ecf20Sopenharmony_ci#include "smc_core.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistruct smc_diag_dump_ctx {
268c2ecf20Sopenharmony_ci	int pos[2];
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic struct smc_diag_dump_ctx *smc_dump_context(struct netlink_callback *cb)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	return (struct smc_diag_dump_ctx *)cb->ctx;
328c2ecf20Sopenharmony_ci}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic void smc_gid_be16_convert(__u8 *buf, u8 *gid_raw)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
378c2ecf20Sopenharmony_ci		be16_to_cpu(((__be16 *)gid_raw)[0]),
388c2ecf20Sopenharmony_ci		be16_to_cpu(((__be16 *)gid_raw)[1]),
398c2ecf20Sopenharmony_ci		be16_to_cpu(((__be16 *)gid_raw)[2]),
408c2ecf20Sopenharmony_ci		be16_to_cpu(((__be16 *)gid_raw)[3]),
418c2ecf20Sopenharmony_ci		be16_to_cpu(((__be16 *)gid_raw)[4]),
428c2ecf20Sopenharmony_ci		be16_to_cpu(((__be16 *)gid_raw)[5]),
438c2ecf20Sopenharmony_ci		be16_to_cpu(((__be16 *)gid_raw)[6]),
448c2ecf20Sopenharmony_ci		be16_to_cpu(((__be16 *)gid_raw)[7]));
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic void smc_diag_msg_common_fill(struct smc_diag_msg *r, struct sock *sk)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	struct smc_sock *smc = smc_sk(sk);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	memset(r, 0, sizeof(*r));
528c2ecf20Sopenharmony_ci	r->diag_family = sk->sk_family;
538c2ecf20Sopenharmony_ci	sock_diag_save_cookie(sk, r->id.idiag_cookie);
548c2ecf20Sopenharmony_ci	if (!smc->clcsock)
558c2ecf20Sopenharmony_ci		return;
568c2ecf20Sopenharmony_ci	r->id.idiag_sport = htons(smc->clcsock->sk->sk_num);
578c2ecf20Sopenharmony_ci	r->id.idiag_dport = smc->clcsock->sk->sk_dport;
588c2ecf20Sopenharmony_ci	r->id.idiag_if = smc->clcsock->sk->sk_bound_dev_if;
598c2ecf20Sopenharmony_ci	if (sk->sk_protocol == SMCPROTO_SMC) {
608c2ecf20Sopenharmony_ci		r->id.idiag_src[0] = smc->clcsock->sk->sk_rcv_saddr;
618c2ecf20Sopenharmony_ci		r->id.idiag_dst[0] = smc->clcsock->sk->sk_daddr;
628c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
638c2ecf20Sopenharmony_ci	} else if (sk->sk_protocol == SMCPROTO_SMC6) {
648c2ecf20Sopenharmony_ci		memcpy(&r->id.idiag_src, &smc->clcsock->sk->sk_v6_rcv_saddr,
658c2ecf20Sopenharmony_ci		       sizeof(smc->clcsock->sk->sk_v6_rcv_saddr));
668c2ecf20Sopenharmony_ci		memcpy(&r->id.idiag_dst, &smc->clcsock->sk->sk_v6_daddr,
678c2ecf20Sopenharmony_ci		       sizeof(smc->clcsock->sk->sk_v6_daddr));
688c2ecf20Sopenharmony_ci#endif
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int smc_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
738c2ecf20Sopenharmony_ci				   struct smc_diag_msg *r,
748c2ecf20Sopenharmony_ci				   struct user_namespace *user_ns)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	if (nla_put_u8(skb, SMC_DIAG_SHUTDOWN, sk->sk_shutdown))
778c2ecf20Sopenharmony_ci		return 1;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	r->diag_uid = from_kuid_munged(user_ns, sock_i_uid(sk));
808c2ecf20Sopenharmony_ci	r->diag_inode = sock_i_ino(sk);
818c2ecf20Sopenharmony_ci	return 0;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int __smc_diag_dump(struct sock *sk, struct sk_buff *skb,
858c2ecf20Sopenharmony_ci			   struct netlink_callback *cb,
868c2ecf20Sopenharmony_ci			   const struct smc_diag_req *req,
878c2ecf20Sopenharmony_ci			   struct nlattr *bc)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct smc_sock *smc = smc_sk(sk);
908c2ecf20Sopenharmony_ci	struct smc_diag_fallback fallback;
918c2ecf20Sopenharmony_ci	struct user_namespace *user_ns;
928c2ecf20Sopenharmony_ci	struct smc_diag_msg *r;
938c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
968c2ecf20Sopenharmony_ci			cb->nlh->nlmsg_type, sizeof(*r), NLM_F_MULTI);
978c2ecf20Sopenharmony_ci	if (!nlh)
988c2ecf20Sopenharmony_ci		return -EMSGSIZE;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	r = nlmsg_data(nlh);
1018c2ecf20Sopenharmony_ci	smc_diag_msg_common_fill(r, sk);
1028c2ecf20Sopenharmony_ci	r->diag_state = sk->sk_state;
1038c2ecf20Sopenharmony_ci	if (smc->use_fallback)
1048c2ecf20Sopenharmony_ci		r->diag_mode = SMC_DIAG_MODE_FALLBACK_TCP;
1058c2ecf20Sopenharmony_ci	else if (smc->conn.lgr && smc->conn.lgr->is_smcd)
1068c2ecf20Sopenharmony_ci		r->diag_mode = SMC_DIAG_MODE_SMCD;
1078c2ecf20Sopenharmony_ci	else
1088c2ecf20Sopenharmony_ci		r->diag_mode = SMC_DIAG_MODE_SMCR;
1098c2ecf20Sopenharmony_ci	user_ns = sk_user_ns(NETLINK_CB(cb->skb).sk);
1108c2ecf20Sopenharmony_ci	if (smc_diag_msg_attrs_fill(sk, skb, r, user_ns))
1118c2ecf20Sopenharmony_ci		goto errout;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	fallback.reason = smc->fallback_rsn;
1148c2ecf20Sopenharmony_ci	fallback.peer_diagnosis = smc->peer_diagnosis;
1158c2ecf20Sopenharmony_ci	if (nla_put(skb, SMC_DIAG_FALLBACK, sizeof(fallback), &fallback) < 0)
1168c2ecf20Sopenharmony_ci		goto errout;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if ((req->diag_ext & (1 << (SMC_DIAG_CONNINFO - 1))) &&
1198c2ecf20Sopenharmony_ci	    smc->conn.alert_token_local) {
1208c2ecf20Sopenharmony_ci		struct smc_connection *conn = &smc->conn;
1218c2ecf20Sopenharmony_ci		struct smc_diag_conninfo cinfo = {
1228c2ecf20Sopenharmony_ci			.token = conn->alert_token_local,
1238c2ecf20Sopenharmony_ci			.sndbuf_size = conn->sndbuf_desc ?
1248c2ecf20Sopenharmony_ci				conn->sndbuf_desc->len : 0,
1258c2ecf20Sopenharmony_ci			.rmbe_size = conn->rmb_desc ? conn->rmb_desc->len : 0,
1268c2ecf20Sopenharmony_ci			.peer_rmbe_size = conn->peer_rmbe_size,
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci			.rx_prod.wrap = conn->local_rx_ctrl.prod.wrap,
1298c2ecf20Sopenharmony_ci			.rx_prod.count = conn->local_rx_ctrl.prod.count,
1308c2ecf20Sopenharmony_ci			.rx_cons.wrap = conn->local_rx_ctrl.cons.wrap,
1318c2ecf20Sopenharmony_ci			.rx_cons.count = conn->local_rx_ctrl.cons.count,
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci			.tx_prod.wrap = conn->local_tx_ctrl.prod.wrap,
1348c2ecf20Sopenharmony_ci			.tx_prod.count = conn->local_tx_ctrl.prod.count,
1358c2ecf20Sopenharmony_ci			.tx_cons.wrap = conn->local_tx_ctrl.cons.wrap,
1368c2ecf20Sopenharmony_ci			.tx_cons.count = conn->local_tx_ctrl.cons.count,
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci			.tx_prod_flags =
1398c2ecf20Sopenharmony_ci				*(u8 *)&conn->local_tx_ctrl.prod_flags,
1408c2ecf20Sopenharmony_ci			.tx_conn_state_flags =
1418c2ecf20Sopenharmony_ci				*(u8 *)&conn->local_tx_ctrl.conn_state_flags,
1428c2ecf20Sopenharmony_ci			.rx_prod_flags = *(u8 *)&conn->local_rx_ctrl.prod_flags,
1438c2ecf20Sopenharmony_ci			.rx_conn_state_flags =
1448c2ecf20Sopenharmony_ci				*(u8 *)&conn->local_rx_ctrl.conn_state_flags,
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci			.tx_prep.wrap = conn->tx_curs_prep.wrap,
1478c2ecf20Sopenharmony_ci			.tx_prep.count = conn->tx_curs_prep.count,
1488c2ecf20Sopenharmony_ci			.tx_sent.wrap = conn->tx_curs_sent.wrap,
1498c2ecf20Sopenharmony_ci			.tx_sent.count = conn->tx_curs_sent.count,
1508c2ecf20Sopenharmony_ci			.tx_fin.wrap = conn->tx_curs_fin.wrap,
1518c2ecf20Sopenharmony_ci			.tx_fin.count = conn->tx_curs_fin.count,
1528c2ecf20Sopenharmony_ci		};
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		if (nla_put(skb, SMC_DIAG_CONNINFO, sizeof(cinfo), &cinfo) < 0)
1558c2ecf20Sopenharmony_ci			goto errout;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (smc->conn.lgr && !smc->conn.lgr->is_smcd &&
1598c2ecf20Sopenharmony_ci	    (req->diag_ext & (1 << (SMC_DIAG_LGRINFO - 1))) &&
1608c2ecf20Sopenharmony_ci	    !list_empty(&smc->conn.lgr->list)) {
1618c2ecf20Sopenharmony_ci		struct smc_diag_lgrinfo linfo = {
1628c2ecf20Sopenharmony_ci			.role = smc->conn.lgr->role,
1638c2ecf20Sopenharmony_ci			.lnk[0].ibport = smc->conn.lgr->lnk[0].ibport,
1648c2ecf20Sopenharmony_ci			.lnk[0].link_id = smc->conn.lgr->lnk[0].link_id,
1658c2ecf20Sopenharmony_ci		};
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		memcpy(linfo.lnk[0].ibname,
1688c2ecf20Sopenharmony_ci		       smc->conn.lgr->lnk[0].smcibdev->ibdev->name,
1698c2ecf20Sopenharmony_ci		       sizeof(smc->conn.lgr->lnk[0].smcibdev->ibdev->name));
1708c2ecf20Sopenharmony_ci		smc_gid_be16_convert(linfo.lnk[0].gid,
1718c2ecf20Sopenharmony_ci				     smc->conn.lgr->lnk[0].gid);
1728c2ecf20Sopenharmony_ci		smc_gid_be16_convert(linfo.lnk[0].peer_gid,
1738c2ecf20Sopenharmony_ci				     smc->conn.lgr->lnk[0].peer_gid);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		if (nla_put(skb, SMC_DIAG_LGRINFO, sizeof(linfo), &linfo) < 0)
1768c2ecf20Sopenharmony_ci			goto errout;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci	if (smc->conn.lgr && smc->conn.lgr->is_smcd &&
1798c2ecf20Sopenharmony_ci	    (req->diag_ext & (1 << (SMC_DIAG_DMBINFO - 1))) &&
1808c2ecf20Sopenharmony_ci	    !list_empty(&smc->conn.lgr->list) && smc->conn.rmb_desc) {
1818c2ecf20Sopenharmony_ci		struct smc_connection *conn = &smc->conn;
1828c2ecf20Sopenharmony_ci		struct smcd_diag_dmbinfo dinfo;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		memset(&dinfo, 0, sizeof(dinfo));
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		dinfo.linkid = *((u32 *)conn->lgr->id);
1878c2ecf20Sopenharmony_ci		dinfo.peer_gid = conn->lgr->peer_gid;
1888c2ecf20Sopenharmony_ci		dinfo.my_gid = conn->lgr->smcd->local_gid;
1898c2ecf20Sopenharmony_ci		dinfo.token = conn->rmb_desc->token;
1908c2ecf20Sopenharmony_ci		dinfo.peer_token = conn->peer_token;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		if (nla_put(skb, SMC_DIAG_DMBINFO, sizeof(dinfo), &dinfo) < 0)
1938c2ecf20Sopenharmony_ci			goto errout;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
1978c2ecf20Sopenharmony_ci	return 0;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cierrout:
2008c2ecf20Sopenharmony_ci	nlmsg_cancel(skb, nlh);
2018c2ecf20Sopenharmony_ci	return -EMSGSIZE;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int smc_diag_dump_proto(struct proto *prot, struct sk_buff *skb,
2058c2ecf20Sopenharmony_ci			       struct netlink_callback *cb, int p_type)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct smc_diag_dump_ctx *cb_ctx = smc_dump_context(cb);
2088c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
2098c2ecf20Sopenharmony_ci	int snum = cb_ctx->pos[p_type];
2108c2ecf20Sopenharmony_ci	struct nlattr *bc = NULL;
2118c2ecf20Sopenharmony_ci	struct hlist_head *head;
2128c2ecf20Sopenharmony_ci	int rc = 0, num = 0;
2138c2ecf20Sopenharmony_ci	struct sock *sk;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	read_lock(&prot->h.smc_hash->lock);
2168c2ecf20Sopenharmony_ci	head = &prot->h.smc_hash->ht;
2178c2ecf20Sopenharmony_ci	if (hlist_empty(head))
2188c2ecf20Sopenharmony_ci		goto out;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	sk_for_each(sk, head) {
2218c2ecf20Sopenharmony_ci		if (!net_eq(sock_net(sk), net))
2228c2ecf20Sopenharmony_ci			continue;
2238c2ecf20Sopenharmony_ci		if (num < snum)
2248c2ecf20Sopenharmony_ci			goto next;
2258c2ecf20Sopenharmony_ci		rc = __smc_diag_dump(sk, skb, cb, nlmsg_data(cb->nlh), bc);
2268c2ecf20Sopenharmony_ci		if (rc < 0)
2278c2ecf20Sopenharmony_ci			goto out;
2288c2ecf20Sopenharmony_cinext:
2298c2ecf20Sopenharmony_ci		num++;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ciout:
2338c2ecf20Sopenharmony_ci	read_unlock(&prot->h.smc_hash->lock);
2348c2ecf20Sopenharmony_ci	cb_ctx->pos[p_type] = num;
2358c2ecf20Sopenharmony_ci	return rc;
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic int smc_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	int rc = 0;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	rc = smc_diag_dump_proto(&smc_proto, skb, cb, SMCPROTO_SMC);
2438c2ecf20Sopenharmony_ci	if (!rc)
2448c2ecf20Sopenharmony_ci		smc_diag_dump_proto(&smc_proto6, skb, cb, SMCPROTO_SMC6);
2458c2ecf20Sopenharmony_ci	return skb->len;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic int smc_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY &&
2538c2ecf20Sopenharmony_ci	    h->nlmsg_flags & NLM_F_DUMP) {
2548c2ecf20Sopenharmony_ci		{
2558c2ecf20Sopenharmony_ci			struct netlink_dump_control c = {
2568c2ecf20Sopenharmony_ci				.dump = smc_diag_dump,
2578c2ecf20Sopenharmony_ci				.min_dump_alloc = SKB_WITH_OVERHEAD(32768),
2588c2ecf20Sopenharmony_ci			};
2598c2ecf20Sopenharmony_ci			return netlink_dump_start(net->diag_nlsk, skb, h, &c);
2608c2ecf20Sopenharmony_ci		}
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci	return 0;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic const struct sock_diag_handler smc_diag_handler = {
2668c2ecf20Sopenharmony_ci	.family = AF_SMC,
2678c2ecf20Sopenharmony_ci	.dump = smc_diag_handler_dump,
2688c2ecf20Sopenharmony_ci};
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic int __init smc_diag_init(void)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	return sock_diag_register(&smc_diag_handler);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic void __exit smc_diag_exit(void)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	sock_diag_unregister(&smc_diag_handler);
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cimodule_init(smc_diag_init);
2818c2ecf20Sopenharmony_cimodule_exit(smc_diag_exit);
2828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2838c2ecf20Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 43 /* AF_SMC */);
284