net/smc: add ipv6 support to CLC layer

The CLC layer is updated to support ipv6 proposal messages from peers and
to match incoming proposal messages against the ipv6 addresses of the net
device. struct smc_clc_ipv6_prefix is updated to provide the space for an
ipv6 address (struct was not used before). SMC_CLC_MAX_LEN is updated to
include the size of the proposal prefix. Existing code in net is not
affected, the previous SMC_CLC_MAX_LEN value is large enough to hold ipv4
proposal messages.

Signed-off-by: Karsten Graul <kgraul@linux.vnet.ibm.com>
Signed-off-by: Ursula Braun <ubraun@linux.vnet.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Karsten Graul 2018-03-16 15:06:40 +01:00 committed by David S. Miller
parent c246d942ea
commit 1a26d0201d
2 changed files with 105 additions and 17 deletions

View File

@ -5,7 +5,7 @@
* CLC (connection layer control) handshake over initial TCP socket to
* prepare for RDMA traffic
*
* Copyright IBM Corp. 2016
* Copyright IBM Corp. 2016, 2018
*
* Author(s): Ursula Braun <ubraun@linux.vnet.ibm.com>
*/
@ -15,6 +15,7 @@
#include <linux/if_ether.h>
#include <linux/sched/signal.h>
#include <net/addrconf.h>
#include <net/sock.h>
#include <net/tcp.h>
@ -93,12 +94,44 @@ static int smc_clc_prfx_set4_rcu(struct dst_entry *dst, __be32 ipv4,
return -ENOENT;
}
/* fill CLC proposal msg with ipv6 prefixes from device */
static int smc_clc_prfx_set6_rcu(struct dst_entry *dst,
struct smc_clc_msg_proposal_prefix *prop,
struct smc_clc_ipv6_prefix *ipv6_prfx)
{
#if IS_ENABLED(CONFIG_IPV6)
struct inet6_dev *in6_dev = __in6_dev_get(dst->dev);
struct inet6_ifaddr *ifa;
int cnt = 0;
if (!in6_dev)
return -ENODEV;
/* use a maximum of 8 IPv6 prefixes from device */
list_for_each_entry(ifa, &in6_dev->addr_list, if_list) {
if (ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)
continue;
ipv6_addr_prefix(&ipv6_prfx[cnt].prefix,
&ifa->addr, ifa->prefix_len);
ipv6_prfx[cnt].prefix_len = ifa->prefix_len;
cnt++;
if (cnt == SMC_CLC_MAX_V6_PREFIX)
break;
}
prop->ipv6_prefixes_cnt = cnt;
if (cnt)
return 0;
#endif
return -ENOENT;
}
/* retrieve and set prefixes in CLC proposal msg */
static int smc_clc_prfx_set(struct socket *clcsock,
struct smc_clc_msg_proposal_prefix *prop)
struct smc_clc_msg_proposal_prefix *prop,
struct smc_clc_ipv6_prefix *ipv6_prfx)
{
struct dst_entry *dst = sk_dst_get(clcsock->sk);
struct sockaddr_storage addrs;
struct sockaddr_in6 *addr6;
struct sockaddr_in *addr;
int rc = -ENOENT;
@ -114,11 +147,19 @@ static int smc_clc_prfx_set(struct socket *clcsock,
/* get address to which the internal TCP socket is bound */
kernel_getsockname(clcsock, (struct sockaddr *)&addrs);
/* analyze IP specific data of net_device belonging to TCP socket */
addr6 = (struct sockaddr_in6 *)&addrs;
rcu_read_lock();
if (addrs.ss_family == PF_INET) {
/* IPv4 */
addr = (struct sockaddr_in *)&addrs;
rc = smc_clc_prfx_set4_rcu(dst, addr->sin_addr.s_addr, prop);
} else if (ipv6_addr_v4mapped(&addr6->sin6_addr)) {
/* mapped IPv4 address - peer is IPv4 only */
rc = smc_clc_prfx_set4_rcu(dst, addr6->sin6_addr.s6_addr32[3],
prop);
} else {
/* IPv6 */
rc = smc_clc_prfx_set6_rcu(dst, prop, ipv6_prfx);
}
rcu_read_unlock();
out_rel:
@ -144,12 +185,41 @@ static int smc_clc_prfx_match4_rcu(struct net_device *dev,
return -ENOENT;
}
/* match ipv6 addrs of dev against addrs in CLC proposal */
static int smc_clc_prfx_match6_rcu(struct net_device *dev,
struct smc_clc_msg_proposal_prefix *prop)
{
#if IS_ENABLED(CONFIG_IPV6)
struct inet6_dev *in6_dev = __in6_dev_get(dev);
struct smc_clc_ipv6_prefix *ipv6_prfx;
struct inet6_ifaddr *ifa;
int i, max;
if (!in6_dev)
return -ENODEV;
/* ipv6 prefix list starts behind smc_clc_msg_proposal_prefix */
ipv6_prfx = (struct smc_clc_ipv6_prefix *)((u8 *)prop + sizeof(*prop));
max = min_t(u8, prop->ipv6_prefixes_cnt, SMC_CLC_MAX_V6_PREFIX);
list_for_each_entry(ifa, &in6_dev->addr_list, if_list) {
if (ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)
continue;
for (i = 0; i < max; i++) {
if (ifa->prefix_len == ipv6_prfx[i].prefix_len &&
ipv6_prefix_equal(&ifa->addr, &ipv6_prfx[i].prefix,
ifa->prefix_len))
return 0;
}
}
#endif
return -ENOENT;
}
/* check if proposed prefixes match one of our device prefixes */
int smc_clc_prfx_match(struct socket *clcsock,
struct smc_clc_msg_proposal_prefix *prop)
{
struct dst_entry *dst = sk_dst_get(clcsock->sk);
int rc = -ENOENT;
int rc;
if (!dst) {
rc = -ENOTCONN;
@ -162,6 +232,8 @@ int smc_clc_prfx_match(struct socket *clcsock,
rcu_read_lock();
if (!prop->ipv6_prefixes_cnt)
rc = smc_clc_prfx_match4_rcu(dst->dev, prop);
else
rc = smc_clc_prfx_match6_rcu(dst->dev, prop);
rcu_read_unlock();
out_rel:
dst_release(dst);
@ -288,21 +360,24 @@ int smc_clc_send_proposal(struct smc_sock *smc,
struct smc_ib_device *smcibdev,
u8 ibport)
{
struct smc_clc_ipv6_prefix ipv6_prfx[SMC_CLC_MAX_V6_PREFIX];
struct smc_clc_msg_proposal_prefix pclc_prfx;
struct smc_clc_msg_proposal pclc;
struct smc_clc_msg_trail trl;
int len, i, plen, rc;
int reason_code = 0;
struct kvec vec[3];
struct kvec vec[4];
struct msghdr msg;
int len, plen, rc;
/* retrieve ip prefixes for CLC proposal msg */
rc = smc_clc_prfx_set(smc->clcsock, &pclc_prfx);
rc = smc_clc_prfx_set(smc->clcsock, &pclc_prfx, ipv6_prfx);
if (rc)
return SMC_CLC_DECL_CNFERR; /* configuration error */
/* send SMC Proposal CLC message */
plen = sizeof(pclc) + sizeof(pclc_prfx) + sizeof(trl);
plen = sizeof(pclc) + sizeof(pclc_prfx) +
(pclc_prfx.ipv6_prefixes_cnt * sizeof(ipv6_prfx[0])) +
sizeof(trl);
memset(&pclc, 0, sizeof(pclc));
memcpy(pclc.hdr.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER));
pclc.hdr.type = SMC_CLC_PROPOSAL;
@ -315,14 +390,20 @@ int smc_clc_send_proposal(struct smc_sock *smc,
memcpy(trl.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER));
memset(&msg, 0, sizeof(msg));
vec[0].iov_base = &pclc;
vec[0].iov_len = sizeof(pclc);
vec[1].iov_base = &pclc_prfx;
vec[1].iov_len = sizeof(pclc_prfx);
vec[2].iov_base = &trl;
vec[2].iov_len = sizeof(trl);
i = 0;
vec[i].iov_base = &pclc;
vec[i++].iov_len = sizeof(pclc);
vec[i].iov_base = &pclc_prfx;
vec[i++].iov_len = sizeof(pclc_prfx);
if (pclc_prfx.ipv6_prefixes_cnt > 0) {
vec[i].iov_base = &ipv6_prfx[0];
vec[i++].iov_len = pclc_prfx.ipv6_prefixes_cnt *
sizeof(ipv6_prfx[0]);
}
vec[i].iov_base = &trl;
vec[i++].iov_len = sizeof(trl);
/* due to the few bytes needed for clc-handshake this cannot block */
len = kernel_sendmsg(smc->clcsock, &msg, vec, 3, plen);
len = kernel_sendmsg(smc->clcsock, &msg, vec, i, plen);
if (len < sizeof(pclc)) {
if (len >= 0) {
reason_code = -ENETUNREACH;

View File

@ -60,10 +60,15 @@ struct smc_clc_msg_local { /* header2 of clc messages */
u8 mac[6]; /* mac of ib_device port */
};
#define SMC_CLC_MAX_V6_PREFIX 8
/* Struct would be 4 byte aligned, but it is used in an array that is sent
* to peers and must conform to RFC7609, hence we need to use packed here.
*/
struct smc_clc_ipv6_prefix {
u8 prefix[4];
struct in6_addr prefix;
u8 prefix_len;
} __packed;
} __packed; /* format defined in RFC7609 */
struct smc_clc_msg_proposal_prefix { /* prefix part of clc proposal message*/
__be32 outgoing_subnet; /* subnet mask */
@ -79,9 +84,11 @@ struct smc_clc_msg_proposal { /* clc proposal message sent by Linux */
} __aligned(4);
#define SMC_CLC_PROPOSAL_MAX_OFFSET 0x28
#define SMC_CLC_PROPOSAL_MAX_PREFIX (8 * sizeof(struct smc_clc_ipv6_prefix))
#define SMC_CLC_PROPOSAL_MAX_PREFIX (SMC_CLC_MAX_V6_PREFIX * \
sizeof(struct smc_clc_ipv6_prefix))
#define SMC_CLC_MAX_LEN (sizeof(struct smc_clc_msg_proposal) + \
SMC_CLC_PROPOSAL_MAX_OFFSET + \
sizeof(struct smc_clc_msg_proposal_prefix) + \
SMC_CLC_PROPOSAL_MAX_PREFIX + \
sizeof(struct smc_clc_msg_trail))