18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Shared Memory Communications over RDMA (SMC-R) and RoCE
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  CLC (connection layer control) handshake over initial TCP socket to
68c2ecf20Sopenharmony_ci *  prepare for RDMA traffic
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *  Copyright IBM Corp. 2016, 2018
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *  Author(s):  Ursula Braun <ubraun@linux.vnet.ibm.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/in.h>
148c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
158c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
168c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
178c2ecf20Sopenharmony_ci#include <linux/utsname.h>
188c2ecf20Sopenharmony_ci#include <linux/ctype.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <net/addrconf.h>
218c2ecf20Sopenharmony_ci#include <net/sock.h>
228c2ecf20Sopenharmony_ci#include <net/tcp.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "smc.h"
258c2ecf20Sopenharmony_ci#include "smc_core.h"
268c2ecf20Sopenharmony_ci#include "smc_clc.h"
278c2ecf20Sopenharmony_ci#include "smc_ib.h"
288c2ecf20Sopenharmony_ci#include "smc_ism.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define SMCR_CLC_ACCEPT_CONFIRM_LEN 68
318c2ecf20Sopenharmony_ci#define SMCD_CLC_ACCEPT_CONFIRM_LEN 48
328c2ecf20Sopenharmony_ci#define SMCD_CLC_ACCEPT_CONFIRM_LEN_V2 78
338c2ecf20Sopenharmony_ci#define SMC_CLC_RECV_BUF_LEN	100
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* eye catcher "SMCR" EBCDIC for CLC messages */
368c2ecf20Sopenharmony_cistatic const char SMC_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xd9'};
378c2ecf20Sopenharmony_ci/* eye catcher "SMCD" EBCDIC for CLC messages */
388c2ecf20Sopenharmony_cistatic const char SMCD_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xc4'};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic u8 smc_hostname[SMC_MAX_HOSTNAME_LEN];
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* check arriving CLC proposal */
438c2ecf20Sopenharmony_cistatic bool smc_clc_msg_prop_valid(struct smc_clc_msg_proposal *pclc)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct smc_clc_msg_proposal_prefix *pclc_prfx;
468c2ecf20Sopenharmony_ci	struct smc_clc_smcd_v2_extension *smcd_v2_ext;
478c2ecf20Sopenharmony_ci	struct smc_clc_msg_hdr *hdr = &pclc->hdr;
488c2ecf20Sopenharmony_ci	struct smc_clc_v2_extension *v2_ext;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	v2_ext = smc_get_clc_v2_ext(pclc);
518c2ecf20Sopenharmony_ci	pclc_prfx = smc_clc_proposal_get_prefix(pclc);
528c2ecf20Sopenharmony_ci	if (hdr->version == SMC_V1) {
538c2ecf20Sopenharmony_ci		if (hdr->typev1 == SMC_TYPE_N)
548c2ecf20Sopenharmony_ci			return false;
558c2ecf20Sopenharmony_ci		if (ntohs(hdr->length) !=
568c2ecf20Sopenharmony_ci			sizeof(*pclc) + ntohs(pclc->iparea_offset) +
578c2ecf20Sopenharmony_ci			sizeof(*pclc_prfx) +
588c2ecf20Sopenharmony_ci			pclc_prfx->ipv6_prefixes_cnt *
598c2ecf20Sopenharmony_ci				sizeof(struct smc_clc_ipv6_prefix) +
608c2ecf20Sopenharmony_ci			sizeof(struct smc_clc_msg_trail))
618c2ecf20Sopenharmony_ci			return false;
628c2ecf20Sopenharmony_ci	} else {
638c2ecf20Sopenharmony_ci		if (ntohs(hdr->length) !=
648c2ecf20Sopenharmony_ci			sizeof(*pclc) +
658c2ecf20Sopenharmony_ci			sizeof(struct smc_clc_msg_smcd) +
668c2ecf20Sopenharmony_ci			(hdr->typev1 != SMC_TYPE_N ?
678c2ecf20Sopenharmony_ci				sizeof(*pclc_prfx) +
688c2ecf20Sopenharmony_ci				pclc_prfx->ipv6_prefixes_cnt *
698c2ecf20Sopenharmony_ci				sizeof(struct smc_clc_ipv6_prefix) : 0) +
708c2ecf20Sopenharmony_ci			(hdr->typev2 != SMC_TYPE_N ?
718c2ecf20Sopenharmony_ci				sizeof(*v2_ext) +
728c2ecf20Sopenharmony_ci				v2_ext->hdr.eid_cnt * SMC_MAX_EID_LEN : 0) +
738c2ecf20Sopenharmony_ci			(smcd_indicated(hdr->typev2) ?
748c2ecf20Sopenharmony_ci				sizeof(*smcd_v2_ext) + v2_ext->hdr.ism_gid_cnt *
758c2ecf20Sopenharmony_ci					sizeof(struct smc_clc_smcd_gid_chid) :
768c2ecf20Sopenharmony_ci				0) +
778c2ecf20Sopenharmony_ci			sizeof(struct smc_clc_msg_trail))
788c2ecf20Sopenharmony_ci			return false;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci	return true;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/* check arriving CLC accept or confirm */
848c2ecf20Sopenharmony_cistatic bool
858c2ecf20Sopenharmony_cismc_clc_msg_acc_conf_valid(struct smc_clc_msg_accept_confirm_v2 *clc_v2)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct smc_clc_msg_hdr *hdr = &clc_v2->hdr;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (hdr->typev1 != SMC_TYPE_R && hdr->typev1 != SMC_TYPE_D)
908c2ecf20Sopenharmony_ci		return false;
918c2ecf20Sopenharmony_ci	if (hdr->version == SMC_V1) {
928c2ecf20Sopenharmony_ci		if ((hdr->typev1 == SMC_TYPE_R &&
938c2ecf20Sopenharmony_ci		     ntohs(hdr->length) != SMCR_CLC_ACCEPT_CONFIRM_LEN) ||
948c2ecf20Sopenharmony_ci		    (hdr->typev1 == SMC_TYPE_D &&
958c2ecf20Sopenharmony_ci		     ntohs(hdr->length) != SMCD_CLC_ACCEPT_CONFIRM_LEN))
968c2ecf20Sopenharmony_ci			return false;
978c2ecf20Sopenharmony_ci	} else {
988c2ecf20Sopenharmony_ci		if (hdr->typev1 == SMC_TYPE_D &&
998c2ecf20Sopenharmony_ci		    ntohs(hdr->length) != SMCD_CLC_ACCEPT_CONFIRM_LEN_V2 &&
1008c2ecf20Sopenharmony_ci		    (ntohs(hdr->length) != SMCD_CLC_ACCEPT_CONFIRM_LEN_V2 +
1018c2ecf20Sopenharmony_ci				sizeof(struct smc_clc_first_contact_ext)))
1028c2ecf20Sopenharmony_ci			return false;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci	return true;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic void smc_clc_fill_fce(struct smc_clc_first_contact_ext *fce, int *len)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	memset(fce, 0, sizeof(*fce));
1108c2ecf20Sopenharmony_ci	fce->os_type = SMC_CLC_OS_LINUX;
1118c2ecf20Sopenharmony_ci	fce->release = SMC_RELEASE;
1128c2ecf20Sopenharmony_ci	memcpy(fce->hostname, smc_hostname, sizeof(smc_hostname));
1138c2ecf20Sopenharmony_ci	(*len) += sizeof(*fce);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/* check if received message has a correct header length and contains valid
1178c2ecf20Sopenharmony_ci * heading and trailing eyecatchers
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_cistatic bool smc_clc_msg_hdr_valid(struct smc_clc_msg_hdr *clcm, bool check_trl)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct smc_clc_msg_accept_confirm_v2 *clc_v2;
1228c2ecf20Sopenharmony_ci	struct smc_clc_msg_proposal *pclc;
1238c2ecf20Sopenharmony_ci	struct smc_clc_msg_decline *dclc;
1248c2ecf20Sopenharmony_ci	struct smc_clc_msg_trail *trl;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (memcmp(clcm->eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)) &&
1278c2ecf20Sopenharmony_ci	    memcmp(clcm->eyecatcher, SMCD_EYECATCHER, sizeof(SMCD_EYECATCHER)))
1288c2ecf20Sopenharmony_ci		return false;
1298c2ecf20Sopenharmony_ci	switch (clcm->type) {
1308c2ecf20Sopenharmony_ci	case SMC_CLC_PROPOSAL:
1318c2ecf20Sopenharmony_ci		pclc = (struct smc_clc_msg_proposal *)clcm;
1328c2ecf20Sopenharmony_ci		if (!smc_clc_msg_prop_valid(pclc))
1338c2ecf20Sopenharmony_ci			return false;
1348c2ecf20Sopenharmony_ci		trl = (struct smc_clc_msg_trail *)
1358c2ecf20Sopenharmony_ci			((u8 *)pclc + ntohs(pclc->hdr.length) - sizeof(*trl));
1368c2ecf20Sopenharmony_ci		break;
1378c2ecf20Sopenharmony_ci	case SMC_CLC_ACCEPT:
1388c2ecf20Sopenharmony_ci	case SMC_CLC_CONFIRM:
1398c2ecf20Sopenharmony_ci		clc_v2 = (struct smc_clc_msg_accept_confirm_v2 *)clcm;
1408c2ecf20Sopenharmony_ci		if (!smc_clc_msg_acc_conf_valid(clc_v2))
1418c2ecf20Sopenharmony_ci			return false;
1428c2ecf20Sopenharmony_ci		trl = (struct smc_clc_msg_trail *)
1438c2ecf20Sopenharmony_ci			((u8 *)clc_v2 + ntohs(clc_v2->hdr.length) -
1448c2ecf20Sopenharmony_ci							sizeof(*trl));
1458c2ecf20Sopenharmony_ci		break;
1468c2ecf20Sopenharmony_ci	case SMC_CLC_DECLINE:
1478c2ecf20Sopenharmony_ci		dclc = (struct smc_clc_msg_decline *)clcm;
1488c2ecf20Sopenharmony_ci		if (ntohs(dclc->hdr.length) != sizeof(*dclc))
1498c2ecf20Sopenharmony_ci			return false;
1508c2ecf20Sopenharmony_ci		trl = &dclc->trl;
1518c2ecf20Sopenharmony_ci		break;
1528c2ecf20Sopenharmony_ci	default:
1538c2ecf20Sopenharmony_ci		return false;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci	if (check_trl &&
1568c2ecf20Sopenharmony_ci	    memcmp(trl->eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)) &&
1578c2ecf20Sopenharmony_ci	    memcmp(trl->eyecatcher, SMCD_EYECATCHER, sizeof(SMCD_EYECATCHER)))
1588c2ecf20Sopenharmony_ci		return false;
1598c2ecf20Sopenharmony_ci	return true;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci/* find ipv4 addr on device and get the prefix len, fill CLC proposal msg */
1638c2ecf20Sopenharmony_cistatic int smc_clc_prfx_set4_rcu(struct dst_entry *dst, __be32 ipv4,
1648c2ecf20Sopenharmony_ci				 struct smc_clc_msg_proposal_prefix *prop)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct in_device *in_dev = __in_dev_get_rcu(dst->dev);
1678c2ecf20Sopenharmony_ci	const struct in_ifaddr *ifa;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (!in_dev)
1708c2ecf20Sopenharmony_ci		return -ENODEV;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1738c2ecf20Sopenharmony_ci		if (!inet_ifa_match(ipv4, ifa))
1748c2ecf20Sopenharmony_ci			continue;
1758c2ecf20Sopenharmony_ci		prop->prefix_len = inet_mask_len(ifa->ifa_mask);
1768c2ecf20Sopenharmony_ci		prop->outgoing_subnet = ifa->ifa_address & ifa->ifa_mask;
1778c2ecf20Sopenharmony_ci		/* prop->ipv6_prefixes_cnt = 0; already done by memset before */
1788c2ecf20Sopenharmony_ci		return 0;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci	return -ENOENT;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/* fill CLC proposal msg with ipv6 prefixes from device */
1848c2ecf20Sopenharmony_cistatic int smc_clc_prfx_set6_rcu(struct dst_entry *dst,
1858c2ecf20Sopenharmony_ci				 struct smc_clc_msg_proposal_prefix *prop,
1868c2ecf20Sopenharmony_ci				 struct smc_clc_ipv6_prefix *ipv6_prfx)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
1898c2ecf20Sopenharmony_ci	struct inet6_dev *in6_dev = __in6_dev_get(dst->dev);
1908c2ecf20Sopenharmony_ci	struct inet6_ifaddr *ifa;
1918c2ecf20Sopenharmony_ci	int cnt = 0;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (!in6_dev)
1948c2ecf20Sopenharmony_ci		return -ENODEV;
1958c2ecf20Sopenharmony_ci	/* use a maximum of 8 IPv6 prefixes from device */
1968c2ecf20Sopenharmony_ci	list_for_each_entry(ifa, &in6_dev->addr_list, if_list) {
1978c2ecf20Sopenharmony_ci		if (ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)
1988c2ecf20Sopenharmony_ci			continue;
1998c2ecf20Sopenharmony_ci		ipv6_addr_prefix(&ipv6_prfx[cnt].prefix,
2008c2ecf20Sopenharmony_ci				 &ifa->addr, ifa->prefix_len);
2018c2ecf20Sopenharmony_ci		ipv6_prfx[cnt].prefix_len = ifa->prefix_len;
2028c2ecf20Sopenharmony_ci		cnt++;
2038c2ecf20Sopenharmony_ci		if (cnt == SMC_CLC_MAX_V6_PREFIX)
2048c2ecf20Sopenharmony_ci			break;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci	prop->ipv6_prefixes_cnt = cnt;
2078c2ecf20Sopenharmony_ci	if (cnt)
2088c2ecf20Sopenharmony_ci		return 0;
2098c2ecf20Sopenharmony_ci#endif
2108c2ecf20Sopenharmony_ci	return -ENOENT;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/* retrieve and set prefixes in CLC proposal msg */
2148c2ecf20Sopenharmony_cistatic int smc_clc_prfx_set(struct socket *clcsock,
2158c2ecf20Sopenharmony_ci			    struct smc_clc_msg_proposal_prefix *prop,
2168c2ecf20Sopenharmony_ci			    struct smc_clc_ipv6_prefix *ipv6_prfx)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct dst_entry *dst = sk_dst_get(clcsock->sk);
2198c2ecf20Sopenharmony_ci	struct sockaddr_storage addrs;
2208c2ecf20Sopenharmony_ci	struct sockaddr_in6 *addr6;
2218c2ecf20Sopenharmony_ci	struct sockaddr_in *addr;
2228c2ecf20Sopenharmony_ci	int rc = -ENOENT;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	if (!dst) {
2258c2ecf20Sopenharmony_ci		rc = -ENOTCONN;
2268c2ecf20Sopenharmony_ci		goto out;
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci	if (!dst->dev) {
2298c2ecf20Sopenharmony_ci		rc = -ENODEV;
2308c2ecf20Sopenharmony_ci		goto out_rel;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci	/* get address to which the internal TCP socket is bound */
2338c2ecf20Sopenharmony_ci	if (kernel_getsockname(clcsock, (struct sockaddr *)&addrs) < 0)
2348c2ecf20Sopenharmony_ci		goto out_rel;
2358c2ecf20Sopenharmony_ci	/* analyze IP specific data of net_device belonging to TCP socket */
2368c2ecf20Sopenharmony_ci	addr6 = (struct sockaddr_in6 *)&addrs;
2378c2ecf20Sopenharmony_ci	rcu_read_lock();
2388c2ecf20Sopenharmony_ci	if (addrs.ss_family == PF_INET) {
2398c2ecf20Sopenharmony_ci		/* IPv4 */
2408c2ecf20Sopenharmony_ci		addr = (struct sockaddr_in *)&addrs;
2418c2ecf20Sopenharmony_ci		rc = smc_clc_prfx_set4_rcu(dst, addr->sin_addr.s_addr, prop);
2428c2ecf20Sopenharmony_ci	} else if (ipv6_addr_v4mapped(&addr6->sin6_addr)) {
2438c2ecf20Sopenharmony_ci		/* mapped IPv4 address - peer is IPv4 only */
2448c2ecf20Sopenharmony_ci		rc = smc_clc_prfx_set4_rcu(dst, addr6->sin6_addr.s6_addr32[3],
2458c2ecf20Sopenharmony_ci					   prop);
2468c2ecf20Sopenharmony_ci	} else {
2478c2ecf20Sopenharmony_ci		/* IPv6 */
2488c2ecf20Sopenharmony_ci		rc = smc_clc_prfx_set6_rcu(dst, prop, ipv6_prfx);
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci	rcu_read_unlock();
2518c2ecf20Sopenharmony_ciout_rel:
2528c2ecf20Sopenharmony_ci	dst_release(dst);
2538c2ecf20Sopenharmony_ciout:
2548c2ecf20Sopenharmony_ci	return rc;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci/* match ipv4 addrs of dev against addr in CLC proposal */
2588c2ecf20Sopenharmony_cistatic int smc_clc_prfx_match4_rcu(struct net_device *dev,
2598c2ecf20Sopenharmony_ci				   struct smc_clc_msg_proposal_prefix *prop)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct in_device *in_dev = __in_dev_get_rcu(dev);
2628c2ecf20Sopenharmony_ci	const struct in_ifaddr *ifa;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (!in_dev)
2658c2ecf20Sopenharmony_ci		return -ENODEV;
2668c2ecf20Sopenharmony_ci	in_dev_for_each_ifa_rcu(ifa, in_dev) {
2678c2ecf20Sopenharmony_ci		if (prop->prefix_len == inet_mask_len(ifa->ifa_mask) &&
2688c2ecf20Sopenharmony_ci		    inet_ifa_match(prop->outgoing_subnet, ifa))
2698c2ecf20Sopenharmony_ci			return 0;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return -ENOENT;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci/* match ipv6 addrs of dev against addrs in CLC proposal */
2768c2ecf20Sopenharmony_cistatic int smc_clc_prfx_match6_rcu(struct net_device *dev,
2778c2ecf20Sopenharmony_ci				   struct smc_clc_msg_proposal_prefix *prop)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
2808c2ecf20Sopenharmony_ci	struct inet6_dev *in6_dev = __in6_dev_get(dev);
2818c2ecf20Sopenharmony_ci	struct smc_clc_ipv6_prefix *ipv6_prfx;
2828c2ecf20Sopenharmony_ci	struct inet6_ifaddr *ifa;
2838c2ecf20Sopenharmony_ci	int i, max;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	if (!in6_dev)
2868c2ecf20Sopenharmony_ci		return -ENODEV;
2878c2ecf20Sopenharmony_ci	/* ipv6 prefix list starts behind smc_clc_msg_proposal_prefix */
2888c2ecf20Sopenharmony_ci	ipv6_prfx = (struct smc_clc_ipv6_prefix *)((u8 *)prop + sizeof(*prop));
2898c2ecf20Sopenharmony_ci	max = min_t(u8, prop->ipv6_prefixes_cnt, SMC_CLC_MAX_V6_PREFIX);
2908c2ecf20Sopenharmony_ci	list_for_each_entry(ifa, &in6_dev->addr_list, if_list) {
2918c2ecf20Sopenharmony_ci		if (ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)
2928c2ecf20Sopenharmony_ci			continue;
2938c2ecf20Sopenharmony_ci		for (i = 0; i < max; i++) {
2948c2ecf20Sopenharmony_ci			if (ifa->prefix_len == ipv6_prfx[i].prefix_len &&
2958c2ecf20Sopenharmony_ci			    ipv6_prefix_equal(&ifa->addr, &ipv6_prfx[i].prefix,
2968c2ecf20Sopenharmony_ci					      ifa->prefix_len))
2978c2ecf20Sopenharmony_ci				return 0;
2988c2ecf20Sopenharmony_ci		}
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci#endif
3018c2ecf20Sopenharmony_ci	return -ENOENT;
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci/* check if proposed prefixes match one of our device prefixes */
3058c2ecf20Sopenharmony_ciint smc_clc_prfx_match(struct socket *clcsock,
3068c2ecf20Sopenharmony_ci		       struct smc_clc_msg_proposal_prefix *prop)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	struct dst_entry *dst = sk_dst_get(clcsock->sk);
3098c2ecf20Sopenharmony_ci	int rc;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (!dst) {
3128c2ecf20Sopenharmony_ci		rc = -ENOTCONN;
3138c2ecf20Sopenharmony_ci		goto out;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci	if (!dst->dev) {
3168c2ecf20Sopenharmony_ci		rc = -ENODEV;
3178c2ecf20Sopenharmony_ci		goto out_rel;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	rcu_read_lock();
3208c2ecf20Sopenharmony_ci	if (!prop->ipv6_prefixes_cnt)
3218c2ecf20Sopenharmony_ci		rc = smc_clc_prfx_match4_rcu(dst->dev, prop);
3228c2ecf20Sopenharmony_ci	else
3238c2ecf20Sopenharmony_ci		rc = smc_clc_prfx_match6_rcu(dst->dev, prop);
3248c2ecf20Sopenharmony_ci	rcu_read_unlock();
3258c2ecf20Sopenharmony_ciout_rel:
3268c2ecf20Sopenharmony_ci	dst_release(dst);
3278c2ecf20Sopenharmony_ciout:
3288c2ecf20Sopenharmony_ci	return rc;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci/* Wait for data on the tcp-socket, analyze received data
3328c2ecf20Sopenharmony_ci * Returns:
3338c2ecf20Sopenharmony_ci * 0 if success and it was not a decline that we received.
3348c2ecf20Sopenharmony_ci * SMC_CLC_DECL_REPLY if decline received for fallback w/o another decl send.
3358c2ecf20Sopenharmony_ci * clcsock error, -EINTR, -ECONNRESET, -EPROTO otherwise.
3368c2ecf20Sopenharmony_ci */
3378c2ecf20Sopenharmony_ciint smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
3388c2ecf20Sopenharmony_ci		     u8 expected_type, unsigned long timeout)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	long rcvtimeo = smc->clcsock->sk->sk_rcvtimeo;
3418c2ecf20Sopenharmony_ci	struct sock *clc_sk = smc->clcsock->sk;
3428c2ecf20Sopenharmony_ci	struct smc_clc_msg_hdr *clcm = buf;
3438c2ecf20Sopenharmony_ci	struct msghdr msg = {NULL, 0};
3448c2ecf20Sopenharmony_ci	int reason_code = 0;
3458c2ecf20Sopenharmony_ci	struct kvec vec = {buf, buflen};
3468c2ecf20Sopenharmony_ci	int len, datlen, recvlen;
3478c2ecf20Sopenharmony_ci	bool check_trl = true;
3488c2ecf20Sopenharmony_ci	int krflags;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	/* peek the first few bytes to determine length of data to receive
3518c2ecf20Sopenharmony_ci	 * so we don't consume any subsequent CLC message or payload data
3528c2ecf20Sopenharmony_ci	 * in the TCP byte stream
3538c2ecf20Sopenharmony_ci	 */
3548c2ecf20Sopenharmony_ci	/*
3558c2ecf20Sopenharmony_ci	 * Caller must make sure that buflen is no less than
3568c2ecf20Sopenharmony_ci	 * sizeof(struct smc_clc_msg_hdr)
3578c2ecf20Sopenharmony_ci	 */
3588c2ecf20Sopenharmony_ci	krflags = MSG_PEEK | MSG_WAITALL;
3598c2ecf20Sopenharmony_ci	clc_sk->sk_rcvtimeo = timeout;
3608c2ecf20Sopenharmony_ci	iov_iter_kvec(&msg.msg_iter, READ, &vec, 1,
3618c2ecf20Sopenharmony_ci			sizeof(struct smc_clc_msg_hdr));
3628c2ecf20Sopenharmony_ci	len = sock_recvmsg(smc->clcsock, &msg, krflags);
3638c2ecf20Sopenharmony_ci	if (signal_pending(current)) {
3648c2ecf20Sopenharmony_ci		reason_code = -EINTR;
3658c2ecf20Sopenharmony_ci		clc_sk->sk_err = EINTR;
3668c2ecf20Sopenharmony_ci		smc->sk.sk_err = EINTR;
3678c2ecf20Sopenharmony_ci		goto out;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci	if (clc_sk->sk_err) {
3708c2ecf20Sopenharmony_ci		reason_code = -clc_sk->sk_err;
3718c2ecf20Sopenharmony_ci		if (clc_sk->sk_err == EAGAIN &&
3728c2ecf20Sopenharmony_ci		    expected_type == SMC_CLC_DECLINE)
3738c2ecf20Sopenharmony_ci			clc_sk->sk_err = 0; /* reset for fallback usage */
3748c2ecf20Sopenharmony_ci		else
3758c2ecf20Sopenharmony_ci			smc->sk.sk_err = clc_sk->sk_err;
3768c2ecf20Sopenharmony_ci		goto out;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci	if (!len) { /* peer has performed orderly shutdown */
3798c2ecf20Sopenharmony_ci		smc->sk.sk_err = ECONNRESET;
3808c2ecf20Sopenharmony_ci		reason_code = -ECONNRESET;
3818c2ecf20Sopenharmony_ci		goto out;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci	if (len < 0) {
3848c2ecf20Sopenharmony_ci		if (len != -EAGAIN || expected_type != SMC_CLC_DECLINE)
3858c2ecf20Sopenharmony_ci			smc->sk.sk_err = -len;
3868c2ecf20Sopenharmony_ci		reason_code = len;
3878c2ecf20Sopenharmony_ci		goto out;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci	datlen = ntohs(clcm->length);
3908c2ecf20Sopenharmony_ci	if ((len < sizeof(struct smc_clc_msg_hdr)) ||
3918c2ecf20Sopenharmony_ci	    (clcm->version < SMC_V1) ||
3928c2ecf20Sopenharmony_ci	    ((clcm->type != SMC_CLC_DECLINE) &&
3938c2ecf20Sopenharmony_ci	     (clcm->type != expected_type))) {
3948c2ecf20Sopenharmony_ci		smc->sk.sk_err = EPROTO;
3958c2ecf20Sopenharmony_ci		reason_code = -EPROTO;
3968c2ecf20Sopenharmony_ci		goto out;
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/* receive the complete CLC message */
4008c2ecf20Sopenharmony_ci	memset(&msg, 0, sizeof(struct msghdr));
4018c2ecf20Sopenharmony_ci	if (datlen > buflen) {
4028c2ecf20Sopenharmony_ci		check_trl = false;
4038c2ecf20Sopenharmony_ci		recvlen = buflen;
4048c2ecf20Sopenharmony_ci	} else {
4058c2ecf20Sopenharmony_ci		recvlen = datlen;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci	iov_iter_kvec(&msg.msg_iter, READ, &vec, 1, recvlen);
4088c2ecf20Sopenharmony_ci	krflags = MSG_WAITALL;
4098c2ecf20Sopenharmony_ci	len = sock_recvmsg(smc->clcsock, &msg, krflags);
4108c2ecf20Sopenharmony_ci	if (len < recvlen || !smc_clc_msg_hdr_valid(clcm, check_trl)) {
4118c2ecf20Sopenharmony_ci		smc->sk.sk_err = EPROTO;
4128c2ecf20Sopenharmony_ci		reason_code = -EPROTO;
4138c2ecf20Sopenharmony_ci		goto out;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci	datlen -= len;
4168c2ecf20Sopenharmony_ci	while (datlen) {
4178c2ecf20Sopenharmony_ci		u8 tmp[SMC_CLC_RECV_BUF_LEN];
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci		vec.iov_base = &tmp;
4208c2ecf20Sopenharmony_ci		vec.iov_len = SMC_CLC_RECV_BUF_LEN;
4218c2ecf20Sopenharmony_ci		/* receive remaining proposal message */
4228c2ecf20Sopenharmony_ci		recvlen = datlen > SMC_CLC_RECV_BUF_LEN ?
4238c2ecf20Sopenharmony_ci						SMC_CLC_RECV_BUF_LEN : datlen;
4248c2ecf20Sopenharmony_ci		iov_iter_kvec(&msg.msg_iter, READ, &vec, 1, recvlen);
4258c2ecf20Sopenharmony_ci		len = sock_recvmsg(smc->clcsock, &msg, krflags);
4268c2ecf20Sopenharmony_ci		datlen -= len;
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci	if (clcm->type == SMC_CLC_DECLINE) {
4298c2ecf20Sopenharmony_ci		struct smc_clc_msg_decline *dclc;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		dclc = (struct smc_clc_msg_decline *)clcm;
4328c2ecf20Sopenharmony_ci		reason_code = SMC_CLC_DECL_PEERDECL;
4338c2ecf20Sopenharmony_ci		smc->peer_diagnosis = ntohl(dclc->peer_diagnosis);
4348c2ecf20Sopenharmony_ci		if (((struct smc_clc_msg_decline *)buf)->hdr.typev2 &
4358c2ecf20Sopenharmony_ci						SMC_FIRST_CONTACT_MASK) {
4368c2ecf20Sopenharmony_ci			smc->conn.lgr->sync_err = 1;
4378c2ecf20Sopenharmony_ci			smc_lgr_terminate_sched(smc->conn.lgr);
4388c2ecf20Sopenharmony_ci		}
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ciout:
4428c2ecf20Sopenharmony_ci	clc_sk->sk_rcvtimeo = rcvtimeo;
4438c2ecf20Sopenharmony_ci	return reason_code;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci/* send CLC DECLINE message across internal TCP socket */
4478c2ecf20Sopenharmony_ciint smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info, u8 version)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	struct smc_clc_msg_decline dclc;
4508c2ecf20Sopenharmony_ci	struct msghdr msg;
4518c2ecf20Sopenharmony_ci	struct kvec vec;
4528c2ecf20Sopenharmony_ci	int len;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	memset(&dclc, 0, sizeof(dclc));
4558c2ecf20Sopenharmony_ci	memcpy(dclc.hdr.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER));
4568c2ecf20Sopenharmony_ci	dclc.hdr.type = SMC_CLC_DECLINE;
4578c2ecf20Sopenharmony_ci	dclc.hdr.length = htons(sizeof(struct smc_clc_msg_decline));
4588c2ecf20Sopenharmony_ci	dclc.hdr.version = version;
4598c2ecf20Sopenharmony_ci	dclc.os_type = version == SMC_V1 ? 0 : SMC_CLC_OS_LINUX;
4608c2ecf20Sopenharmony_ci	dclc.hdr.typev2 = (peer_diag_info == SMC_CLC_DECL_SYNCERR) ?
4618c2ecf20Sopenharmony_ci						SMC_FIRST_CONTACT_MASK : 0;
4628c2ecf20Sopenharmony_ci	if ((!smc->conn.lgr || !smc->conn.lgr->is_smcd) &&
4638c2ecf20Sopenharmony_ci	    smc_ib_is_valid_local_systemid())
4648c2ecf20Sopenharmony_ci		memcpy(dclc.id_for_peer, local_systemid,
4658c2ecf20Sopenharmony_ci		       sizeof(local_systemid));
4668c2ecf20Sopenharmony_ci	dclc.peer_diagnosis = htonl(peer_diag_info);
4678c2ecf20Sopenharmony_ci	memcpy(dclc.trl.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER));
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	memset(&msg, 0, sizeof(msg));
4708c2ecf20Sopenharmony_ci	vec.iov_base = &dclc;
4718c2ecf20Sopenharmony_ci	vec.iov_len = sizeof(struct smc_clc_msg_decline);
4728c2ecf20Sopenharmony_ci	len = kernel_sendmsg(smc->clcsock, &msg, &vec, 1,
4738c2ecf20Sopenharmony_ci			     sizeof(struct smc_clc_msg_decline));
4748c2ecf20Sopenharmony_ci	if (len < 0 || len < sizeof(struct smc_clc_msg_decline))
4758c2ecf20Sopenharmony_ci		len = -EPROTO;
4768c2ecf20Sopenharmony_ci	return len > 0 ? 0 : len;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci/* send CLC PROPOSAL message across internal TCP socket */
4808c2ecf20Sopenharmony_ciint smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	struct smc_clc_smcd_v2_extension *smcd_v2_ext;
4838c2ecf20Sopenharmony_ci	struct smc_clc_msg_proposal_prefix *pclc_prfx;
4848c2ecf20Sopenharmony_ci	struct smc_clc_msg_proposal *pclc_base;
4858c2ecf20Sopenharmony_ci	struct smc_clc_smcd_gid_chid *gidchids;
4868c2ecf20Sopenharmony_ci	struct smc_clc_msg_proposal_area *pclc;
4878c2ecf20Sopenharmony_ci	struct smc_clc_ipv6_prefix *ipv6_prfx;
4888c2ecf20Sopenharmony_ci	struct smc_clc_v2_extension *v2_ext;
4898c2ecf20Sopenharmony_ci	struct smc_clc_msg_smcd *pclc_smcd;
4908c2ecf20Sopenharmony_ci	struct smc_clc_msg_trail *trl;
4918c2ecf20Sopenharmony_ci	int len, i, plen, rc;
4928c2ecf20Sopenharmony_ci	int reason_code = 0;
4938c2ecf20Sopenharmony_ci	struct kvec vec[8];
4948c2ecf20Sopenharmony_ci	struct msghdr msg;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	pclc = kzalloc(sizeof(*pclc), GFP_KERNEL);
4978c2ecf20Sopenharmony_ci	if (!pclc)
4988c2ecf20Sopenharmony_ci		return -ENOMEM;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	pclc_base = &pclc->pclc_base;
5018c2ecf20Sopenharmony_ci	pclc_smcd = &pclc->pclc_smcd;
5028c2ecf20Sopenharmony_ci	pclc_prfx = &pclc->pclc_prfx;
5038c2ecf20Sopenharmony_ci	ipv6_prfx = pclc->pclc_prfx_ipv6;
5048c2ecf20Sopenharmony_ci	v2_ext = &pclc->pclc_v2_ext;
5058c2ecf20Sopenharmony_ci	smcd_v2_ext = &pclc->pclc_smcd_v2_ext;
5068c2ecf20Sopenharmony_ci	gidchids = pclc->pclc_gidchids;
5078c2ecf20Sopenharmony_ci	trl = &pclc->pclc_trl;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	pclc_base->hdr.version = SMC_V2;
5108c2ecf20Sopenharmony_ci	pclc_base->hdr.typev1 = ini->smc_type_v1;
5118c2ecf20Sopenharmony_ci	pclc_base->hdr.typev2 = ini->smc_type_v2;
5128c2ecf20Sopenharmony_ci	plen = sizeof(*pclc_base) + sizeof(*pclc_smcd) + sizeof(*trl);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	/* retrieve ip prefixes for CLC proposal msg */
5158c2ecf20Sopenharmony_ci	if (ini->smc_type_v1 != SMC_TYPE_N) {
5168c2ecf20Sopenharmony_ci		rc = smc_clc_prfx_set(smc->clcsock, pclc_prfx, ipv6_prfx);
5178c2ecf20Sopenharmony_ci		if (rc) {
5188c2ecf20Sopenharmony_ci			if (ini->smc_type_v2 == SMC_TYPE_N) {
5198c2ecf20Sopenharmony_ci				kfree(pclc);
5208c2ecf20Sopenharmony_ci				return SMC_CLC_DECL_CNFERR;
5218c2ecf20Sopenharmony_ci			}
5228c2ecf20Sopenharmony_ci			pclc_base->hdr.typev1 = SMC_TYPE_N;
5238c2ecf20Sopenharmony_ci		} else {
5248c2ecf20Sopenharmony_ci			pclc_base->iparea_offset = htons(sizeof(*pclc_smcd));
5258c2ecf20Sopenharmony_ci			plen += sizeof(*pclc_prfx) +
5268c2ecf20Sopenharmony_ci					pclc_prfx->ipv6_prefixes_cnt *
5278c2ecf20Sopenharmony_ci					sizeof(ipv6_prfx[0]);
5288c2ecf20Sopenharmony_ci		}
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	/* build SMC Proposal CLC message */
5328c2ecf20Sopenharmony_ci	memcpy(pclc_base->hdr.eyecatcher, SMC_EYECATCHER,
5338c2ecf20Sopenharmony_ci	       sizeof(SMC_EYECATCHER));
5348c2ecf20Sopenharmony_ci	pclc_base->hdr.type = SMC_CLC_PROPOSAL;
5358c2ecf20Sopenharmony_ci	if (smcr_indicated(ini->smc_type_v1)) {
5368c2ecf20Sopenharmony_ci		/* add SMC-R specifics */
5378c2ecf20Sopenharmony_ci		memcpy(pclc_base->lcl.id_for_peer, local_systemid,
5388c2ecf20Sopenharmony_ci		       sizeof(local_systemid));
5398c2ecf20Sopenharmony_ci		memcpy(pclc_base->lcl.gid, ini->ib_gid, SMC_GID_SIZE);
5408c2ecf20Sopenharmony_ci		memcpy(pclc_base->lcl.mac, &ini->ib_dev->mac[ini->ib_port - 1],
5418c2ecf20Sopenharmony_ci		       ETH_ALEN);
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci	if (smcd_indicated(ini->smc_type_v1)) {
5448c2ecf20Sopenharmony_ci		/* add SMC-D specifics */
5458c2ecf20Sopenharmony_ci		if (ini->ism_dev[0]) {
5468c2ecf20Sopenharmony_ci			pclc_smcd->ism.gid = htonll(ini->ism_dev[0]->local_gid);
5478c2ecf20Sopenharmony_ci			pclc_smcd->ism.chid =
5488c2ecf20Sopenharmony_ci				htons(smc_ism_get_chid(ini->ism_dev[0]));
5498c2ecf20Sopenharmony_ci		}
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci	if (ini->smc_type_v2 == SMC_TYPE_N) {
5528c2ecf20Sopenharmony_ci		pclc_smcd->v2_ext_offset = 0;
5538c2ecf20Sopenharmony_ci	} else {
5548c2ecf20Sopenharmony_ci		u16 v2_ext_offset;
5558c2ecf20Sopenharmony_ci		u8 *eid = NULL;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci		v2_ext_offset = sizeof(*pclc_smcd) -
5588c2ecf20Sopenharmony_ci			offsetofend(struct smc_clc_msg_smcd, v2_ext_offset);
5598c2ecf20Sopenharmony_ci		if (ini->smc_type_v1 != SMC_TYPE_N)
5608c2ecf20Sopenharmony_ci			v2_ext_offset += sizeof(*pclc_prfx) +
5618c2ecf20Sopenharmony_ci						pclc_prfx->ipv6_prefixes_cnt *
5628c2ecf20Sopenharmony_ci						sizeof(ipv6_prfx[0]);
5638c2ecf20Sopenharmony_ci		pclc_smcd->v2_ext_offset = htons(v2_ext_offset);
5648c2ecf20Sopenharmony_ci		v2_ext->hdr.eid_cnt = 0;
5658c2ecf20Sopenharmony_ci		v2_ext->hdr.ism_gid_cnt = ini->ism_offered_cnt;
5668c2ecf20Sopenharmony_ci		v2_ext->hdr.flag.release = SMC_RELEASE;
5678c2ecf20Sopenharmony_ci		v2_ext->hdr.flag.seid = 1;
5688c2ecf20Sopenharmony_ci		v2_ext->hdr.smcd_v2_ext_offset = htons(sizeof(*v2_ext) -
5698c2ecf20Sopenharmony_ci				offsetofend(struct smc_clnt_opts_area_hdr,
5708c2ecf20Sopenharmony_ci					    smcd_v2_ext_offset) +
5718c2ecf20Sopenharmony_ci				v2_ext->hdr.eid_cnt * SMC_MAX_EID_LEN);
5728c2ecf20Sopenharmony_ci		if (ini->ism_dev[0])
5738c2ecf20Sopenharmony_ci			smc_ism_get_system_eid(ini->ism_dev[0], &eid);
5748c2ecf20Sopenharmony_ci		else
5758c2ecf20Sopenharmony_ci			smc_ism_get_system_eid(ini->ism_dev[1], &eid);
5768c2ecf20Sopenharmony_ci		if (eid)
5778c2ecf20Sopenharmony_ci			memcpy(smcd_v2_ext->system_eid, eid, SMC_MAX_EID_LEN);
5788c2ecf20Sopenharmony_ci		plen += sizeof(*v2_ext) + sizeof(*smcd_v2_ext);
5798c2ecf20Sopenharmony_ci		if (ini->ism_offered_cnt) {
5808c2ecf20Sopenharmony_ci			for (i = 1; i <= ini->ism_offered_cnt; i++) {
5818c2ecf20Sopenharmony_ci				gidchids[i - 1].gid =
5828c2ecf20Sopenharmony_ci					htonll(ini->ism_dev[i]->local_gid);
5838c2ecf20Sopenharmony_ci				gidchids[i - 1].chid =
5848c2ecf20Sopenharmony_ci					htons(smc_ism_get_chid(ini->ism_dev[i]));
5858c2ecf20Sopenharmony_ci			}
5868c2ecf20Sopenharmony_ci			plen += ini->ism_offered_cnt *
5878c2ecf20Sopenharmony_ci				sizeof(struct smc_clc_smcd_gid_chid);
5888c2ecf20Sopenharmony_ci		}
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci	pclc_base->hdr.length = htons(plen);
5918c2ecf20Sopenharmony_ci	memcpy(trl->eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER));
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	/* send SMC Proposal CLC message */
5948c2ecf20Sopenharmony_ci	memset(&msg, 0, sizeof(msg));
5958c2ecf20Sopenharmony_ci	i = 0;
5968c2ecf20Sopenharmony_ci	vec[i].iov_base = pclc_base;
5978c2ecf20Sopenharmony_ci	vec[i++].iov_len = sizeof(*pclc_base);
5988c2ecf20Sopenharmony_ci	vec[i].iov_base = pclc_smcd;
5998c2ecf20Sopenharmony_ci	vec[i++].iov_len = sizeof(*pclc_smcd);
6008c2ecf20Sopenharmony_ci	if (ini->smc_type_v1 != SMC_TYPE_N) {
6018c2ecf20Sopenharmony_ci		vec[i].iov_base = pclc_prfx;
6028c2ecf20Sopenharmony_ci		vec[i++].iov_len = sizeof(*pclc_prfx);
6038c2ecf20Sopenharmony_ci		if (pclc_prfx->ipv6_prefixes_cnt > 0) {
6048c2ecf20Sopenharmony_ci			vec[i].iov_base = ipv6_prfx;
6058c2ecf20Sopenharmony_ci			vec[i++].iov_len = pclc_prfx->ipv6_prefixes_cnt *
6068c2ecf20Sopenharmony_ci					   sizeof(ipv6_prfx[0]);
6078c2ecf20Sopenharmony_ci		}
6088c2ecf20Sopenharmony_ci	}
6098c2ecf20Sopenharmony_ci	if (ini->smc_type_v2 != SMC_TYPE_N) {
6108c2ecf20Sopenharmony_ci		vec[i].iov_base = v2_ext;
6118c2ecf20Sopenharmony_ci		vec[i++].iov_len = sizeof(*v2_ext);
6128c2ecf20Sopenharmony_ci		vec[i].iov_base = smcd_v2_ext;
6138c2ecf20Sopenharmony_ci		vec[i++].iov_len = sizeof(*smcd_v2_ext);
6148c2ecf20Sopenharmony_ci		if (ini->ism_offered_cnt) {
6158c2ecf20Sopenharmony_ci			vec[i].iov_base = gidchids;
6168c2ecf20Sopenharmony_ci			vec[i++].iov_len = ini->ism_offered_cnt *
6178c2ecf20Sopenharmony_ci					sizeof(struct smc_clc_smcd_gid_chid);
6188c2ecf20Sopenharmony_ci		}
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci	vec[i].iov_base = trl;
6218c2ecf20Sopenharmony_ci	vec[i++].iov_len = sizeof(*trl);
6228c2ecf20Sopenharmony_ci	/* due to the few bytes needed for clc-handshake this cannot block */
6238c2ecf20Sopenharmony_ci	len = kernel_sendmsg(smc->clcsock, &msg, vec, i, plen);
6248c2ecf20Sopenharmony_ci	if (len < 0) {
6258c2ecf20Sopenharmony_ci		smc->sk.sk_err = smc->clcsock->sk->sk_err;
6268c2ecf20Sopenharmony_ci		reason_code = -smc->sk.sk_err;
6278c2ecf20Sopenharmony_ci	} else if (len < ntohs(pclc_base->hdr.length)) {
6288c2ecf20Sopenharmony_ci		reason_code = -ENETUNREACH;
6298c2ecf20Sopenharmony_ci		smc->sk.sk_err = -reason_code;
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	kfree(pclc);
6338c2ecf20Sopenharmony_ci	return reason_code;
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci/* build and send CLC CONFIRM / ACCEPT message */
6378c2ecf20Sopenharmony_cistatic int smc_clc_send_confirm_accept(struct smc_sock *smc,
6388c2ecf20Sopenharmony_ci				       struct smc_clc_msg_accept_confirm_v2 *clc_v2,
6398c2ecf20Sopenharmony_ci				       int first_contact, u8 version)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	struct smc_connection *conn = &smc->conn;
6428c2ecf20Sopenharmony_ci	struct smc_clc_msg_accept_confirm *clc;
6438c2ecf20Sopenharmony_ci	struct smc_clc_first_contact_ext fce;
6448c2ecf20Sopenharmony_ci	struct smc_clc_msg_trail trl;
6458c2ecf20Sopenharmony_ci	struct kvec vec[3];
6468c2ecf20Sopenharmony_ci	struct msghdr msg;
6478c2ecf20Sopenharmony_ci	int i, len;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	/* send SMC Confirm CLC msg */
6508c2ecf20Sopenharmony_ci	clc = (struct smc_clc_msg_accept_confirm *)clc_v2;
6518c2ecf20Sopenharmony_ci	clc->hdr.version = version;	/* SMC version */
6528c2ecf20Sopenharmony_ci	if (first_contact)
6538c2ecf20Sopenharmony_ci		clc->hdr.typev2 |= SMC_FIRST_CONTACT_MASK;
6548c2ecf20Sopenharmony_ci	if (conn->lgr->is_smcd) {
6558c2ecf20Sopenharmony_ci		/* SMC-D specific settings */
6568c2ecf20Sopenharmony_ci		memcpy(clc->hdr.eyecatcher, SMCD_EYECATCHER,
6578c2ecf20Sopenharmony_ci		       sizeof(SMCD_EYECATCHER));
6588c2ecf20Sopenharmony_ci		clc->hdr.typev1 = SMC_TYPE_D;
6598c2ecf20Sopenharmony_ci		clc->d0.gid = conn->lgr->smcd->local_gid;
6608c2ecf20Sopenharmony_ci		clc->d0.token = conn->rmb_desc->token;
6618c2ecf20Sopenharmony_ci		clc->d0.dmbe_size = conn->rmbe_size_short;
6628c2ecf20Sopenharmony_ci		clc->d0.dmbe_idx = 0;
6638c2ecf20Sopenharmony_ci		memcpy(&clc->d0.linkid, conn->lgr->id, SMC_LGR_ID_SIZE);
6648c2ecf20Sopenharmony_ci		if (version == SMC_V1) {
6658c2ecf20Sopenharmony_ci			clc->hdr.length = htons(SMCD_CLC_ACCEPT_CONFIRM_LEN);
6668c2ecf20Sopenharmony_ci		} else {
6678c2ecf20Sopenharmony_ci			u8 *eid = NULL;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci			clc_v2->chid = htons(smc_ism_get_chid(conn->lgr->smcd));
6708c2ecf20Sopenharmony_ci			smc_ism_get_system_eid(conn->lgr->smcd, &eid);
6718c2ecf20Sopenharmony_ci			if (eid)
6728c2ecf20Sopenharmony_ci				memcpy(clc_v2->eid, eid, SMC_MAX_EID_LEN);
6738c2ecf20Sopenharmony_ci			len = SMCD_CLC_ACCEPT_CONFIRM_LEN_V2;
6748c2ecf20Sopenharmony_ci			if (first_contact)
6758c2ecf20Sopenharmony_ci				smc_clc_fill_fce(&fce, &len);
6768c2ecf20Sopenharmony_ci			clc_v2->hdr.length = htons(len);
6778c2ecf20Sopenharmony_ci		}
6788c2ecf20Sopenharmony_ci		memcpy(trl.eyecatcher, SMCD_EYECATCHER,
6798c2ecf20Sopenharmony_ci		       sizeof(SMCD_EYECATCHER));
6808c2ecf20Sopenharmony_ci	} else {
6818c2ecf20Sopenharmony_ci		struct smc_link *link = conn->lnk;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci		/* SMC-R specific settings */
6848c2ecf20Sopenharmony_ci		link = conn->lnk;
6858c2ecf20Sopenharmony_ci		memcpy(clc->hdr.eyecatcher, SMC_EYECATCHER,
6868c2ecf20Sopenharmony_ci		       sizeof(SMC_EYECATCHER));
6878c2ecf20Sopenharmony_ci		clc->hdr.typev1 = SMC_TYPE_R;
6888c2ecf20Sopenharmony_ci		clc->hdr.length = htons(SMCR_CLC_ACCEPT_CONFIRM_LEN);
6898c2ecf20Sopenharmony_ci		memcpy(clc->r0.lcl.id_for_peer, local_systemid,
6908c2ecf20Sopenharmony_ci		       sizeof(local_systemid));
6918c2ecf20Sopenharmony_ci		memcpy(&clc->r0.lcl.gid, link->gid, SMC_GID_SIZE);
6928c2ecf20Sopenharmony_ci		memcpy(&clc->r0.lcl.mac, &link->smcibdev->mac[link->ibport - 1],
6938c2ecf20Sopenharmony_ci		       ETH_ALEN);
6948c2ecf20Sopenharmony_ci		hton24(clc->r0.qpn, link->roce_qp->qp_num);
6958c2ecf20Sopenharmony_ci		clc->r0.rmb_rkey =
6968c2ecf20Sopenharmony_ci			htonl(conn->rmb_desc->mr_rx[link->link_idx]->rkey);
6978c2ecf20Sopenharmony_ci		clc->r0.rmbe_idx = 1; /* for now: 1 RMB = 1 RMBE */
6988c2ecf20Sopenharmony_ci		clc->r0.rmbe_alert_token = htonl(conn->alert_token_local);
6998c2ecf20Sopenharmony_ci		switch (clc->hdr.type) {
7008c2ecf20Sopenharmony_ci		case SMC_CLC_ACCEPT:
7018c2ecf20Sopenharmony_ci			clc->r0.qp_mtu = link->path_mtu;
7028c2ecf20Sopenharmony_ci			break;
7038c2ecf20Sopenharmony_ci		case SMC_CLC_CONFIRM:
7048c2ecf20Sopenharmony_ci			clc->r0.qp_mtu = min(link->path_mtu, link->peer_mtu);
7058c2ecf20Sopenharmony_ci			break;
7068c2ecf20Sopenharmony_ci		}
7078c2ecf20Sopenharmony_ci		clc->r0.rmbe_size = conn->rmbe_size_short;
7088c2ecf20Sopenharmony_ci		clc->r0.rmb_dma_addr = cpu_to_be64((u64)sg_dma_address
7098c2ecf20Sopenharmony_ci				(conn->rmb_desc->sgt[link->link_idx].sgl));
7108c2ecf20Sopenharmony_ci		hton24(clc->r0.psn, link->psn_initial);
7118c2ecf20Sopenharmony_ci		memcpy(trl.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER));
7128c2ecf20Sopenharmony_ci	}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	memset(&msg, 0, sizeof(msg));
7158c2ecf20Sopenharmony_ci	i = 0;
7168c2ecf20Sopenharmony_ci	vec[i].iov_base = clc_v2;
7178c2ecf20Sopenharmony_ci	if (version > SMC_V1)
7188c2ecf20Sopenharmony_ci		vec[i++].iov_len = SMCD_CLC_ACCEPT_CONFIRM_LEN_V2 - sizeof(trl);
7198c2ecf20Sopenharmony_ci	else
7208c2ecf20Sopenharmony_ci		vec[i++].iov_len = (clc->hdr.typev1 == SMC_TYPE_D ?
7218c2ecf20Sopenharmony_ci						SMCD_CLC_ACCEPT_CONFIRM_LEN :
7228c2ecf20Sopenharmony_ci						SMCR_CLC_ACCEPT_CONFIRM_LEN) -
7238c2ecf20Sopenharmony_ci				   sizeof(trl);
7248c2ecf20Sopenharmony_ci	if (version > SMC_V1 && first_contact) {
7258c2ecf20Sopenharmony_ci		vec[i].iov_base = &fce;
7268c2ecf20Sopenharmony_ci		vec[i++].iov_len = sizeof(fce);
7278c2ecf20Sopenharmony_ci	}
7288c2ecf20Sopenharmony_ci	vec[i].iov_base = &trl;
7298c2ecf20Sopenharmony_ci	vec[i++].iov_len = sizeof(trl);
7308c2ecf20Sopenharmony_ci	return kernel_sendmsg(smc->clcsock, &msg, vec, 1,
7318c2ecf20Sopenharmony_ci			      ntohs(clc->hdr.length));
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci/* send CLC CONFIRM message across internal TCP socket */
7358c2ecf20Sopenharmony_ciint smc_clc_send_confirm(struct smc_sock *smc, bool clnt_first_contact,
7368c2ecf20Sopenharmony_ci			 u8 version)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	struct smc_clc_msg_accept_confirm_v2 cclc_v2;
7398c2ecf20Sopenharmony_ci	int reason_code = 0;
7408c2ecf20Sopenharmony_ci	int len;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	/* send SMC Confirm CLC msg */
7438c2ecf20Sopenharmony_ci	memset(&cclc_v2, 0, sizeof(cclc_v2));
7448c2ecf20Sopenharmony_ci	cclc_v2.hdr.type = SMC_CLC_CONFIRM;
7458c2ecf20Sopenharmony_ci	len = smc_clc_send_confirm_accept(smc, &cclc_v2, clnt_first_contact,
7468c2ecf20Sopenharmony_ci					  version);
7478c2ecf20Sopenharmony_ci	if (len < ntohs(cclc_v2.hdr.length)) {
7488c2ecf20Sopenharmony_ci		if (len >= 0) {
7498c2ecf20Sopenharmony_ci			reason_code = -ENETUNREACH;
7508c2ecf20Sopenharmony_ci			smc->sk.sk_err = -reason_code;
7518c2ecf20Sopenharmony_ci		} else {
7528c2ecf20Sopenharmony_ci			smc->sk.sk_err = smc->clcsock->sk->sk_err;
7538c2ecf20Sopenharmony_ci			reason_code = -smc->sk.sk_err;
7548c2ecf20Sopenharmony_ci		}
7558c2ecf20Sopenharmony_ci	}
7568c2ecf20Sopenharmony_ci	return reason_code;
7578c2ecf20Sopenharmony_ci}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci/* send CLC ACCEPT message across internal TCP socket */
7608c2ecf20Sopenharmony_ciint smc_clc_send_accept(struct smc_sock *new_smc, bool srv_first_contact,
7618c2ecf20Sopenharmony_ci			u8 version)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	struct smc_clc_msg_accept_confirm_v2 aclc_v2;
7648c2ecf20Sopenharmony_ci	int len;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	memset(&aclc_v2, 0, sizeof(aclc_v2));
7678c2ecf20Sopenharmony_ci	aclc_v2.hdr.type = SMC_CLC_ACCEPT;
7688c2ecf20Sopenharmony_ci	len = smc_clc_send_confirm_accept(new_smc, &aclc_v2, srv_first_contact,
7698c2ecf20Sopenharmony_ci					  version);
7708c2ecf20Sopenharmony_ci	if (len < ntohs(aclc_v2.hdr.length))
7718c2ecf20Sopenharmony_ci		len = len >= 0 ? -EPROTO : -new_smc->clcsock->sk->sk_err;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	return len > 0 ? 0 : len;
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_civoid __init smc_clc_init(void)
7778c2ecf20Sopenharmony_ci{
7788c2ecf20Sopenharmony_ci	struct new_utsname *u;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	memset(smc_hostname, _S, sizeof(smc_hostname)); /* ASCII blanks */
7818c2ecf20Sopenharmony_ci	u = utsname();
7828c2ecf20Sopenharmony_ci	memcpy(smc_hostname, u->nodename,
7838c2ecf20Sopenharmony_ci	       min_t(size_t, strlen(u->nodename), sizeof(smc_hostname)));
7848c2ecf20Sopenharmony_ci}
785