All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 net-next 0/3] rds: IPv6 support
@ 2018-06-27 10:23 Ka-Cheong Poon
  2018-06-27 10:23 ` [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr Ka-Cheong Poon
                   ` (3 more replies)
  0 siblings, 4 replies; 18+ messages in thread
From: Ka-Cheong Poon @ 2018-06-27 10:23 UTC (permalink / raw)
  To: netdev; +Cc: santosh.shilimkar, davem, rds-devel

This patch set adds IPv6 support to the kernel RDS and related
modules.  Existing RDS apps using IPv4 address continue to run without
any problem.  New RDS apps which want to use IPv6 address can do so by
passing the address in struct sockaddr_in6 to bind(), connect() or
sendmsg().  And those apps also need to use the new IPv6 equivalents
of some of the existing socket options as the existing options use a
32 bit integer to store IP address.

All RDS code now use struct in6_addr to store IP address.  IPv4
address is stored as an IPv4 mapped address.

Header file changes

There are many data structures (RDS socket options) used by RDS apps
which use a 32 bit integer to store IP address. To support IPv6,
struct in6_addr needs to be used. To ensure backward compatibility, a
new data structure is introduced for each of those data structures
which use a 32 bit integer to represent an IP address. And new socket
options are introduced to use those new structures. This means that
existing apps should work without a problem with the new RDS module.
For apps which want to use IPv6, those new data structures and socket
options can be used. IPv4 mapped address is used to represent IPv4
address in the new data structures.

Internally, all RDS data structures which contain an IP address are
changed to use struct in6_addr to store the address. IPv4 address is
stored as an IPv4 mapped address. All the functions which take an IP
address as argument are also changed to use struct in6_addr.

RDS/RDMA/IB uses a private data (struct rds_ib_connect_private)
exchange between endpoints at RDS connection establishment time to
support RDMA. This private data exchange uses a 32 bit integer to
represent an IP address. This needs to be changed in order to support
IPv6. A new private data struct rds6_ib_connect_private is introduced
to handle this. To ensure backward compatibility, an IPv6 capable RDS
stack uses another RDMA listener port (RDS_CM_PORT) to accept IPv6
connection. And it continues to use the original RDS_PORT for IPv4 RDS
connections. When it needs to communicate with an IPv6 peer, it uses
the RDS_TCP_PORT to send the connection set up request.

RDS/TCP changes

TCP related code is changed to support IPv6.  Note that only an IPv6
TCP listener on port RDS_TCP_PORT is created as it can accept both
IPv4 and IPv6 connection requests.

IB/RDMA changes

The initial private data exchange between IB endpoints using RDMA is
changed to support IPv6 address instead, if the peer address is IPv6.
To ensure backward compatibility, annother RDMA listener port
(RDS_CM_PORT) is used to accept IPv6 connection. An IPv6 capable RDS
module continues to use the original RDS_PORT for IPv4 RDS
connections. When it needs to communicate with an IPv6 peer, it uses
the RDS_CM_PORT to send the connection set up request.

Ka-Cheong Poon (3):
  rds: Changing IP address internal representation to struct in6_addr
  rds: Enable RDS IPv6 support
  rds: Extend RDS API for IPv6 support

 include/uapi/linux/rds.h |  71 ++++++++++-
 net/rds/af_rds.c         | 164 ++++++++++++++++++++------
 net/rds/bind.c           | 119 ++++++++++++++-----
 net/rds/cong.c           |  23 ++--
 net/rds/connection.c     | 249 +++++++++++++++++++++++++++++----------
 net/rds/ib.c             | 114 ++++++++++++++++--
 net/rds/ib.h             |  45 +++++--
 net/rds/ib_cm.c          | 301 +++++++++++++++++++++++++++++++++++------------
 net/rds/ib_mr.h          |   2 +
 net/rds/ib_rdma.c        |  24 ++--
 net/rds/ib_recv.c        |  18 +--
 net/rds/ib_send.c        |  10 +-
 net/rds/loop.c           |   7 +-
 net/rds/rdma.c           |   6 +-
 net/rds/rdma_transport.c |  84 ++++++++++---
 net/rds/rdma_transport.h |   2 +
 net/rds/rds.h            |  85 ++++++++-----
 net/rds/recv.c           |  76 +++++++++---
 net/rds/send.c           |  95 ++++++++++++---
 net/rds/tcp.c            | 128 ++++++++++++++++----
 net/rds/tcp.h            |   4 +-
 net/rds/tcp_connect.c    |  68 ++++++++---
 net/rds/tcp_listen.c     |  58 ++++++---
 net/rds/tcp_recv.c       |   9 +-
 net/rds/tcp_send.c       |   4 +-
 net/rds/threads.c        |  69 +++++++++--
 net/rds/transport.c      |  15 ++-
 27 files changed, 1426 insertions(+), 424 deletions(-)

-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr
  2018-06-27 10:23 [PATCH v2 net-next 0/3] rds: IPv6 support Ka-Cheong Poon
@ 2018-06-27 10:23 ` Ka-Cheong Poon
  2018-06-30  8:50   ` David Miller
  2018-07-05 17:58   ` Santosh Shilimkar
  2018-06-27 10:23 ` [PATCH v2 net-next 2/3] rds: Enable RDS IPv6 support Ka-Cheong Poon
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 18+ messages in thread
From: Ka-Cheong Poon @ 2018-06-27 10:23 UTC (permalink / raw)
  To: netdev; +Cc: santosh.shilimkar, davem, rds-devel

This patch changes the internal representation of an IP address to use
struct in6_addr.  IPv4 address is stored as an IPv4 mapped address.
All the functions which take an IP address as argument are also
changed to use struct in6_addr.  But RDS socket layer is not modified
such that it still does not accept IPv6 address from an application.
And RDS layer does not accept nor initiate IPv6 connections.

v2: Fixed sparse warnings.

Signed-off-by: Ka-Cheong Poon <ka-cheong.poon@oracle.com>
---
 net/rds/af_rds.c         | 138 ++++++++++++++++------
 net/rds/bind.c           |  91 +++++++++-----
 net/rds/cong.c           |  23 ++--
 net/rds/connection.c     | 132 +++++++++++++--------
 net/rds/ib.c             |  17 +--
 net/rds/ib.h             |  45 +++++--
 net/rds/ib_cm.c          | 300 +++++++++++++++++++++++++++++++++++------------
 net/rds/ib_rdma.c        |  15 +--
 net/rds/ib_recv.c        |  18 +--
 net/rds/ib_send.c        |  10 +-
 net/rds/loop.c           |   7 +-
 net/rds/rdma.c           |   6 +-
 net/rds/rdma_transport.c |  56 ++++++---
 net/rds/rds.h            |  69 +++++++----
 net/rds/recv.c           |  51 +++++---
 net/rds/send.c           |  67 ++++++++---
 net/rds/tcp.c            |  32 ++++-
 net/rds/tcp_connect.c    |  34 +++---
 net/rds/tcp_listen.c     |  18 +--
 net/rds/tcp_recv.c       |   9 +-
 net/rds/tcp_send.c       |   4 +-
 net/rds/threads.c        |  69 +++++++++--
 net/rds/transport.c      |  15 ++-
 23 files changed, 857 insertions(+), 369 deletions(-)

diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c
index ab751a1..fc1a5c6 100644
--- a/net/rds/af_rds.c
+++ b/net/rds/af_rds.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -35,6 +35,7 @@
 #include <linux/kernel.h>
 #include <linux/gfp.h>
 #include <linux/in.h>
+#include <linux/ipv6.h>
 #include <linux/poll.h>
 #include <net/sock.h>
 
@@ -113,26 +114,63 @@ void rds_wake_sk_sleep(struct rds_sock *rs)
 static int rds_getname(struct socket *sock, struct sockaddr *uaddr,
 		       int peer)
 {
-	struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
 	struct rds_sock *rs = rds_sk_to_rs(sock->sk);
-
-	memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+	struct sockaddr_in6 *sin6;
+	struct sockaddr_in *sin;
+	int uaddr_len;
 
 	/* racey, don't care */
 	if (peer) {
-		if (!rs->rs_conn_addr)
+		if (ipv6_addr_any(&rs->rs_conn_addr))
 			return -ENOTCONN;
 
-		sin->sin_port = rs->rs_conn_port;
-		sin->sin_addr.s_addr = rs->rs_conn_addr;
+		if (ipv6_addr_v4mapped(&rs->rs_conn_addr)) {
+			sin = (struct sockaddr_in *)uaddr;
+			memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+			sin->sin_family = AF_INET;
+			sin->sin_port = rs->rs_conn_port;
+			sin->sin_addr.s_addr = rs->rs_conn_addr_v4;
+			uaddr_len = sizeof(*sin);
+		} else {
+			sin6 = (struct sockaddr_in6 *)uaddr;
+			sin6->sin6_family = AF_INET6;
+			sin6->sin6_port = rs->rs_conn_port;
+			sin6->sin6_addr = rs->rs_conn_addr;
+			sin6->sin6_flowinfo = 0;
+			/* scope_id is the same as in the bound address. */
+			sin6->sin6_scope_id = rs->rs_bound_scope_id;
+			uaddr_len = sizeof(*sin6);
+		}
 	} else {
-		sin->sin_port = rs->rs_bound_port;
-		sin->sin_addr.s_addr = rs->rs_bound_addr;
+		/* If socket is not yet bound, set the return address family
+		 * to be AF_UNSPEC (value 0) and the address size to be that
+		 * of an IPv4 address.
+		 */
+		if (ipv6_addr_any(&rs->rs_bound_addr)) {
+			sin = (struct sockaddr_in *)uaddr;
+			memset(sin, 0, sizeof(*sin));
+			sin->sin_family = AF_UNSPEC;
+			return sizeof(*sin);
+		}
+		if (ipv6_addr_v4mapped(&rs->rs_bound_addr)) {
+			sin = (struct sockaddr_in *)uaddr;
+			memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+			sin->sin_family = AF_INET;
+			sin->sin_port = rs->rs_bound_port;
+			sin->sin_addr.s_addr = rs->rs_bound_addr_v4;
+			uaddr_len = sizeof(*sin);
+		} else {
+			sin6 = (struct sockaddr_in6 *)uaddr;
+			sin6->sin6_family = AF_INET6;
+			sin6->sin6_port = rs->rs_bound_port;
+			sin6->sin6_addr = rs->rs_bound_addr;
+			sin6->sin6_flowinfo = 0;
+			sin6->sin6_scope_id = rs->rs_bound_scope_id;
+			uaddr_len = sizeof(*sin6);
+		}
 	}
 
-	sin->sin_family = AF_INET;
-
-	return sizeof(*sin);
+	return uaddr_len;
 }
 
 /*
@@ -203,11 +241,12 @@ static int rds_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 static int rds_cancel_sent_to(struct rds_sock *rs, char __user *optval,
 			      int len)
 {
+	struct sockaddr_in6 sin6;
 	struct sockaddr_in sin;
 	int ret = 0;
 
 	/* racing with another thread binding seems ok here */
-	if (rs->rs_bound_addr == 0) {
+	if (ipv6_addr_any(&rs->rs_bound_addr)) {
 		ret = -ENOTCONN; /* XXX not a great errno */
 		goto out;
 	}
@@ -215,14 +254,23 @@ static int rds_cancel_sent_to(struct rds_sock *rs, char __user *optval,
 	if (len < sizeof(struct sockaddr_in)) {
 		ret = -EINVAL;
 		goto out;
+	} else if (len < sizeof(struct sockaddr_in6)) {
+		/* Assume IPv4 */
+		if (copy_from_user(&sin, optval, sizeof(struct sockaddr_in))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		ipv6_addr_set_v4mapped(sin.sin_addr.s_addr, &sin6.sin6_addr);
+		sin6.sin6_port = sin.sin_port;
+	} else {
+		if (copy_from_user(&sin6, optval,
+				   sizeof(struct sockaddr_in6))) {
+			ret = -EFAULT;
+			goto out;
+		}
 	}
 
-	if (copy_from_user(&sin, optval, sizeof(sin))) {
-		ret = -EFAULT;
-		goto out;
-	}
-
-	rds_send_drop_to(rs, &sin);
+	rds_send_drop_to(rs, &sin6);
 out:
 	return ret;
 }
@@ -435,31 +483,41 @@ static int rds_connect(struct socket *sock, struct sockaddr *uaddr,
 		       int addr_len, int flags)
 {
 	struct sock *sk = sock->sk;
-	struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
+	struct sockaddr_in *sin;
 	struct rds_sock *rs = rds_sk_to_rs(sk);
 	int ret = 0;
 
 	lock_sock(sk);
 
-	if (addr_len != sizeof(struct sockaddr_in)) {
-		ret = -EINVAL;
-		goto out;
-	}
+	switch (addr_len) {
+	case sizeof(struct sockaddr_in):
+		sin = (struct sockaddr_in *)uaddr;
+		if (sin->sin_family != AF_INET) {
+			ret = -EAFNOSUPPORT;
+			break;
+		}
+		if (sin->sin_addr.s_addr == htonl(INADDR_ANY)) {
+			ret = -EDESTADDRREQ;
+			break;
+		}
+		if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) ||
+		    sin->sin_addr.s_addr == htonl(INADDR_BROADCAST)) {
+			ret = -EINVAL;
+			break;
+		}
+		ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &rs->rs_conn_addr);
+		rs->rs_conn_port = sin->sin_port;
+		break;
 
-	if (sin->sin_family != AF_INET) {
-		ret = -EAFNOSUPPORT;
-		goto out;
-	}
+	case sizeof(struct sockaddr_in6):
+		ret = -EPROTONOSUPPORT;
+		break;
 
-	if (sin->sin_addr.s_addr == htonl(INADDR_ANY)) {
-		ret = -EDESTADDRREQ;
-		goto out;
+	default:
+		ret = -EINVAL;
+		break;
 	}
 
-	rs->rs_conn_addr = sin->sin_addr.s_addr;
-	rs->rs_conn_port = sin->sin_port;
-
-out:
 	release_sock(sk);
 	return ret;
 }
@@ -578,8 +636,10 @@ static void rds_sock_inc_info(struct socket *sock, unsigned int len,
 		list_for_each_entry(inc, &rs->rs_recv_queue, i_item) {
 			total++;
 			if (total <= len)
-				rds_inc_info_copy(inc, iter, inc->i_saddr,
-						  rs->rs_bound_addr, 1);
+				rds_inc_info_copy(inc, iter,
+						  inc->i_saddr.s6_addr32[3],
+						  rs->rs_bound_addr_v4,
+						  1);
 		}
 
 		read_unlock(&rs->rs_recv_lock);
@@ -608,8 +668,8 @@ static void rds_sock_info(struct socket *sock, unsigned int len,
 	list_for_each_entry(rs, &rds_sock_list, rs_item) {
 		sinfo.sndbuf = rds_sk_sndbuf(rs);
 		sinfo.rcvbuf = rds_sk_rcvbuf(rs);
-		sinfo.bound_addr = rs->rs_bound_addr;
-		sinfo.connected_addr = rs->rs_conn_addr;
+		sinfo.bound_addr = rs->rs_bound_addr_v4;
+		sinfo.connected_addr = rs->rs_conn_addr_v4;
 		sinfo.bound_port = rs->rs_bound_port;
 		sinfo.connected_port = rs->rs_conn_port;
 		sinfo.inum = sock_i_ino(rds_rs_to_sk(rs));
diff --git a/net/rds/bind.c b/net/rds/bind.c
index 5aa3a64..3822886 100644
--- a/net/rds/bind.c
+++ b/net/rds/bind.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -33,6 +33,7 @@
 #include <linux/kernel.h>
 #include <net/sock.h>
 #include <linux/in.h>
+#include <linux/ipv6.h>
 #include <linux/if_arp.h>
 #include <linux/jhash.h>
 #include <linux/ratelimit.h>
@@ -42,42 +43,58 @@
 
 static const struct rhashtable_params ht_parms = {
 	.nelem_hint = 768,
-	.key_len = sizeof(u64),
+	.key_len = RDS_BOUND_KEY_LEN,
 	.key_offset = offsetof(struct rds_sock, rs_bound_key),
 	.head_offset = offsetof(struct rds_sock, rs_bound_node),
 	.max_size = 16384,
 	.min_size = 1024,
 };
 
+/* Create a key for the bind hash table manipulation.  Port is in network byte
+ * order.
+ */
+static inline void __rds_create_bind_key(u8 *key, const struct in6_addr *addr,
+					 __be16 port, __u32 scope_id)
+{
+	memcpy(key, addr, sizeof(*addr));
+	key += sizeof(*addr);
+	memcpy(key, &port, sizeof(port));
+	key += sizeof(port);
+	memcpy(key, &scope_id, sizeof(scope_id));
+}
+
 /*
  * Return the rds_sock bound at the given local address.
  *
  * The rx path can race with rds_release.  We notice if rds_release() has
  * marked this socket and don't return a rs ref to the rx path.
  */
-struct rds_sock *rds_find_bound(__be32 addr, __be16 port)
+struct rds_sock *rds_find_bound(const struct in6_addr *addr, __be16 port,
+				__u32 scope_id)
 {
-	u64 key = ((u64)addr << 32) | port;
+	u8 key[RDS_BOUND_KEY_LEN];
 	struct rds_sock *rs;
 
-	rs = rhashtable_lookup_fast(&bind_hash_table, &key, ht_parms);
+	__rds_create_bind_key(key, addr, port, scope_id);
+	rs = rhashtable_lookup_fast(&bind_hash_table, key, ht_parms);
 	if (rs && !sock_flag(rds_rs_to_sk(rs), SOCK_DEAD))
 		rds_sock_addref(rs);
 	else
 		rs = NULL;
 
-	rdsdebug("returning rs %p for %pI4:%u\n", rs, &addr,
-		ntohs(port));
+	rdsdebug("returning rs %p for %pI6c:%u\n", rs, addr,
+		 ntohs(port));
 
 	return rs;
 }
 
 /* returns -ve errno or +ve port */
-static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port)
+static int rds_add_bound(struct rds_sock *rs, const struct in6_addr *addr,
+			 __be16 *port, __u32 scope_id)
 {
 	int ret = -EADDRINUSE;
 	u16 rover, last;
-	u64 key;
+	u8 key[RDS_BOUND_KEY_LEN];
 
 	if (*port != 0) {
 		rover = be16_to_cpu(*port);
@@ -95,12 +112,13 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port)
 
 		if (rover == RDS_FLAG_PROBE_PORT)
 			continue;
-		key = ((u64)addr << 32) | cpu_to_be16(rover);
-		if (rhashtable_lookup_fast(&bind_hash_table, &key, ht_parms))
+		__rds_create_bind_key(key, addr, cpu_to_be16(rover),
+				      scope_id);
+		if (rhashtable_lookup_fast(&bind_hash_table, key, ht_parms))
 			continue;
 
-		rs->rs_bound_key = key;
-		rs->rs_bound_addr = addr;
+		memcpy(rs->rs_bound_key, key, sizeof(rs->rs_bound_key));
+		rs->rs_bound_addr = *addr;
 		net_get_random_once(&rs->rs_hash_initval,
 				    sizeof(rs->rs_hash_initval));
 		rs->rs_bound_port = cpu_to_be16(rover);
@@ -114,7 +132,7 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port)
 			  rs, &addr, (int)ntohs(*port));
 			break;
 		} else {
-			rs->rs_bound_addr = 0;
+			rs->rs_bound_addr = in6addr_any;
 			rds_sock_put(rs);
 			ret = -ENOMEM;
 			break;
@@ -127,44 +145,61 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port)
 void rds_remove_bound(struct rds_sock *rs)
 {
 
-	if (!rs->rs_bound_addr)
+	if (ipv6_addr_any(&rs->rs_bound_addr))
 		return;
 
-	rdsdebug("rs %p unbinding from %pI4:%d\n",
+	rdsdebug("rs %p unbinding from %pI6c:%d\n",
 		 rs, &rs->rs_bound_addr,
 		 ntohs(rs->rs_bound_port));
 
 	rhashtable_remove_fast(&bind_hash_table, &rs->rs_bound_node, ht_parms);
 	rds_sock_put(rs);
-	rs->rs_bound_addr = 0;
+	rs->rs_bound_addr = in6addr_any;
 }
 
 int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
 	struct sock *sk = sock->sk;
-	struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
 	struct rds_sock *rs = rds_sk_to_rs(sk);
+	struct in6_addr v6addr, *binding_addr;
 	struct rds_transport *trans;
+	__u32 scope_id = 0;
 	int ret = 0;
+	__be16 port;
 
+	/* We only allow an RDS socket to be bound to and IPv4 address. IPv6
+	 * address support will be added later.
+	 */
+	if (addr_len == sizeof(struct sockaddr_in)) {
+		struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
+
+		if (sin->sin_family != AF_INET ||
+		    sin->sin_addr.s_addr == htonl(INADDR_ANY))
+			return -EINVAL;
+		ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &v6addr);
+		binding_addr = &v6addr;
+		port = sin->sin_port;
+	} else if (addr_len == sizeof(struct sockaddr_in6)) {
+		return -EPROTONOSUPPORT;
+	} else {
+		return -EINVAL;
+	}
 	lock_sock(sk);
 
-	if (addr_len != sizeof(struct sockaddr_in) ||
-	    sin->sin_family != AF_INET ||
-	    rs->rs_bound_addr ||
-	    sin->sin_addr.s_addr == htonl(INADDR_ANY)) {
+	/* RDS socket does not allow re-binding. */
+	if (!ipv6_addr_any(&rs->rs_bound_addr)) {
 		ret = -EINVAL;
 		goto out;
 	}
 
-	ret = rds_add_bound(rs, sin->sin_addr.s_addr, &sin->sin_port);
+	ret = rds_add_bound(rs, binding_addr, &port, scope_id);
 	if (ret)
 		goto out;
 
 	if (rs->rs_transport) { /* previously bound */
 		trans = rs->rs_transport;
 		if (trans->laddr_check(sock_net(sock->sk),
-				       sin->sin_addr.s_addr) != 0) {
+				       binding_addr, scope_id) != 0) {
 			ret = -ENOPROTOOPT;
 			rds_remove_bound(rs);
 		} else {
@@ -172,13 +207,13 @@ int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 		}
 		goto out;
 	}
-	trans = rds_trans_get_preferred(sock_net(sock->sk),
-					sin->sin_addr.s_addr);
+	trans = rds_trans_get_preferred(sock_net(sock->sk), binding_addr,
+					scope_id);
 	if (!trans) {
 		ret = -EADDRNOTAVAIL;
 		rds_remove_bound(rs);
-		pr_info_ratelimited("RDS: %s could not find a transport for %pI4, load rds_tcp or rds_rdma?\n",
-				    __func__, &sin->sin_addr.s_addr);
+		pr_info_ratelimited("RDS: %s could not find a transport for %pI6c, load rds_tcp or rds_rdma?\n",
+				    __func__, binding_addr);
 		goto out;
 	}
 
diff --git a/net/rds/cong.c b/net/rds/cong.c
index 63da9d2..ccdff09 100644
--- a/net/rds/cong.c
+++ b/net/rds/cong.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007 Oracle.  All rights reserved.
+ * Copyright (c) 2007, 2017 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -101,7 +101,7 @@
 static DEFINE_SPINLOCK(rds_cong_lock);
 static struct rb_root rds_cong_tree = RB_ROOT;
 
-static struct rds_cong_map *rds_cong_tree_walk(__be32 addr,
+static struct rds_cong_map *rds_cong_tree_walk(const struct in6_addr *addr,
 					       struct rds_cong_map *insert)
 {
 	struct rb_node **p = &rds_cong_tree.rb_node;
@@ -109,12 +109,15 @@ static struct rds_cong_map *rds_cong_tree_walk(__be32 addr,
 	struct rds_cong_map *map;
 
 	while (*p) {
+		int diff;
+
 		parent = *p;
 		map = rb_entry(parent, struct rds_cong_map, m_rb_node);
 
-		if (addr < map->m_addr)
+		diff = rds_addr_cmp(addr, &map->m_addr);
+		if (diff < 0)
 			p = &(*p)->rb_left;
-		else if (addr > map->m_addr)
+		else if (diff > 0)
 			p = &(*p)->rb_right;
 		else
 			return map;
@@ -132,7 +135,7 @@ static struct rds_cong_map *rds_cong_tree_walk(__be32 addr,
  * these bitmaps in the process getting pointers to them.  The bitmaps are only
  * ever freed as the module is removed after all connections have been freed.
  */
-static struct rds_cong_map *rds_cong_from_addr(__be32 addr)
+static struct rds_cong_map *rds_cong_from_addr(const struct in6_addr *addr)
 {
 	struct rds_cong_map *map;
 	struct rds_cong_map *ret = NULL;
@@ -144,7 +147,7 @@ static struct rds_cong_map *rds_cong_from_addr(__be32 addr)
 	if (!map)
 		return NULL;
 
-	map->m_addr = addr;
+	map->m_addr = *addr;
 	init_waitqueue_head(&map->m_waitq);
 	INIT_LIST_HEAD(&map->m_conn_list);
 
@@ -171,7 +174,7 @@ static struct rds_cong_map *rds_cong_from_addr(__be32 addr)
 		kfree(map);
 	}
 
-	rdsdebug("map %p for addr %x\n", ret, be32_to_cpu(addr));
+	rdsdebug("map %p for addr %pI6c\n", ret, addr);
 
 	return ret;
 }
@@ -202,8 +205,8 @@ void rds_cong_remove_conn(struct rds_connection *conn)
 
 int rds_cong_get_maps(struct rds_connection *conn)
 {
-	conn->c_lcong = rds_cong_from_addr(conn->c_laddr);
-	conn->c_fcong = rds_cong_from_addr(conn->c_faddr);
+	conn->c_lcong = rds_cong_from_addr(&conn->c_laddr);
+	conn->c_fcong = rds_cong_from_addr(&conn->c_faddr);
 
 	if (!(conn->c_lcong && conn->c_fcong))
 		return -ENOMEM;
@@ -353,7 +356,7 @@ void rds_cong_remove_socket(struct rds_sock *rs)
 
 	/* update congestion map for now-closed port */
 	spin_lock_irqsave(&rds_cong_lock, flags);
-	map = rds_cong_tree_walk(rs->rs_bound_addr, NULL);
+	map = rds_cong_tree_walk(&rs->rs_bound_addr, NULL);
 	spin_unlock_irqrestore(&rds_cong_lock, flags);
 
 	if (map && rds_cong_test_bit(map, rs->rs_bound_port)) {
diff --git a/net/rds/connection.c b/net/rds/connection.c
index abef75d..ca72563 100644
--- a/net/rds/connection.c
+++ b/net/rds/connection.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -34,7 +34,8 @@
 #include <linux/list.h>
 #include <linux/slab.h>
 #include <linux/export.h>
-#include <net/inet_hashtables.h>
+#include <net/ipv6.h>
+#include <net/inet6_hashtables.h>
 
 #include "rds.h"
 #include "loop.h"
@@ -49,18 +50,21 @@
 static struct hlist_head rds_conn_hash[RDS_CONNECTION_HASH_ENTRIES];
 static struct kmem_cache *rds_conn_slab;
 
-static struct hlist_head *rds_conn_bucket(__be32 laddr, __be32 faddr)
+static struct hlist_head *rds_conn_bucket(const struct in6_addr *laddr,
+					  const struct in6_addr *faddr)
 {
+	static u32 rds6_hash_secret __read_mostly;
 	static u32 rds_hash_secret __read_mostly;
 
-	unsigned long hash;
+	u32 lhash, fhash, hash;
 
 	net_get_random_once(&rds_hash_secret, sizeof(rds_hash_secret));
+	net_get_random_once(&rds6_hash_secret, sizeof(rds6_hash_secret));
+
+	lhash = (__force u32)laddr->s6_addr32[3];
+	fhash = __ipv6_addr_jhash(faddr, rds6_hash_secret);
+	hash = __inet6_ehashfn(lhash, 0, fhash, 0, rds_hash_secret);
 
-	/* Pass NULL, don't need struct net for hash */
-	hash = __inet_ehashfn(be32_to_cpu(laddr), 0,
-			      be32_to_cpu(faddr), 0,
-			      rds_hash_secret);
 	return &rds_conn_hash[hash & RDS_CONNECTION_HASH_MASK];
 }
 
@@ -72,20 +76,25 @@ static struct hlist_head *rds_conn_bucket(__be32 laddr, __be32 faddr)
 /* rcu read lock must be held or the connection spinlock */
 static struct rds_connection *rds_conn_lookup(struct net *net,
 					      struct hlist_head *head,
-					      __be32 laddr, __be32 faddr,
-					      struct rds_transport *trans)
+					      const struct in6_addr *laddr,
+					      const struct in6_addr *faddr,
+					      struct rds_transport *trans,
+					      int dev_if)
 {
 	struct rds_connection *conn, *ret = NULL;
 
 	hlist_for_each_entry_rcu(conn, head, c_hash_node) {
-		if (conn->c_faddr == faddr && conn->c_laddr == laddr &&
-		    conn->c_trans == trans && net == rds_conn_net(conn)) {
+		if (ipv6_addr_equal(&conn->c_faddr, faddr) &&
+		    ipv6_addr_equal(&conn->c_laddr, laddr) &&
+		    conn->c_trans == trans &&
+		    net == rds_conn_net(conn) &&
+		    conn->c_dev_if == dev_if) {
 			ret = conn;
 			break;
 		}
 	}
-	rdsdebug("returning conn %p for %pI4 -> %pI4\n", ret,
-		 &laddr, &faddr);
+	rdsdebug("returning conn %p for %pI6c -> %pI6c\n", ret,
+		 laddr, faddr);
 	return ret;
 }
 
@@ -99,8 +108,8 @@ static void rds_conn_path_reset(struct rds_conn_path *cp)
 {
 	struct rds_connection *conn = cp->cp_conn;
 
-	rdsdebug("connection %pI4 to %pI4 reset\n",
-	  &conn->c_laddr, &conn->c_faddr);
+	rdsdebug("connection %pI6c to %pI6c reset\n",
+		 &conn->c_laddr, &conn->c_faddr);
 
 	rds_stats_inc(s_conn_reset);
 	rds_send_path_reset(cp);
@@ -142,9 +151,12 @@ static void __rds_conn_path_init(struct rds_connection *conn,
  * are torn down as the module is removed, if ever.
  */
 static struct rds_connection *__rds_conn_create(struct net *net,
-						__be32 laddr, __be32 faddr,
-				       struct rds_transport *trans, gfp_t gfp,
-				       int is_outgoing)
+						const struct in6_addr *laddr,
+						const struct in6_addr *faddr,
+						struct rds_transport *trans,
+						gfp_t gfp,
+						int is_outgoing,
+						int dev_if)
 {
 	struct rds_connection *conn, *parent = NULL;
 	struct hlist_head *head = rds_conn_bucket(laddr, faddr);
@@ -154,9 +166,12 @@ static struct rds_connection *__rds_conn_create(struct net *net,
 	int npaths = (trans->t_mp_capable ? RDS_MPATH_WORKERS : 1);
 
 	rcu_read_lock();
-	conn = rds_conn_lookup(net, head, laddr, faddr, trans);
-	if (conn && conn->c_loopback && conn->c_trans != &rds_loop_transport &&
-	    laddr == faddr && !is_outgoing) {
+	conn = rds_conn_lookup(net, head, laddr, faddr, trans, dev_if);
+	if (conn &&
+	    conn->c_loopback &&
+	    conn->c_trans != &rds_loop_transport &&
+	    ipv6_addr_equal(laddr, faddr) &&
+	    !is_outgoing) {
 		/* This is a looped back IB connection, and we're
 		 * called by the code handling the incoming connect.
 		 * We need a second connection object into which we
@@ -181,8 +196,10 @@ static struct rds_connection *__rds_conn_create(struct net *net,
 	}
 
 	INIT_HLIST_NODE(&conn->c_hash_node);
-	conn->c_laddr = laddr;
-	conn->c_faddr = faddr;
+	conn->c_laddr = *laddr;
+	conn->c_isv6 = !ipv6_addr_v4mapped(laddr);
+	conn->c_faddr = *faddr;
+	conn->c_dev_if = dev_if;
 
 	rds_conn_net_set(conn, net);
 
@@ -199,7 +216,7 @@ static struct rds_connection *__rds_conn_create(struct net *net,
 	 * can bind to the destination address then we'd rather the messages
 	 * flow through loopback rather than either transport.
 	 */
-	loop_trans = rds_trans_get_preferred(net, faddr);
+	loop_trans = rds_trans_get_preferred(net, faddr, conn->c_dev_if);
 	if (loop_trans) {
 		rds_trans_put(loop_trans);
 		conn->c_loopback = 1;
@@ -233,10 +250,10 @@ static struct rds_connection *__rds_conn_create(struct net *net,
 		goto out;
 	}
 
-	rdsdebug("allocated conn %p for %pI4 -> %pI4 over %s %s\n",
-	  conn, &laddr, &faddr,
-	  strnlen(trans->t_name, sizeof(trans->t_name)) ? trans->t_name :
-	  "[unknown]", is_outgoing ? "(outgoing)" : "");
+	rdsdebug("allocated conn %p for %pI6c -> %pI6c over %s %s\n",
+		 conn, laddr, faddr,
+		 strnlen(trans->t_name, sizeof(trans->t_name)) ?
+		 trans->t_name : "[unknown]", is_outgoing ? "(outgoing)" : "");
 
 	/*
 	 * Since we ran without holding the conn lock, someone could
@@ -262,7 +279,8 @@ static struct rds_connection *__rds_conn_create(struct net *net,
 		/* Creating normal conn */
 		struct rds_connection *found;
 
-		found = rds_conn_lookup(net, head, laddr, faddr, trans);
+		found = rds_conn_lookup(net, head, laddr, faddr, trans,
+					dev_if);
 		if (found) {
 			struct rds_conn_path *cp;
 			int i;
@@ -295,18 +313,22 @@ static struct rds_connection *__rds_conn_create(struct net *net,
 }
 
 struct rds_connection *rds_conn_create(struct net *net,
-				       __be32 laddr, __be32 faddr,
-				       struct rds_transport *trans, gfp_t gfp)
+				       const struct in6_addr *laddr,
+				       const struct in6_addr *faddr,
+				       struct rds_transport *trans, gfp_t gfp,
+				       int dev_if)
 {
-	return __rds_conn_create(net, laddr, faddr, trans, gfp, 0);
+	return __rds_conn_create(net, laddr, faddr, trans, gfp, 0, dev_if);
 }
 EXPORT_SYMBOL_GPL(rds_conn_create);
 
 struct rds_connection *rds_conn_create_outgoing(struct net *net,
-						__be32 laddr, __be32 faddr,
-				       struct rds_transport *trans, gfp_t gfp)
+						const struct in6_addr *laddr,
+						const struct in6_addr *faddr,
+						struct rds_transport *trans,
+						gfp_t gfp, int dev_if)
 {
-	return __rds_conn_create(net, laddr, faddr, trans, gfp, 1);
+	return __rds_conn_create(net, laddr, faddr, trans, gfp, 1, dev_if);
 }
 EXPORT_SYMBOL_GPL(rds_conn_create_outgoing);
 
@@ -502,12 +524,17 @@ static void rds_conn_message_info(struct socket *sock, unsigned int len,
 
 				/* XXX too lazy to maintain counts.. */
 				list_for_each_entry(rm, list, m_conn_item) {
+					__be32 laddr;
+					__be32 faddr;
+
 					total++;
+					laddr = conn->c_laddr.s6_addr32[3];
+					faddr = conn->c_faddr.s6_addr32[3];
 					if (total <= len)
 						rds_inc_info_copy(&rm->m_inc,
 								  iter,
-								  conn->c_laddr,
-								  conn->c_faddr,
+								  laddr,
+								  faddr,
 								  0);
 				}
 
@@ -584,7 +611,6 @@ static void rds_walk_conn_path_info(struct socket *sock, unsigned int len,
 	struct hlist_head *head;
 	struct rds_connection *conn;
 	size_t i;
-	int j;
 
 	rcu_read_lock();
 
@@ -595,17 +621,20 @@ static void rds_walk_conn_path_info(struct socket *sock, unsigned int len,
 	     i++, head++) {
 		hlist_for_each_entry_rcu(conn, head, c_hash_node) {
 			struct rds_conn_path *cp;
-			int npaths;
 
-			npaths = (conn->c_trans->t_mp_capable ?
-				 RDS_MPATH_WORKERS : 1);
-			for (j = 0; j < npaths; j++) {
-				cp = &conn->c_path[j];
+			/* XXX We only copy the information from the first
+			 * path for now.  The problem is that if there are
+			 * more than one underlying paths, we cannot report
+			 * information of all of them using the exisitng
+			 * API.  For example, there is only one next_tx_seq,
+			 * which path's next_tx_seq should we report?  It is
+			 * a bug in the design of MPRDS.
+			 */
+			cp = conn->c_path;
 
-				/* XXX no cp_lock usage.. */
-				if (!visitor(cp, buffer))
-					continue;
-			}
+			/* XXX no cp_lock usage.. */
+			if (!visitor(cp, buffer))
+				continue;
 
 			/* We copy as much as we can fit in the buffer,
 			 * but we count all items so that the caller
@@ -624,12 +653,13 @@ static void rds_walk_conn_path_info(struct socket *sock, unsigned int len,
 static int rds_conn_info_visitor(struct rds_conn_path *cp, void *buffer)
 {
 	struct rds_info_connection *cinfo = buffer;
+	struct rds_connection *conn = cp->cp_conn;
 
 	cinfo->next_tx_seq = cp->cp_next_tx_seq;
 	cinfo->next_rx_seq = cp->cp_next_rx_seq;
-	cinfo->laddr = cp->cp_conn->c_laddr;
-	cinfo->faddr = cp->cp_conn->c_faddr;
-	strncpy(cinfo->transport, cp->cp_conn->c_trans->t_name,
+	cinfo->laddr = conn->c_laddr.s6_addr32[3];
+	cinfo->faddr = conn->c_faddr.s6_addr32[3];
+	strncpy(cinfo->transport, conn->c_trans->t_name,
 		sizeof(cinfo->transport));
 	cinfo->flags = 0;
 
diff --git a/net/rds/ib.c b/net/rds/ib.c
index b6ad38e..c712a84 100644
--- a/net/rds/ib.c
+++ b/net/rds/ib.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -296,8 +296,8 @@ static int rds_ib_conn_info_visitor(struct rds_connection *conn,
 	if (conn->c_trans != &rds_ib_transport)
 		return 0;
 
-	iinfo->src_addr = conn->c_laddr;
-	iinfo->dst_addr = conn->c_faddr;
+	iinfo->src_addr = conn->c_laddr.s6_addr32[3];
+	iinfo->dst_addr = conn->c_faddr.s6_addr32[3];
 
 	memset(&iinfo->src_gid, 0, sizeof(iinfo->src_gid));
 	memset(&iinfo->dst_gid, 0, sizeof(iinfo->dst_gid));
@@ -341,7 +341,8 @@ static void rds_ib_ic_info(struct socket *sock, unsigned int len,
  * allowed to influence which paths have priority.  We could call userspace
  * asserting this policy "routing".
  */
-static int rds_ib_laddr_check(struct net *net, __be32 addr)
+static int rds_ib_laddr_check(struct net *net, const struct in6_addr *addr,
+			      __u32 scope_id)
 {
 	int ret;
 	struct rdma_cm_id *cm_id;
@@ -357,7 +358,7 @@ static int rds_ib_laddr_check(struct net *net, __be32 addr)
 
 	memset(&sin, 0, sizeof(sin));
 	sin.sin_family = AF_INET;
-	sin.sin_addr.s_addr = addr;
+	sin.sin_addr.s_addr = addr->s6_addr32[3];
 
 	/* rdma_bind_addr will only succeed for IB & iWARP devices */
 	ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin);
@@ -367,9 +368,9 @@ static int rds_ib_laddr_check(struct net *net, __be32 addr)
 	    cm_id->device->node_type != RDMA_NODE_IB_CA)
 		ret = -EADDRNOTAVAIL;
 
-	rdsdebug("addr %pI4 ret %d node type %d\n",
-		&addr, ret,
-		cm_id->device ? cm_id->device->node_type : -1);
+	rdsdebug("addr %pI6c ret %d node type %d\n",
+		 addr, ret,
+		 cm_id->device ? cm_id->device->node_type : -1);
 
 	rdma_destroy_id(cm_id);
 
diff --git a/net/rds/ib.h b/net/rds/ib.h
index a6f4d7d..12f96b3 100644
--- a/net/rds/ib.h
+++ b/net/rds/ib.h
@@ -57,16 +57,38 @@ struct rds_ib_refill_cache {
 	struct list_head	 *ready;
 };
 
+struct rds_ib_conn_priv_cmn {
+	u8			ricpc_protocol_major;
+	u8			ricpc_protocol_minor;
+	__be16			ricpc_protocol_minor_mask;	/* bitmask */
+	__be32			ricpc_reserved1;
+	__be64			ricpc_ack_seq;
+	__be32			ricpc_credit;	/* non-zero enables flow ctl */
+};
+
 struct rds_ib_connect_private {
 	/* Add new fields at the end, and don't permute existing fields. */
-	__be32			dp_saddr;
-	__be32			dp_daddr;
-	u8			dp_protocol_major;
-	u8			dp_protocol_minor;
-	__be16			dp_protocol_minor_mask; /* bitmask */
-	__be32			dp_reserved1;
-	__be64			dp_ack_seq;
-	__be32			dp_credit;		/* non-zero enables flow ctl */
+	__be32				dp_saddr;
+	__be32				dp_daddr;
+	struct rds_ib_conn_priv_cmn	dp_cmn;
+};
+
+struct rds6_ib_connect_private {
+	/* Add new fields at the end, and don't permute existing fields. */
+	struct in6_addr			dp_saddr;
+	struct in6_addr			dp_daddr;
+	struct rds_ib_conn_priv_cmn	dp_cmn;
+};
+
+#define dp_protocol_major	dp_cmn.ricpc_protocol_major
+#define dp_protocol_minor	dp_cmn.ricpc_protocol_minor
+#define dp_protocol_minor_mask	dp_cmn.ricpc_protocol_minor_mask
+#define dp_ack_seq		dp_cmn.ricpc_ack_seq
+#define dp_credit		dp_cmn.ricpc_credit
+
+union rds_ib_conn_priv {
+	struct rds_ib_connect_private	ricp_v4;
+	struct rds6_ib_connect_private	ricp_v6;
 };
 
 struct rds_ib_send_work {
@@ -351,8 +373,8 @@ static inline void rds_ib_dma_sync_sg_for_device(struct ib_device *dev,
 __printf(2, 3)
 void __rds_ib_conn_error(struct rds_connection *conn, const char *, ...);
 int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
-			     struct rdma_cm_event *event);
-int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id);
+			     struct rdma_cm_event *event, bool isv6);
+int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id, bool isv6);
 void rds_ib_cm_connect_complete(struct rds_connection *conn,
 				struct rdma_cm_event *event);
 
@@ -361,7 +383,8 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn,
 	__rds_ib_conn_error(conn, KERN_WARNING "RDS/IB: " fmt)
 
 /* ib_rdma.c */
-int rds_ib_update_ipaddr(struct rds_ib_device *rds_ibdev, __be32 ipaddr);
+int rds_ib_update_ipaddr(struct rds_ib_device *rds_ibdev,
+			 struct in6_addr *ipaddr);
 void rds_ib_add_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *conn);
 void rds_ib_remove_conn(struct rds_ib_device *rds_ibdev, struct rds_connection *conn);
 void rds_ib_destroy_nodev_conns(void);
diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
index f1684ae..5b8b181 100644
--- a/net/rds/ib_cm.c
+++ b/net/rds/ib_cm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -35,10 +35,12 @@
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/ratelimit.h>
+#include <net/addrconf.h>
 
 #include "rds_single_path.h"
 #include "rds.h"
 #include "ib.h"
+#include "tcp.h"
 
 /*
  * Set the selected protocol version
@@ -95,25 +97,45 @@ static void rds_ib_set_flow_control(struct rds_connection *conn, u32 credits)
  */
 void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_event *event)
 {
-	const struct rds_ib_connect_private *dp = NULL;
 	struct rds_ib_connection *ic = conn->c_transport_data;
+	const union rds_ib_conn_priv *dp = NULL;
 	struct ib_qp_attr qp_attr;
+	__be64 ack_seq = 0;
+	__be32 credit = 0;
+	u8 major = 0;
+	u8 minor = 0;
 	int err;
 
-	if (event->param.conn.private_data_len >= sizeof(*dp)) {
-		dp = event->param.conn.private_data;
-
-		/* make sure it isn't empty data */
-		if (dp->dp_protocol_major) {
-			rds_ib_set_protocol(conn,
-				RDS_PROTOCOL(dp->dp_protocol_major,
-				dp->dp_protocol_minor));
-			rds_ib_set_flow_control(conn, be32_to_cpu(dp->dp_credit));
+	dp = event->param.conn.private_data;
+	if (conn->c_isv6) {
+		if (event->param.conn.private_data_len >=
+		    sizeof(struct rds6_ib_connect_private)) {
+			major = dp->ricp_v6.dp_protocol_major;
+			minor = dp->ricp_v6.dp_protocol_minor;
+			credit = dp->ricp_v6.dp_credit;
+			/* dp structure start is not guaranteed to be 8 bytes
+			 * aligned.  Since dp_ack_seq is 64-bit extended load
+			 * operations can be used so go through get_unaligned
+			 * to avoid unaligned errors.
+			 */
+			ack_seq = get_unaligned(&dp->ricp_v6.dp_ack_seq);
 		}
+	} else if (event->param.conn.private_data_len >=
+		   sizeof(struct rds_ib_connect_private)) {
+		major = dp->ricp_v4.dp_protocol_major;
+		minor = dp->ricp_v4.dp_protocol_minor;
+		credit = dp->ricp_v4.dp_credit;
+		ack_seq = get_unaligned(&dp->ricp_v4.dp_ack_seq);
+	}
+
+	/* make sure it isn't empty data */
+	if (major) {
+		rds_ib_set_protocol(conn, RDS_PROTOCOL(major, minor));
+		rds_ib_set_flow_control(conn, be32_to_cpu(credit));
 	}
 
 	if (conn->c_version < RDS_PROTOCOL(3, 1)) {
-		pr_notice("RDS/IB: Connection <%pI4,%pI4> version %u.%u no longer supported\n",
+		pr_notice("RDS/IB: Connection <%pI6c,%pI6c> version %u.%u no longer supported\n",
 			  &conn->c_laddr, &conn->c_faddr,
 			  RDS_PROTOCOL_MAJOR(conn->c_version),
 			  RDS_PROTOCOL_MINOR(conn->c_version));
@@ -121,7 +143,7 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even
 		rds_conn_destroy(conn);
 		return;
 	} else {
-		pr_notice("RDS/IB: %s conn connected <%pI4,%pI4> version %u.%u%s\n",
+		pr_notice("RDS/IB: %s conn connected <%pI6c,%pI6c> version %u.%u%s\n",
 			  ic->i_active_side ? "Active" : "Passive",
 			  &conn->c_laddr, &conn->c_faddr,
 			  RDS_PROTOCOL_MAJOR(conn->c_version),
@@ -150,7 +172,7 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even
 		printk(KERN_NOTICE "ib_modify_qp(IB_QP_STATE, RTS): err=%d\n", err);
 
 	/* update ib_device with this local ipaddr */
-	err = rds_ib_update_ipaddr(ic->rds_ibdev, conn->c_laddr);
+	err = rds_ib_update_ipaddr(ic->rds_ibdev, &conn->c_laddr);
 	if (err)
 		printk(KERN_ERR "rds_ib_update_ipaddr failed (%d)\n",
 			err);
@@ -158,14 +180,8 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even
 	/* If the peer gave us the last packet it saw, process this as if
 	 * we had received a regular ACK. */
 	if (dp) {
-		/* dp structure start is not guaranteed to be 8 bytes aligned.
-		 * Since dp_ack_seq is 64-bit extended load operations can be
-		 * used so go through get_unaligned to avoid unaligned errors.
-		 */
-		__be64 dp_ack_seq = get_unaligned(&dp->dp_ack_seq);
-
-		if (dp_ack_seq)
-			rds_send_drop_acked(conn, be64_to_cpu(dp_ack_seq),
+		if (ack_seq)
+			rds_send_drop_acked(conn, be64_to_cpu(ack_seq),
 					    NULL);
 	}
 
@@ -173,11 +189,12 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even
 }
 
 static void rds_ib_cm_fill_conn_param(struct rds_connection *conn,
-			struct rdma_conn_param *conn_param,
-			struct rds_ib_connect_private *dp,
-			u32 protocol_version,
-			u32 max_responder_resources,
-			u32 max_initiator_depth)
+				      struct rdma_conn_param *conn_param,
+				      union rds_ib_conn_priv *dp,
+				      u32 protocol_version,
+				      u32 max_responder_resources,
+				      u32 max_initiator_depth,
+				      bool isv6)
 {
 	struct rds_ib_connection *ic = conn->c_transport_data;
 	struct rds_ib_device *rds_ibdev = ic->rds_ibdev;
@@ -193,24 +210,49 @@ static void rds_ib_cm_fill_conn_param(struct rds_connection *conn,
 
 	if (dp) {
 		memset(dp, 0, sizeof(*dp));
-		dp->dp_saddr = conn->c_laddr;
-		dp->dp_daddr = conn->c_faddr;
-		dp->dp_protocol_major = RDS_PROTOCOL_MAJOR(protocol_version);
-		dp->dp_protocol_minor = RDS_PROTOCOL_MINOR(protocol_version);
-		dp->dp_protocol_minor_mask = cpu_to_be16(RDS_IB_SUPPORTED_PROTOCOLS);
-		dp->dp_ack_seq = cpu_to_be64(rds_ib_piggyb_ack(ic));
+		if (isv6) {
+			dp->ricp_v6.dp_saddr = conn->c_laddr;
+			dp->ricp_v6.dp_daddr = conn->c_faddr;
+			dp->ricp_v6.dp_protocol_major =
+			    RDS_PROTOCOL_MAJOR(protocol_version);
+			dp->ricp_v6.dp_protocol_minor =
+			    RDS_PROTOCOL_MINOR(protocol_version);
+			dp->ricp_v6.dp_protocol_minor_mask =
+			    cpu_to_be16(RDS_IB_SUPPORTED_PROTOCOLS);
+			dp->ricp_v6.dp_ack_seq =
+			    cpu_to_be64(rds_ib_piggyb_ack(ic));
+
+			conn_param->private_data = &dp->ricp_v6;
+			conn_param->private_data_len = sizeof(dp->ricp_v6);
+		} else {
+			dp->ricp_v4.dp_saddr = conn->c_laddr.s6_addr32[3];
+			dp->ricp_v4.dp_daddr = conn->c_faddr.s6_addr32[3];
+			dp->ricp_v4.dp_protocol_major =
+			    RDS_PROTOCOL_MAJOR(protocol_version);
+			dp->ricp_v4.dp_protocol_minor =
+			    RDS_PROTOCOL_MINOR(protocol_version);
+			dp->ricp_v4.dp_protocol_minor_mask =
+			    cpu_to_be16(RDS_IB_SUPPORTED_PROTOCOLS);
+			dp->ricp_v4.dp_ack_seq =
+			    cpu_to_be64(rds_ib_piggyb_ack(ic));
+
+			conn_param->private_data = &dp->ricp_v4;
+			conn_param->private_data_len = sizeof(dp->ricp_v4);
+		}
 
 		/* Advertise flow control */
 		if (ic->i_flowctl) {
 			unsigned int credits;
 
-			credits = IB_GET_POST_CREDITS(atomic_read(&ic->i_credits));
-			dp->dp_credit = cpu_to_be32(credits);
-			atomic_sub(IB_SET_POST_CREDITS(credits), &ic->i_credits);
+			credits = IB_GET_POST_CREDITS
+				(atomic_read(&ic->i_credits));
+			if (isv6)
+				dp->ricp_v6.dp_credit = cpu_to_be32(credits);
+			else
+				dp->ricp_v4.dp_credit = cpu_to_be32(credits);
+			atomic_sub(IB_SET_POST_CREDITS(credits),
+				   &ic->i_credits);
 		}
-
-		conn_param->private_data = dp;
-		conn_param->private_data_len = sizeof(*dp);
 	}
 }
 
@@ -349,7 +391,7 @@ static void rds_ib_qp_event_handler(struct ib_event *event, void *data)
 		break;
 	default:
 		rdsdebug("Fatal QP Event %u (%s) "
-			"- connection %pI4->%pI4, reconnecting\n",
+			"- connection %pI6c->%pI6c, reconnecting\n",
 			event->event, ib_event_msg(event->event),
 			&conn->c_laddr, &conn->c_faddr);
 		rds_conn_drop(conn);
@@ -580,11 +622,13 @@ static int rds_ib_setup_qp(struct rds_connection *conn)
 	return ret;
 }
 
-static u32 rds_ib_protocol_compatible(struct rdma_cm_event *event)
+static u32 rds_ib_protocol_compatible(struct rdma_cm_event *event, bool isv6)
 {
-	const struct rds_ib_connect_private *dp = event->param.conn.private_data;
-	u16 common;
+	const union rds_ib_conn_priv *dp = event->param.conn.private_data;
+	u8 data_len, major, minor;
 	u32 version = 0;
+	__be16 mask;
+	u16 common;
 
 	/*
 	 * rdma_cm private data is odd - when there is any private data in the
@@ -603,51 +647,126 @@ static u32 rds_ib_protocol_compatible(struct rdma_cm_event *event)
 		return 0;
 	}
 
+	if (isv6) {
+		data_len = sizeof(struct rds6_ib_connect_private);
+		major = dp->ricp_v6.dp_protocol_major;
+		minor = dp->ricp_v6.dp_protocol_minor;
+		mask = dp->ricp_v6.dp_protocol_minor_mask;
+	} else {
+		data_len = sizeof(struct rds_ib_connect_private);
+		major = dp->ricp_v4.dp_protocol_major;
+		minor = dp->ricp_v4.dp_protocol_minor;
+		mask = dp->ricp_v4.dp_protocol_minor_mask;
+	}
+
 	/* Even if len is crap *now* I still want to check it. -ASG */
-	if (event->param.conn.private_data_len < sizeof (*dp) ||
-	    dp->dp_protocol_major == 0)
+	if (event->param.conn.private_data_len < data_len || major == 0)
 		return RDS_PROTOCOL_3_0;
 
-	common = be16_to_cpu(dp->dp_protocol_minor_mask) & RDS_IB_SUPPORTED_PROTOCOLS;
-	if (dp->dp_protocol_major == 3 && common) {
+	common = be16_to_cpu(mask) & RDS_IB_SUPPORTED_PROTOCOLS;
+	if (major == 3 && common) {
 		version = RDS_PROTOCOL_3_0;
 		while ((common >>= 1) != 0)
 			version++;
-	} else
-		printk_ratelimited(KERN_NOTICE "RDS: Connection from %pI4 using incompatible protocol version %u.%u\n",
-				&dp->dp_saddr,
-				dp->dp_protocol_major,
-				dp->dp_protocol_minor);
+	} else {
+		if (isv6)
+			printk_ratelimited(KERN_NOTICE "RDS: Connection from %pI6c using incompatible protocol version %u.%u\n",
+					   &dp->ricp_v6.dp_saddr, major, minor);
+		else
+			printk_ratelimited(KERN_NOTICE "RDS: Connection from %pI4 using incompatible protocol version %u.%u\n",
+					   &dp->ricp_v4.dp_saddr, major, minor);
+	}
 	return version;
 }
 
+/* Given an IPv6 address, find the IB net_device which hosts that address and
+ * return its index.  This is used by the rds_ib_cm_handle_connect() code to
+ * find the interface index of where an incoming request comes from when
+ * the request is using a link local address.
+ *
+ * Note one problem in this search.  It is possible that two interfaces have
+ * the same link local address.  Unfortunately, this cannot be solved unless
+ * the underlying layer gives us the interface which an incoming RDMA connect
+ * request comes from.
+ */
+static u32 __rds_find_ifindex(struct net *net, const struct in6_addr *addr)
+{
+	struct net_device *dev;
+	int idx = 0;
+
+	rcu_read_lock();
+	for_each_netdev_rcu(net, dev) {
+		if (dev->type == ARPHRD_INFINIBAND &&
+		    ipv6_chk_addr(net, addr, dev, 0)) {
+			idx = dev->ifindex;
+			break;
+		}
+	}
+	rcu_read_unlock();
+
+	return idx;
+}
+
 int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
-				    struct rdma_cm_event *event)
+			     struct rdma_cm_event *event, bool isv6)
 {
 	__be64 lguid = cm_id->route.path_rec->sgid.global.interface_id;
 	__be64 fguid = cm_id->route.path_rec->dgid.global.interface_id;
-	const struct rds_ib_connect_private *dp = event->param.conn.private_data;
-	struct rds_ib_connect_private dp_rep;
+	const struct rds_ib_conn_priv_cmn *dp_cmn;
 	struct rds_connection *conn = NULL;
 	struct rds_ib_connection *ic = NULL;
 	struct rdma_conn_param conn_param;
+	const union rds_ib_conn_priv *dp;
+	union rds_ib_conn_priv dp_rep;
+	struct in6_addr s_mapped_addr;
+	struct in6_addr d_mapped_addr;
+	const struct in6_addr *saddr6;
+	const struct in6_addr *daddr6;
+	int destroy = 1;
+	u32 ifindex = 0;
 	u32 version;
-	int err = 1, destroy = 1;
+	int err = 1;
 
 	/* Check whether the remote protocol version matches ours. */
-	version = rds_ib_protocol_compatible(event);
+	version = rds_ib_protocol_compatible(event, isv6);
 	if (!version)
 		goto out;
 
-	rdsdebug("saddr %pI4 daddr %pI4 RDSv%u.%u lguid 0x%llx fguid "
-		 "0x%llx\n", &dp->dp_saddr, &dp->dp_daddr,
+	dp = event->param.conn.private_data;
+	if (isv6) {
+		dp_cmn = &dp->ricp_v6.dp_cmn;
+		saddr6 = &dp->ricp_v6.dp_saddr;
+		daddr6 = &dp->ricp_v6.dp_daddr;
+		/* If the local address is link local, need to find the
+		 * interface index in order to create a proper RDS
+		 * connection.
+		 */
+		if (ipv6_addr_type(daddr6) & IPV6_ADDR_LINKLOCAL) {
+			/* Using init_net for now ..  */
+			ifindex = __rds_find_ifindex(&init_net, daddr6);
+			/* No index found...  Need to bail out. */
+			if (ifindex == 0) {
+				err = -EOPNOTSUPP;
+				goto out;
+			}
+		}
+	} else {
+		dp_cmn = &dp->ricp_v4.dp_cmn;
+		ipv6_addr_set_v4mapped(dp->ricp_v4.dp_saddr, &s_mapped_addr);
+		ipv6_addr_set_v4mapped(dp->ricp_v4.dp_daddr, &d_mapped_addr);
+		saddr6 = &s_mapped_addr;
+		daddr6 = &d_mapped_addr;
+	}
+
+	rdsdebug("saddr %pI6c daddr %pI6c RDSv%u.%u lguid 0x%llx fguid "
+		 "0x%llx\n", saddr6, daddr6,
 		 RDS_PROTOCOL_MAJOR(version), RDS_PROTOCOL_MINOR(version),
 		 (unsigned long long)be64_to_cpu(lguid),
 		 (unsigned long long)be64_to_cpu(fguid));
 
 	/* RDS/IB is not currently netns aware, thus init_net */
-	conn = rds_conn_create(&init_net, dp->dp_daddr, dp->dp_saddr,
-			       &rds_ib_transport, GFP_KERNEL);
+	conn = rds_conn_create(&init_net, daddr6, saddr6,
+			       &rds_ib_transport, GFP_KERNEL, ifindex);
 	if (IS_ERR(conn)) {
 		rdsdebug("rds_conn_create failed (%ld)\n", PTR_ERR(conn));
 		conn = NULL;
@@ -678,12 +797,13 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
 	ic = conn->c_transport_data;
 
 	rds_ib_set_protocol(conn, version);
-	rds_ib_set_flow_control(conn, be32_to_cpu(dp->dp_credit));
+	rds_ib_set_flow_control(conn, be32_to_cpu(dp_cmn->ricpc_credit));
 
 	/* If the peer gave us the last packet it saw, process this as if
 	 * we had received a regular ACK. */
-	if (dp->dp_ack_seq)
-		rds_send_drop_acked(conn, be64_to_cpu(dp->dp_ack_seq), NULL);
+	if (dp_cmn->ricpc_ack_seq)
+		rds_send_drop_acked(conn, be64_to_cpu(dp_cmn->ricpc_ack_seq),
+				    NULL);
 
 	BUG_ON(cm_id->context);
 	BUG_ON(ic->i_cm_id);
@@ -702,8 +822,8 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
 	}
 
 	rds_ib_cm_fill_conn_param(conn, &conn_param, &dp_rep, version,
-		event->param.conn.responder_resources,
-		event->param.conn.initiator_depth);
+				  event->param.conn.responder_resources,
+				  event->param.conn.initiator_depth, isv6);
 
 	/* rdma_accept() calls rdma_reject() internally if it fails */
 	if (rdma_accept(cm_id, &conn_param))
@@ -718,12 +838,12 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
 }
 
 
-int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id)
+int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id, bool isv6)
 {
 	struct rds_connection *conn = cm_id->context;
 	struct rds_ib_connection *ic = conn->c_transport_data;
 	struct rdma_conn_param conn_param;
-	struct rds_ib_connect_private dp;
+	union rds_ib_conn_priv dp;
 	int ret;
 
 	/* If the peer doesn't do protocol negotiation, we must
@@ -738,7 +858,7 @@ int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id)
 	}
 
 	rds_ib_cm_fill_conn_param(conn, &conn_param, &dp, RDS_PROTOCOL_VERSION,
-		UINT_MAX, UINT_MAX);
+				  UINT_MAX, UINT_MAX, isv6);
 	ret = rdma_connect(cm_id, &conn_param);
 	if (ret)
 		rds_ib_conn_error(conn, "rdma_connect failed (%d)\n", ret);
@@ -758,13 +878,17 @@ int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id)
 int rds_ib_conn_path_connect(struct rds_conn_path *cp)
 {
 	struct rds_connection *conn = cp->cp_conn;
-	struct rds_ib_connection *ic = conn->c_transport_data;
-	struct sockaddr_in src, dest;
+	struct sockaddr_storage src, dest;
+	rdma_cm_event_handler handler;
+	struct rds_ib_connection *ic;
 	int ret;
 
+	ic = conn->c_transport_data;
+
 	/* XXX I wonder what affect the port space has */
 	/* delegate cm event handler to rdma_transport */
-	ic->i_cm_id = rdma_create_id(&init_net, rds_rdma_cm_event_handler, conn,
+	handler = rds_rdma_cm_event_handler;
+	ic->i_cm_id = rdma_create_id(&init_net, handler, conn,
 				     RDMA_PS_TCP, IB_QPT_RC);
 	if (IS_ERR(ic->i_cm_id)) {
 		ret = PTR_ERR(ic->i_cm_id);
@@ -775,13 +899,33 @@ int rds_ib_conn_path_connect(struct rds_conn_path *cp)
 
 	rdsdebug("created cm id %p for conn %p\n", ic->i_cm_id, conn);
 
-	src.sin_family = AF_INET;
-	src.sin_addr.s_addr = (__force u32)conn->c_laddr;
-	src.sin_port = (__force u16)htons(0);
+	if (ipv6_addr_v4mapped(&conn->c_faddr)) {
+		struct sockaddr_in *sin;
+
+		sin = (struct sockaddr_in *)&src;
+		sin->sin_family = AF_INET;
+		sin->sin_addr.s_addr = conn->c_laddr.s6_addr32[3];
+		sin->sin_port = 0;
 
-	dest.sin_family = AF_INET;
-	dest.sin_addr.s_addr = (__force u32)conn->c_faddr;
-	dest.sin_port = (__force u16)htons(RDS_PORT);
+		sin = (struct sockaddr_in *)&dest;
+		sin->sin_family = AF_INET;
+		sin->sin_addr.s_addr = conn->c_faddr.s6_addr32[3];
+		sin->sin_port = htons(RDS_PORT);
+	} else {
+		struct sockaddr_in6 *sin6;
+
+		sin6 = (struct sockaddr_in6 *)&src;
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_addr = conn->c_laddr;
+		sin6->sin6_port = 0;
+		sin6->sin6_scope_id = conn->c_dev_if;
+
+		sin6 = (struct sockaddr_in6 *)&dest;
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_addr = conn->c_faddr;
+		sin6->sin6_port = htons(RDS_TCP_PORT);
+		sin6->sin6_scope_id = conn->c_dev_if;
+	}
 
 	ret = rdma_resolve_addr(ic->i_cm_id, (struct sockaddr *)&src,
 				(struct sockaddr *)&dest,
diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c
index e678699..0ec9df0 100644
--- a/net/rds/ib_rdma.c
+++ b/net/rds/ib_rdma.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -100,18 +100,19 @@ static void rds_ib_remove_ipaddr(struct rds_ib_device *rds_ibdev, __be32 ipaddr)
 		kfree_rcu(to_free, rcu);
 }
 
-int rds_ib_update_ipaddr(struct rds_ib_device *rds_ibdev, __be32 ipaddr)
+int rds_ib_update_ipaddr(struct rds_ib_device *rds_ibdev,
+			 struct in6_addr *ipaddr)
 {
 	struct rds_ib_device *rds_ibdev_old;
 
-	rds_ibdev_old = rds_ib_get_device(ipaddr);
+	rds_ibdev_old = rds_ib_get_device(ipaddr->s6_addr32[3]);
 	if (!rds_ibdev_old)
-		return rds_ib_add_ipaddr(rds_ibdev, ipaddr);
+		return rds_ib_add_ipaddr(rds_ibdev, ipaddr->s6_addr32[3]);
 
 	if (rds_ibdev_old != rds_ibdev) {
-		rds_ib_remove_ipaddr(rds_ibdev_old, ipaddr);
+		rds_ib_remove_ipaddr(rds_ibdev_old, ipaddr->s6_addr32[3]);
 		rds_ib_dev_put(rds_ibdev_old);
-		return rds_ib_add_ipaddr(rds_ibdev, ipaddr);
+		return rds_ib_add_ipaddr(rds_ibdev, ipaddr->s6_addr32[3]);
 	}
 	rds_ib_dev_put(rds_ibdev_old);
 
@@ -544,7 +545,7 @@ void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents,
 	struct rds_ib_connection *ic = rs->rs_conn->c_transport_data;
 	int ret;
 
-	rds_ibdev = rds_ib_get_device(rs->rs_bound_addr);
+	rds_ibdev = rds_ib_get_device(rs->rs_bound_addr.s6_addr32[3]);
 	if (!rds_ibdev) {
 		ret = -ENODEV;
 		goto out;
diff --git a/net/rds/ib_recv.c b/net/rds/ib_recv.c
index b4e421a..f101bed 100644
--- a/net/rds/ib_recv.c
+++ b/net/rds/ib_recv.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -266,7 +266,7 @@ static struct rds_ib_incoming *rds_ib_refill_one_inc(struct rds_ib_connection *i
 		rds_ib_stats_inc(s_ib_rx_total_incs);
 	}
 	INIT_LIST_HEAD(&ibinc->ii_frags);
-	rds_inc_init(&ibinc->ii_inc, ic->conn, ic->conn->c_faddr);
+	rds_inc_init(&ibinc->ii_inc, ic->conn, &ic->conn->c_faddr);
 
 	return ibinc;
 }
@@ -420,7 +420,7 @@ void rds_ib_recv_refill(struct rds_connection *conn, int prefill, gfp_t gfp)
 		ret = ib_post_recv(ic->i_cm_id->qp, &recv->r_wr, &failed_wr);
 		if (ret) {
 			rds_ib_conn_error(conn, "recv post on "
-			       "%pI4 returned %d, disconnecting and "
+			       "%pI6c returned %d, disconnecting and "
 			       "reconnecting\n", &conn->c_faddr,
 			       ret);
 			break;
@@ -850,7 +850,7 @@ static void rds_ib_process_recv(struct rds_connection *conn,
 
 	if (data_len < sizeof(struct rds_header)) {
 		rds_ib_conn_error(conn, "incoming message "
-		       "from %pI4 didn't include a "
+		       "from %pI6c didn't include a "
 		       "header, disconnecting and "
 		       "reconnecting\n",
 		       &conn->c_faddr);
@@ -863,7 +863,7 @@ static void rds_ib_process_recv(struct rds_connection *conn,
 	/* Validate the checksum. */
 	if (!rds_message_verify_checksum(ihdr)) {
 		rds_ib_conn_error(conn, "incoming message "
-		       "from %pI4 has corrupted header - "
+		       "from %pI6c has corrupted header - "
 		       "forcing a reconnect\n",
 		       &conn->c_faddr);
 		rds_stats_inc(s_recv_drop_bad_checksum);
@@ -943,10 +943,10 @@ static void rds_ib_process_recv(struct rds_connection *conn,
 		ic->i_recv_data_rem = 0;
 		ic->i_ibinc = NULL;
 
-		if (ibinc->ii_inc.i_hdr.h_flags == RDS_FLAG_CONG_BITMAP)
+		if (ibinc->ii_inc.i_hdr.h_flags == RDS_FLAG_CONG_BITMAP) {
 			rds_ib_cong_recv(conn, ibinc);
-		else {
-			rds_recv_incoming(conn, conn->c_faddr, conn->c_laddr,
+		} else {
+			rds_recv_incoming(conn, &conn->c_faddr, &conn->c_laddr,
 					  &ibinc->ii_inc, GFP_ATOMIC);
 			state->ack_next = be64_to_cpu(hdr->h_sequence);
 			state->ack_next_valid = 1;
@@ -990,7 +990,7 @@ void rds_ib_recv_cqe_handler(struct rds_ib_connection *ic,
 	} else {
 		/* We expect errors as the qp is drained during shutdown */
 		if (rds_conn_up(conn) || rds_conn_connecting(conn))
-			rds_ib_conn_error(conn, "recv completion on <%pI4,%pI4> had status %u (%s), disconnecting and reconnecting\n",
+			rds_ib_conn_error(conn, "recv completion on <%pI6c,%pI6c> had status %u (%s), disconnecting and reconnecting\n",
 					  &conn->c_laddr, &conn->c_faddr,
 					  wc->status,
 					  ib_wc_status_msg(wc->status));
diff --git a/net/rds/ib_send.c b/net/rds/ib_send.c
index 8557a1c..c4cdfe49 100644
--- a/net/rds/ib_send.c
+++ b/net/rds/ib_send.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -305,7 +305,7 @@ void rds_ib_send_cqe_handler(struct rds_ib_connection *ic, struct ib_wc *wc)
 
 	/* We expect errors as the qp is drained during shutdown */
 	if (wc->status != IB_WC_SUCCESS && rds_conn_up(conn)) {
-		rds_ib_conn_error(conn, "send completion on <%pI4,%pI4> had status %u (%s), disconnecting and reconnecting\n",
+		rds_ib_conn_error(conn, "send completion on <%pI6c,%pI6c> had status %u (%s), disconnecting and reconnecting\n",
 				  &conn->c_laddr, &conn->c_faddr, wc->status,
 				  ib_wc_status_msg(wc->status));
 	}
@@ -730,7 +730,7 @@ int rds_ib_xmit(struct rds_connection *conn, struct rds_message *rm,
 		 first, &first->s_wr, ret, failed_wr);
 	BUG_ON(failed_wr != &first->s_wr);
 	if (ret) {
-		printk(KERN_WARNING "RDS/IB: ib_post_send to %pI4 "
+		printk(KERN_WARNING "RDS/IB: ib_post_send to %pI6c "
 		       "returned %d\n", &conn->c_faddr, ret);
 		rds_ib_ring_unalloc(&ic->i_send_ring, work_alloc);
 		rds_ib_sub_signaled(ic, nr_sig);
@@ -827,7 +827,7 @@ int rds_ib_xmit_atomic(struct rds_connection *conn, struct rm_atomic_op *op)
 		 send, &send->s_atomic_wr, ret, failed_wr);
 	BUG_ON(failed_wr != &send->s_atomic_wr.wr);
 	if (ret) {
-		printk(KERN_WARNING "RDS/IB: atomic ib_post_send to %pI4 "
+		printk(KERN_WARNING "RDS/IB: atomic ib_post_send to %pI6c "
 		       "returned %d\n", &conn->c_faddr, ret);
 		rds_ib_ring_unalloc(&ic->i_send_ring, work_alloc);
 		rds_ib_sub_signaled(ic, nr_sig);
@@ -967,7 +967,7 @@ int rds_ib_xmit_rdma(struct rds_connection *conn, struct rm_rdma_op *op)
 		 first, &first->s_rdma_wr.wr, ret, failed_wr);
 	BUG_ON(failed_wr != &first->s_rdma_wr.wr);
 	if (ret) {
-		printk(KERN_WARNING "RDS/IB: rdma ib_post_send to %pI4 "
+		printk(KERN_WARNING "RDS/IB: rdma ib_post_send to %pI6c "
 		       "returned %d\n", &conn->c_faddr, ret);
 		rds_ib_ring_unalloc(&ic->i_send_ring, work_alloc);
 		rds_ib_sub_signaled(ic, nr_sig);
diff --git a/net/rds/loop.c b/net/rds/loop.c
index dac6218..0dca895 100644
--- a/net/rds/loop.c
+++ b/net/rds/loop.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -33,6 +33,7 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/in.h>
+#include <linux/ipv6.h>
 
 #include "rds_single_path.h"
 #include "rds.h"
@@ -75,11 +76,11 @@ static int rds_loop_xmit(struct rds_connection *conn, struct rds_message *rm,
 
 	BUG_ON(hdr_off || sg || off);
 
-	rds_inc_init(&rm->m_inc, conn, conn->c_laddr);
+	rds_inc_init(&rm->m_inc, conn, &conn->c_laddr);
 	/* For the embedded inc. Matching put is in loop_inc_free() */
 	rds_message_addref(rm);
 
-	rds_recv_incoming(conn, conn->c_laddr, conn->c_faddr, &rm->m_inc,
+	rds_recv_incoming(conn, &conn->c_laddr, &conn->c_faddr, &rm->m_inc,
 			  GFP_KERNEL);
 
 	rds_send_drop_acked(conn, be64_to_cpu(rm->m_inc.i_hdr.h_sequence),
diff --git a/net/rds/rdma.c b/net/rds/rdma.c
index 634cfcb..7b39980 100644
--- a/net/rds/rdma.c
+++ b/net/rds/rdma.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007 Oracle.  All rights reserved.
+ * Copyright (c) 2007, 2017 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -183,7 +183,7 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args,
 	long i;
 	int ret;
 
-	if (rs->rs_bound_addr == 0 || !rs->rs_transport) {
+	if (ipv6_addr_any(&rs->rs_bound_addr) || !rs->rs_transport) {
 		ret = -ENOTCONN; /* XXX not a great errno */
 		goto out;
 	}
@@ -574,7 +574,7 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
 
 	args = CMSG_DATA(cmsg);
 
-	if (rs->rs_bound_addr == 0) {
+	if (ipv6_addr_any(&rs->rs_bound_addr)) {
 		ret = -ENOTCONN; /* XXX not a great errno */
 		goto out_ret;
 	}
diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c
index fc59821..aef73e7 100644
--- a/net/rds/rdma_transport.c
+++ b/net/rds/rdma_transport.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 Oracle.  All rights reserved.
+ * Copyright (c) 2009, 2018 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -39,8 +39,9 @@
 
 static struct rdma_cm_id *rds_rdma_listen_id;
 
-int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
-			      struct rdma_cm_event *event)
+static int rds_rdma_cm_event_handler_cmn(struct rdma_cm_id *cm_id,
+					 struct rdma_cm_event *event,
+					 bool isv6)
 {
 	/* this can be null in the listening path */
 	struct rds_connection *conn = cm_id->context;
@@ -72,7 +73,7 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
 
 	switch (event->event) {
 	case RDMA_CM_EVENT_CONNECT_REQUEST:
-		ret = trans->cm_handle_connect(cm_id, event);
+		ret = trans->cm_handle_connect(cm_id, event, isv6);
 		break;
 
 	case RDMA_CM_EVENT_ADDR_RESOLVED:
@@ -90,7 +91,7 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
 
 			ibic = conn->c_transport_data;
 			if (ibic && ibic->i_cm_id == cm_id)
-				ret = trans->cm_initiate_connect(cm_id);
+				ret = trans->cm_initiate_connect(cm_id, isv6);
 			else
 				rds_conn_drop(conn);
 		}
@@ -116,14 +117,14 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
 
 	case RDMA_CM_EVENT_DISCONNECTED:
 		rdsdebug("DISCONNECT event - dropping connection "
-			"%pI4->%pI4\n", &conn->c_laddr,
+			 "%pI6c->%pI6c\n", &conn->c_laddr,
 			 &conn->c_faddr);
 		rds_conn_drop(conn);
 		break;
 
 	case RDMA_CM_EVENT_TIMEWAIT_EXIT:
 		if (conn) {
-			pr_info("RDS: RDMA_CM_EVENT_TIMEWAIT_EXIT event: dropping connection %pI4->%pI4\n",
+			pr_info("RDS: RDMA_CM_EVENT_TIMEWAIT_EXIT event: dropping connection %pI6c->%pI6c\n",
 				&conn->c_laddr, &conn->c_faddr);
 			rds_conn_drop(conn);
 		}
@@ -146,13 +147,20 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
 	return ret;
 }
 
-static int rds_rdma_listen_init(void)
+int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
+			      struct rdma_cm_event *event)
+{
+	return rds_rdma_cm_event_handler_cmn(cm_id, event, false);
+}
+
+static int rds_rdma_listen_init_common(rdma_cm_event_handler handler,
+				       struct sockaddr *sa,
+				       struct rdma_cm_id **ret_cm_id)
 {
-	struct sockaddr_in sin;
 	struct rdma_cm_id *cm_id;
 	int ret;
 
-	cm_id = rdma_create_id(&init_net, rds_rdma_cm_event_handler, NULL,
+	cm_id = rdma_create_id(&init_net, handler, NULL,
 			       RDMA_PS_TCP, IB_QPT_RC);
 	if (IS_ERR(cm_id)) {
 		ret = PTR_ERR(cm_id);
@@ -161,15 +169,11 @@ static int rds_rdma_listen_init(void)
 		return ret;
 	}
 
-	sin.sin_family = AF_INET;
-	sin.sin_addr.s_addr = (__force u32)htonl(INADDR_ANY);
-	sin.sin_port = (__force u16)htons(RDS_PORT);
-
 	/*
 	 * XXX I bet this binds the cm_id to a device.  If we want to support
 	 * fail-over we'll have to take this into consideration.
 	 */
-	ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin);
+	ret = rdma_bind_addr(cm_id, sa);
 	if (ret) {
 		printk(KERN_ERR "RDS/RDMA: failed to setup listener, "
 		       "rdma_bind_addr() returned %d\n", ret);
@@ -185,7 +189,7 @@ static int rds_rdma_listen_init(void)
 
 	rdsdebug("cm %p listening on port %u\n", cm_id, RDS_PORT);
 
-	rds_rdma_listen_id = cm_id;
+	*ret_cm_id = cm_id;
 	cm_id = NULL;
 out:
 	if (cm_id)
@@ -193,6 +197,26 @@ static int rds_rdma_listen_init(void)
 	return ret;
 }
 
+/* Initialize the RDS RDMA listeners.  We create two listeners for
+ * compatibility reason.  The one on RDS_PORT is used for IPv4
+ * requests only.  The one on RDS_TCP_PORT is used for IPv6 requests
+ * only.  So only IPv6 enabled RDS module will communicate using this
+ * port.
+ */
+static int rds_rdma_listen_init(void)
+{
+	int ret;
+	struct sockaddr_in sin;
+
+	sin.sin_family = PF_INET;
+	sin.sin_addr.s_addr = htonl(INADDR_ANY);
+	sin.sin_port = htons(RDS_PORT);
+	ret = rds_rdma_listen_init_common(rds_rdma_cm_event_handler,
+					  (struct sockaddr *)&sin,
+					  &rds_rdma_listen_id);
+	return ret;
+}
+
 static void rds_rdma_listen_stop(void)
 {
 	if (rds_rdma_listen_id) {
diff --git a/net/rds/rds.h b/net/rds/rds.h
index f2272fb..859808a 100644
--- a/net/rds/rds.h
+++ b/net/rds/rds.h
@@ -10,6 +10,7 @@
 #include <linux/rds.h>
 #include <linux/rhashtable.h>
 #include <linux/refcount.h>
+#include <linux/in6.h>
 
 #include "info.h"
 
@@ -61,7 +62,7 @@ void rdsdebug(char *fmt, ...)
 
 struct rds_cong_map {
 	struct rb_node		m_rb_node;
-	__be32			m_addr;
+	struct in6_addr		m_addr;
 	wait_queue_head_t	m_waitq;
 	struct list_head	m_conn_list;
 	unsigned long		m_page_addrs[RDS_CONG_MAP_PAGES];
@@ -136,11 +137,13 @@ struct rds_conn_path {
 /* One rds_connection per RDS address pair */
 struct rds_connection {
 	struct hlist_node	c_hash_node;
-	__be32			c_laddr;
-	__be32			c_faddr;
+	struct in6_addr		c_laddr;
+	struct in6_addr		c_faddr;
+	int			c_dev_if; /* c_laddrs's interface index */
 	unsigned int		c_loopback:1,
+				c_isv6:1,
 				c_ping_triggered:1,
-				c_pad_to_32:30;
+				c_pad_to_32:29;
 	int			c_npaths;
 	struct rds_connection	*c_passive;
 	struct rds_transport	*c_trans;
@@ -269,7 +272,7 @@ struct rds_incoming {
 	struct rds_conn_path	*i_conn_path;
 	struct rds_header	i_hdr;
 	unsigned long		i_rx_jiffies;
-	__be32			i_saddr;
+	struct in6_addr		i_saddr;
 
 	rds_rdma_cookie_t	i_rdma_cookie;
 	struct timeval		i_rx_tstamp;
@@ -386,7 +389,7 @@ struct rds_message {
 	struct list_head	m_conn_item;
 	struct rds_incoming	m_inc;
 	u64			m_ack_seq;
-	__be32			m_daddr;
+	struct in6_addr		m_daddr;
 	unsigned long		m_flags;
 
 	/* Never access m_rs without holding m_rs_lock.
@@ -519,7 +522,8 @@ struct rds_transport {
 				t_mp_capable:1;
 	unsigned int		t_type;
 
-	int (*laddr_check)(struct net *net, __be32 addr);
+	int (*laddr_check)(struct net *net, const struct in6_addr *addr,
+			   __u32 scope_id);
 	int (*conn_alloc)(struct rds_connection *conn, gfp_t gfp);
 	void (*conn_free)(void *data);
 	int (*conn_path_connect)(struct rds_conn_path *cp);
@@ -535,8 +539,8 @@ struct rds_transport {
 	void (*inc_free)(struct rds_incoming *inc);
 
 	int (*cm_handle_connect)(struct rdma_cm_id *cm_id,
-				 struct rdma_cm_event *event);
-	int (*cm_initiate_connect)(struct rdma_cm_id *cm_id);
+				 struct rdma_cm_event *event, bool isv6);
+	int (*cm_initiate_connect)(struct rdma_cm_id *cm_id, bool isv6);
 	void (*cm_connect_complete)(struct rds_connection *conn,
 				    struct rdma_cm_event *event);
 
@@ -551,6 +555,12 @@ struct rds_transport {
 	bool (*t_unloading)(struct rds_connection *conn);
 };
 
+/* Bind hash table key length.  It is the sum of the size of a struct
+ * in6_addr, a scope_id  and a port.
+ */
+#define RDS_BOUND_KEY_LEN \
+	(sizeof(struct in6_addr) + sizeof(__u32) + sizeof(__be16))
+
 struct rds_sock {
 	struct sock		rs_sk;
 
@@ -562,10 +572,14 @@ struct rds_sock {
 	 * support.
 	 */
 	struct rhash_head	rs_bound_node;
-	u64			rs_bound_key;
-	__be32			rs_bound_addr;
-	__be32			rs_conn_addr;
-	__be16			rs_bound_port;
+	u8			rs_bound_key[RDS_BOUND_KEY_LEN];
+	struct sockaddr_in6	rs_bound_sin6;
+#define rs_bound_addr		rs_bound_sin6.sin6_addr
+#define rs_bound_addr_v4	rs_bound_sin6.sin6_addr.s6_addr32[3]
+#define rs_bound_port		rs_bound_sin6.sin6_port
+#define rs_bound_scope_id	rs_bound_sin6.sin6_scope_id
+	struct in6_addr		rs_conn_addr;
+#define rs_conn_addr_v4		rs_conn_addr.s6_addr32[3]
 	__be16			rs_conn_port;
 	struct rds_transport    *rs_transport;
 
@@ -701,7 +715,8 @@ static inline void __rds_wake_sk_sleep(struct sock *sk)
 /* bind.c */
 int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len);
 void rds_remove_bound(struct rds_sock *rs);
-struct rds_sock *rds_find_bound(__be32 addr, __be16 port);
+struct rds_sock *rds_find_bound(const struct in6_addr *addr, __be16 port,
+				__u32 scope_id);
 int rds_bind_lock_init(void);
 void rds_bind_lock_destroy(void);
 
@@ -725,11 +740,15 @@ static inline void __rds_wake_sk_sleep(struct sock *sk)
 int rds_conn_init(void);
 void rds_conn_exit(void);
 struct rds_connection *rds_conn_create(struct net *net,
-				       __be32 laddr, __be32 faddr,
-				       struct rds_transport *trans, gfp_t gfp);
+				       const struct in6_addr *laddr,
+				       const struct in6_addr *faddr,
+				       struct rds_transport *trans, gfp_t gfp,
+				       int dev_if);
 struct rds_connection *rds_conn_create_outgoing(struct net *net,
-						__be32 laddr, __be32 faddr,
-			       struct rds_transport *trans, gfp_t gfp);
+						const struct in6_addr *laddr,
+						const struct in6_addr *faddr,
+						struct rds_transport *trans,
+						gfp_t gfp, int dev_if);
 void rds_conn_shutdown(struct rds_conn_path *cpath);
 void rds_conn_destroy(struct rds_connection *conn);
 void rds_conn_drop(struct rds_connection *conn);
@@ -840,11 +859,12 @@ int rds_page_remainder_alloc(struct scatterlist *scat, unsigned long bytes,
 
 /* recv.c */
 void rds_inc_init(struct rds_incoming *inc, struct rds_connection *conn,
-		  __be32 saddr);
+		  struct in6_addr *saddr);
 void rds_inc_path_init(struct rds_incoming *inc, struct rds_conn_path *conn,
-		       __be32 saddr);
+		       struct in6_addr *saddr);
 void rds_inc_put(struct rds_incoming *inc);
-void rds_recv_incoming(struct rds_connection *conn, __be32 saddr, __be32 daddr,
+void rds_recv_incoming(struct rds_connection *conn, struct in6_addr *saddr,
+		       struct in6_addr *daddr,
 		       struct rds_incoming *inc, gfp_t gfp);
 int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
 		int msg_flags);
@@ -859,7 +879,7 @@ void rds_inc_info_copy(struct rds_incoming *inc,
 void rds_send_path_reset(struct rds_conn_path *conn);
 int rds_send_xmit(struct rds_conn_path *cp);
 struct sockaddr_in;
-void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in *dest);
+void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in6 *dest);
 typedef int (*is_acked_func)(struct rds_message *rm, uint64_t ack);
 void rds_send_drop_acked(struct rds_connection *conn, u64 ack,
 			 is_acked_func is_acked);
@@ -946,11 +966,14 @@ void rds_stats_info_copy(struct rds_info_iterator *iter,
 void rds_recv_worker(struct work_struct *);
 void rds_connect_path_complete(struct rds_conn_path *conn, int curr);
 void rds_connect_complete(struct rds_connection *conn);
+int rds_addr_cmp(const struct in6_addr *a1, const struct in6_addr *a2);
 
 /* transport.c */
 void rds_trans_register(struct rds_transport *trans);
 void rds_trans_unregister(struct rds_transport *trans);
-struct rds_transport *rds_trans_get_preferred(struct net *net, __be32 addr);
+struct rds_transport *rds_trans_get_preferred(struct net *net,
+					      const struct in6_addr *addr,
+					      __u32 scope_id);
 void rds_trans_put(struct rds_transport *trans);
 unsigned int rds_trans_stats_info_copy(struct rds_info_iterator *iter,
 				       unsigned int avail);
diff --git a/net/rds/recv.c b/net/rds/recv.c
index 192ac6f..4217961 100644
--- a/net/rds/recv.c
+++ b/net/rds/recv.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -41,14 +41,14 @@
 #include "rds.h"
 
 void rds_inc_init(struct rds_incoming *inc, struct rds_connection *conn,
-		  __be32 saddr)
+		 struct in6_addr *saddr)
 {
 	int i;
 
 	refcount_set(&inc->i_refcount, 1);
 	INIT_LIST_HEAD(&inc->i_item);
 	inc->i_conn = conn;
-	inc->i_saddr = saddr;
+	inc->i_saddr = *saddr;
 	inc->i_rdma_cookie = 0;
 	inc->i_rx_tstamp.tv_sec = 0;
 	inc->i_rx_tstamp.tv_usec = 0;
@@ -59,13 +59,13 @@ void rds_inc_init(struct rds_incoming *inc, struct rds_connection *conn,
 EXPORT_SYMBOL_GPL(rds_inc_init);
 
 void rds_inc_path_init(struct rds_incoming *inc, struct rds_conn_path *cp,
-		       __be32 saddr)
+		       struct in6_addr  *saddr)
 {
 	refcount_set(&inc->i_refcount, 1);
 	INIT_LIST_HEAD(&inc->i_item);
 	inc->i_conn = cp->cp_conn;
 	inc->i_conn_path = cp;
-	inc->i_saddr = saddr;
+	inc->i_saddr = *saddr;
 	inc->i_rdma_cookie = 0;
 	inc->i_rx_tstamp.tv_sec = 0;
 	inc->i_rx_tstamp.tv_usec = 0;
@@ -110,7 +110,7 @@ static void rds_recv_rcvbuf_delta(struct rds_sock *rs, struct sock *sk,
 
 	now_congested = rs->rs_rcv_bytes > rds_sk_rcvbuf(rs);
 
-	rdsdebug("rs %p (%pI4:%u) recv bytes %d buf %d "
+	rdsdebug("rs %p (%pI6c:%u) recv bytes %d buf %d "
 	  "now_cong %d delta %d\n",
 	  rs, &rs->rs_bound_addr,
 	  ntohs(rs->rs_bound_port), rs->rs_rcv_bytes,
@@ -260,7 +260,7 @@ static void rds_start_mprds(struct rds_connection *conn)
 	struct rds_conn_path *cp;
 
 	if (conn->c_npaths > 1 &&
-	    IS_CANONICAL(conn->c_laddr, conn->c_faddr)) {
+	    rds_addr_cmp(&conn->c_laddr, &conn->c_faddr) < 0) {
 		for (i = 0; i < conn->c_npaths; i++) {
 			cp = &conn->c_path[i];
 			rds_conn_path_connect_if_down(cp);
@@ -284,7 +284,8 @@ static void rds_start_mprds(struct rds_connection *conn)
  * conn.  This lets loopback, who only has one conn for both directions,
  * tell us which roles the addrs in the conn are playing for this message.
  */
-void rds_recv_incoming(struct rds_connection *conn, __be32 saddr, __be32 daddr,
+void rds_recv_incoming(struct rds_connection *conn, struct in6_addr *saddr,
+		       struct in6_addr *daddr,
 		       struct rds_incoming *inc, gfp_t gfp)
 {
 	struct rds_sock *rs = NULL;
@@ -339,7 +340,8 @@ void rds_recv_incoming(struct rds_connection *conn, __be32 saddr, __be32 daddr,
 
 	if (rds_sysctl_ping_enable && inc->i_hdr.h_dport == 0) {
 		if (inc->i_hdr.h_sport == 0) {
-			rdsdebug("ignore ping with 0 sport from 0x%x\n", saddr);
+			rdsdebug("ignore ping with 0 sport from %pI6c\n",
+				 saddr);
 			goto out;
 		}
 		rds_stats_inc(s_recv_ping);
@@ -362,7 +364,7 @@ void rds_recv_incoming(struct rds_connection *conn, __be32 saddr, __be32 daddr,
 		goto out;
 	}
 
-	rs = rds_find_bound(daddr, inc->i_hdr.h_dport);
+	rs = rds_find_bound(daddr, inc->i_hdr.h_dport, conn->c_dev_if);
 	if (!rs) {
 		rds_stats_inc(s_recv_drop_no_sock);
 		goto out;
@@ -625,6 +627,7 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
 	struct rds_sock *rs = rds_sk_to_rs(sk);
 	long timeo;
 	int ret = 0, nonblock = msg_flags & MSG_DONTWAIT;
+	DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name);
 	DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name);
 	struct rds_incoming *inc = NULL;
 
@@ -673,7 +676,7 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
 			break;
 		}
 
-		rdsdebug("copying inc %p from %pI4:%u to user\n", inc,
+		rdsdebug("copying inc %p from %pI6c:%u to user\n", inc,
 			 &inc->i_conn->c_faddr,
 			 ntohs(inc->i_hdr.h_sport));
 		ret = inc->i_conn->c_trans->inc_copy_to_user(inc, &msg->msg_iter);
@@ -707,12 +710,26 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
 
 		rds_stats_inc(s_recv_delivered);
 
-		if (sin) {
-			sin->sin_family = AF_INET;
-			sin->sin_port = inc->i_hdr.h_sport;
-			sin->sin_addr.s_addr = inc->i_saddr;
-			memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-			msg->msg_namelen = sizeof(*sin);
+		if (msg->msg_name) {
+			if (ipv6_addr_v4mapped(&inc->i_saddr)) {
+				sin = (struct sockaddr_in *)msg->msg_name;
+
+				sin->sin_family = AF_INET;
+				sin->sin_port = inc->i_hdr.h_sport;
+				sin->sin_addr.s_addr =
+				    inc->i_saddr.s6_addr32[3];
+				memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+				msg->msg_namelen = sizeof(*sin);
+			} else {
+				sin6 = (struct sockaddr_in6 *)msg->msg_name;
+
+				sin6->sin6_family = AF_INET6;
+				sin6->sin6_port = inc->i_hdr.h_sport;
+				sin6->sin6_addr = inc->i_saddr;
+				sin6->sin6_flowinfo = 0;
+				sin6->sin6_scope_id = rs->rs_bound_scope_id;
+				msg->msg_namelen = sizeof(*sin6);
+			}
 		}
 		break;
 	}
diff --git a/net/rds/send.c b/net/rds/send.c
index 94c7f74..6ed2e92 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -709,7 +709,7 @@ void rds_send_drop_acked(struct rds_connection *conn, u64 ack,
 }
 EXPORT_SYMBOL_GPL(rds_send_drop_acked);
 
-void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in *dest)
+void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in6 *dest)
 {
 	struct rds_message *rm, *tmp;
 	struct rds_connection *conn;
@@ -721,8 +721,9 @@ void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in *dest)
 	spin_lock_irqsave(&rs->rs_lock, flags);
 
 	list_for_each_entry_safe(rm, tmp, &rs->rs_send_queue, m_sock_item) {
-		if (dest && (dest->sin_addr.s_addr != rm->m_daddr ||
-			     dest->sin_port != rm->m_inc.i_hdr.h_dport))
+		if (dest &&
+		    (!ipv6_addr_equal(&dest->sin6_addr, &rm->m_daddr) ||
+		     dest->sin6_port != rm->m_inc.i_hdr.h_dport))
 			continue;
 
 		list_move(&rm->m_sock_item, &list);
@@ -1059,8 +1060,8 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
 {
 	struct sock *sk = sock->sk;
 	struct rds_sock *rs = rds_sk_to_rs(sk);
+	DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name);
 	DECLARE_SOCKADDR(struct sockaddr_in *, usin, msg->msg_name);
-	__be32 daddr;
 	__be16 dport;
 	struct rds_message *rm = NULL;
 	struct rds_connection *conn;
@@ -1069,10 +1070,13 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
 	int nonblock = msg->msg_flags & MSG_DONTWAIT;
 	long timeo = sock_sndtimeo(sk, nonblock);
 	struct rds_conn_path *cpath;
+	struct in6_addr daddr;
+	__u32 scope_id = 0;
 	size_t total_payload_len = payload_len, rdma_payload_len = 0;
 	bool zcopy = ((msg->msg_flags & MSG_ZEROCOPY) &&
 		      sock_flag(rds_rs_to_sk(rs), SOCK_ZEROCOPY));
 	int num_sgs = ceil(payload_len, PAGE_SIZE);
+	int namelen;
 
 	/* Mirror Linux UDP mirror of BSD error message compatibility */
 	/* XXX: Perhaps MSG_MORE someday */
@@ -1081,27 +1085,59 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
 		goto out;
 	}
 
-	if (msg->msg_namelen) {
-		/* XXX fail non-unicast destination IPs? */
-		if (msg->msg_namelen < sizeof(*usin) || usin->sin_family != AF_INET) {
+	namelen = msg->msg_namelen;
+	if (namelen != 0) {
+		if (namelen < sizeof(*usin)) {
+			ret = -EINVAL;
+			goto out;
+		}
+		switch (namelen) {
+		case sizeof(*usin):
+			if (usin->sin_family != AF_INET ||
+			    usin->sin_addr.s_addr == htonl(INADDR_ANY) ||
+			    usin->sin_addr.s_addr == htonl(INADDR_BROADCAST) ||
+			    IN_MULTICAST(ntohl(usin->sin_addr.s_addr))) {
+				ret = -EINVAL;
+				goto out;
+			}
+			ipv6_addr_set_v4mapped(usin->sin_addr.s_addr, &daddr);
+			dport = usin->sin_port;
+			break;
+
+		case sizeof(*sin6): {
+			ret = -EPROTONOSUPPORT;
+			goto out;
+		}
+
+		default:
 			ret = -EINVAL;
 			goto out;
 		}
-		daddr = usin->sin_addr.s_addr;
-		dport = usin->sin_port;
 	} else {
 		/* We only care about consistency with ->connect() */
 		lock_sock(sk);
 		daddr = rs->rs_conn_addr;
 		dport = rs->rs_conn_port;
+		scope_id = rs->rs_bound_scope_id;
 		release_sock(sk);
 	}
 
 	lock_sock(sk);
-	if (daddr == 0 || rs->rs_bound_addr == 0) {
+	if (ipv6_addr_any(&rs->rs_bound_addr) || ipv6_addr_any(&daddr)) {
 		release_sock(sk);
-		ret = -ENOTCONN; /* XXX not a great errno */
+		ret = -ENOTCONN;
 		goto out;
+	} else if (namelen != 0) {
+		/* Cannot send to an IPv4 address using an IPv6 source
+		 * address and cannot send to an IPv6 address using an
+		 * IPv4 source address.
+		 */
+		if (ipv6_addr_v4mapped(&daddr) ^
+		    ipv6_addr_v4mapped(&rs->rs_bound_addr)) {
+			release_sock(sk);
+			ret = -EOPNOTSUPP;
+			goto out;
+		}
 	}
 	release_sock(sk);
 
@@ -1155,13 +1191,14 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
 
 	/* rds_conn_create has a spinlock that runs with IRQ off.
 	 * Caching the conn in the socket helps a lot. */
-	if (rs->rs_conn && rs->rs_conn->c_faddr == daddr)
+	if (rs->rs_conn && ipv6_addr_equal(&rs->rs_conn->c_faddr, &daddr))
 		conn = rs->rs_conn;
 	else {
 		conn = rds_conn_create_outgoing(sock_net(sock->sk),
-						rs->rs_bound_addr, daddr,
-					rs->rs_transport,
-					sock->sk->sk_allocation);
+						&rs->rs_bound_addr, &daddr,
+						rs->rs_transport,
+						sock->sk->sk_allocation,
+						scope_id);
 		if (IS_ERR(conn)) {
 			ret = PTR_ERR(conn);
 			goto out;
diff --git a/net/rds/tcp.c b/net/rds/tcp.c
index 351a284..dadb337 100644
--- a/net/rds/tcp.c
+++ b/net/rds/tcp.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -37,6 +37,8 @@
 #include <net/tcp.h>
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
+#include <net/tcp.h>
+#include <net/addrconf.h>
 
 #include "rds.h"
 #include "tcp.h"
@@ -262,9 +264,33 @@ static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len,
 	spin_unlock_irqrestore(&rds_tcp_tc_list_lock, flags);
 }
 
-static int rds_tcp_laddr_check(struct net *net, __be32 addr)
+static int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+			       __u32 scope_id)
 {
-	if (inet_addr_type(net, addr) == RTN_LOCAL)
+	struct net_device *dev = NULL;
+	int ret;
+
+	if (ipv6_addr_v4mapped(addr)) {
+		if (inet_addr_type(net, addr->s6_addr32[3]) == RTN_LOCAL)
+			return 0;
+		return -EADDRNOTAVAIL;
+	}
+
+	/* If the scope_id is specified, check only those addresses
+	 * hosted on the specified interface.
+	 */
+	if (scope_id != 0) {
+		rcu_read_lock();
+		dev = dev_get_by_index_rcu(net, scope_id);
+		/* scope_id is not valid... */
+		if (!dev) {
+			rcu_read_unlock();
+			return -EADDRNOTAVAIL;
+		}
+		rcu_read_unlock();
+	}
+	ret = ipv6_chk_addr(net, addr, dev, 0);
+	if (ret)
 		return 0;
 	return -EADDRNOTAVAIL;
 }
diff --git a/net/rds/tcp_connect.c b/net/rds/tcp_connect.c
index d999e70..231ae92 100644
--- a/net/rds/tcp_connect.c
+++ b/net/rds/tcp_connect.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -66,7 +66,8 @@ void rds_tcp_state_change(struct sock *sk)
 		 * RDS connection as RDS_CONN_UP until the reconnect,
 		 * to avoid RDS datagram loss.
 		 */
-		if (!IS_CANONICAL(cp->cp_conn->c_laddr, cp->cp_conn->c_faddr) &&
+		if (rds_addr_cmp(&cp->cp_conn->c_laddr,
+				 &cp->cp_conn->c_faddr) >= 0 &&
 		    rds_conn_path_transition(cp, RDS_CONN_CONNECTING,
 					     RDS_CONN_ERROR)) {
 			rds_conn_path_drop(cp, false);
@@ -88,7 +89,9 @@ void rds_tcp_state_change(struct sock *sk)
 int rds_tcp_conn_path_connect(struct rds_conn_path *cp)
 {
 	struct socket *sock = NULL;
-	struct sockaddr_in src, dest;
+	struct sockaddr_in sin;
+	struct sockaddr *addr;
+	int addrlen;
 	int ret;
 	struct rds_connection *conn = cp->cp_conn;
 	struct rds_tcp_connection *tc = cp->cp_transport_data;
@@ -112,30 +115,33 @@ int rds_tcp_conn_path_connect(struct rds_conn_path *cp)
 
 	rds_tcp_tune(sock);
 
-	src.sin_family = AF_INET;
-	src.sin_addr.s_addr = (__force u32)conn->c_laddr;
-	src.sin_port = (__force u16)htons(0);
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = conn->c_laddr.s6_addr32[3];
+	sin.sin_port = 0;
+	addr = (struct sockaddr *)&sin;
+	addrlen = sizeof(sin);
 
-	ret = sock->ops->bind(sock, (struct sockaddr *)&src, sizeof(src));
+	ret = sock->ops->bind(sock, addr, addrlen);
 	if (ret) {
-		rdsdebug("bind failed with %d at address %pI4\n",
+		rdsdebug("bind failed with %d at address %pI6c\n",
 			 ret, &conn->c_laddr);
 		goto out;
 	}
 
-	dest.sin_family = AF_INET;
-	dest.sin_addr.s_addr = (__force u32)conn->c_faddr;
-	dest.sin_port = (__force u16)htons(RDS_TCP_PORT);
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = conn->c_faddr.s6_addr32[3];
+	sin.sin_port = htons(RDS_TCP_PORT);
+	addr = (struct sockaddr *)&sin;
+	addrlen = sizeof(sin);
 
 	/*
 	 * once we call connect() we can start getting callbacks and they
 	 * own the socket
 	 */
 	rds_tcp_set_callbacks(sock, cp);
-	ret = sock->ops->connect(sock, (struct sockaddr *)&dest, sizeof(dest),
-				 O_NONBLOCK);
+	ret = sock->ops->connect(sock, addr, addrlen, O_NONBLOCK);
 
-	rdsdebug("connect to address %pI4 returned %d\n", &conn->c_faddr, ret);
+	rdsdebug("connect to address %pI6c returned %d\n", &conn->c_faddr, ret);
 	if (ret == -EINPROGRESS)
 		ret = 0;
 	if (ret == 0) {
diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c
index 2257118..4fdf5b3 100644
--- a/net/rds/tcp_listen.c
+++ b/net/rds/tcp_listen.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2018 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -83,13 +83,12 @@ int rds_tcp_keepalive(struct socket *sock)
 struct rds_tcp_connection *rds_tcp_accept_one_path(struct rds_connection *conn)
 {
 	int i;
-	bool peer_is_smaller = IS_CANONICAL(conn->c_faddr, conn->c_laddr);
 	int npaths = max_t(int, 1, conn->c_npaths);
 
 	/* for mprds, all paths MUST be initiated by the peer
 	 * with the smaller address.
 	 */
-	if (!peer_is_smaller) {
+	if (rds_addr_cmp(&conn->c_faddr, &conn->c_laddr) >= 0) {
 		/* Make sure we initiate at least one path if this
 		 * has not already been done; rds_start_mprds() will
 		 * take care of additional paths, if necessary.
@@ -164,13 +163,16 @@ int rds_tcp_accept_one(struct socket *sock)
 
 	inet = inet_sk(new_sock->sk);
 
-	rdsdebug("accepted tcp %pI4:%u -> %pI4:%u\n",
-		 &inet->inet_saddr, ntohs(inet->inet_sport),
-		 &inet->inet_daddr, ntohs(inet->inet_dport));
+	rdsdebug("accepted tcp %pI6c:%u -> %pI6c:%u\n",
+		 &new_sock->sk->sk_v6_rcv_saddr, ntohs(inet->inet_sport),
+		 &new_sock->sk->sk_v6_daddr, ntohs(inet->inet_dport));
 
 	conn = rds_conn_create(sock_net(sock->sk),
-			       inet->inet_saddr, inet->inet_daddr,
-			       &rds_tcp_transport, GFP_KERNEL);
+			       &new_sock->sk->sk_v6_rcv_saddr,
+			       &new_sock->sk->sk_v6_daddr,
+			       &rds_tcp_transport, GFP_KERNEL,
+			       new_sock->sk->sk_bound_dev_if);
+
 	if (IS_ERR(conn)) {
 		ret = PTR_ERR(conn);
 		goto out;
diff --git a/net/rds/tcp_recv.c b/net/rds/tcp_recv.c
index b9fbd2e..42c5ff1 100644
--- a/net/rds/tcp_recv.c
+++ b/net/rds/tcp_recv.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -179,7 +179,7 @@ static int rds_tcp_data_recv(read_descriptor_t *desc, struct sk_buff *skb,
 			tc->t_tinc = tinc;
 			rdsdebug("alloced tinc %p\n", tinc);
 			rds_inc_path_init(&tinc->ti_inc, cp,
-					  cp->cp_conn->c_faddr);
+					  &cp->cp_conn->c_faddr);
 			tinc->ti_inc.i_rx_lat_trace[RDS_MSG_RX_HDR] =
 					local_clock();
 
@@ -239,8 +239,9 @@ static int rds_tcp_data_recv(read_descriptor_t *desc, struct sk_buff *skb,
 			if (tinc->ti_inc.i_hdr.h_flags == RDS_FLAG_CONG_BITMAP)
 				rds_tcp_cong_recv(conn, tinc);
 			else
-				rds_recv_incoming(conn, conn->c_faddr,
-						  conn->c_laddr, &tinc->ti_inc,
+				rds_recv_incoming(conn, &conn->c_faddr,
+						  &conn->c_laddr,
+						  &tinc->ti_inc,
 						  arg->gfp);
 
 			tc->t_tinc_hdr_rem = sizeof(struct rds_header);
diff --git a/net/rds/tcp_send.c b/net/rds/tcp_send.c
index 7df869d..78a2554 100644
--- a/net/rds/tcp_send.c
+++ b/net/rds/tcp_send.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -153,7 +153,7 @@ int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm,
 			 * an incoming RST.
 			 */
 			if (rds_conn_path_up(cp)) {
-				pr_warn("RDS/tcp: send to %pI4 on cp [%d]"
+				pr_warn("RDS/tcp: send to %pI6c on cp [%d]"
 					"returned %d, "
 					"disconnecting and reconnecting\n",
 					&conn->c_faddr, cp->cp_index, ret);
diff --git a/net/rds/threads.c b/net/rds/threads.c
index c52861d..e64f9e4 100644
--- a/net/rds/threads.c
+++ b/net/rds/threads.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -82,8 +82,8 @@ void rds_connect_path_complete(struct rds_conn_path *cp, int curr)
 		return;
 	}
 
-	rdsdebug("conn %p for %pI4 to %pI4 complete\n",
-	  cp->cp_conn, &cp->cp_conn->c_laddr, &cp->cp_conn->c_faddr);
+	rdsdebug("conn %p for %pI6c to %pI6c complete\n",
+		 cp->cp_conn, &cp->cp_conn->c_laddr, &cp->cp_conn->c_faddr);
 
 	cp->cp_reconnect_jiffies = 0;
 	set_bit(0, &cp->cp_conn->c_map_queued);
@@ -125,13 +125,13 @@ void rds_queue_reconnect(struct rds_conn_path *cp)
 	unsigned long rand;
 	struct rds_connection *conn = cp->cp_conn;
 
-	rdsdebug("conn %p for %pI4 to %pI4 reconnect jiffies %lu\n",
-	  conn, &conn->c_laddr, &conn->c_faddr,
-	  cp->cp_reconnect_jiffies);
+	rdsdebug("conn %p for %pI6c to %pI6c reconnect jiffies %lu\n",
+		 conn, &conn->c_laddr, &conn->c_faddr,
+		 cp->cp_reconnect_jiffies);
 
 	/* let peer with smaller addr initiate reconnect, to avoid duels */
 	if (conn->c_trans->t_type == RDS_TRANS_TCP &&
-	    !IS_CANONICAL(conn->c_laddr, conn->c_faddr))
+	    rds_addr_cmp(&conn->c_laddr, &conn->c_faddr) >= 0)
 		return;
 
 	set_bit(RDS_RECONNECT_PENDING, &cp->cp_flags);
@@ -145,7 +145,7 @@ void rds_queue_reconnect(struct rds_conn_path *cp)
 	}
 
 	get_random_bytes(&rand, sizeof(rand));
-	rdsdebug("%lu delay %lu ceil conn %p for %pI4 -> %pI4\n",
+	rdsdebug("%lu delay %lu ceil conn %p for %pI6c -> %pI6c\n",
 		 rand % cp->cp_reconnect_jiffies, cp->cp_reconnect_jiffies,
 		 conn, &conn->c_laddr, &conn->c_faddr);
 	rcu_read_lock();
@@ -167,14 +167,14 @@ void rds_connect_worker(struct work_struct *work)
 	int ret;
 
 	if (cp->cp_index > 0 &&
-	    !IS_CANONICAL(cp->cp_conn->c_laddr, cp->cp_conn->c_faddr))
+	    rds_addr_cmp(&cp->cp_conn->c_laddr, &cp->cp_conn->c_faddr) >= 0)
 		return;
 	clear_bit(RDS_RECONNECT_PENDING, &cp->cp_flags);
 	ret = rds_conn_path_transition(cp, RDS_CONN_DOWN, RDS_CONN_CONNECTING);
 	if (ret) {
 		ret = conn->c_trans->conn_path_connect(cp);
-		rdsdebug("conn %p for %pI4 to %pI4 dispatched, ret %d\n",
-			conn, &conn->c_laddr, &conn->c_faddr, ret);
+		rdsdebug("conn %p for %pI6c to %pI6c dispatched, ret %d\n",
+			 conn, &conn->c_laddr, &conn->c_faddr, ret);
 
 		if (ret) {
 			if (rds_conn_path_transition(cp,
@@ -259,3 +259,50 @@ int rds_threads_init(void)
 
 	return 0;
 }
+
+/* Compare two IPv6 addresses.  Return 0 if the two addresses are equal.
+ * Return 1 if the first is greater.  Return -1 if the second is greater.
+ */
+int rds_addr_cmp(const struct in6_addr *addr1,
+		 const struct in6_addr *addr2)
+{
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
+	const __be64 *a1, *a2;
+	u64 x, y;
+
+	a1 = (__be64 *)addr1;
+	a2 = (__be64 *)addr2;
+
+	if (*a1 != *a2) {
+		if (be64_to_cpu(*a1) < be64_to_cpu(*a2))
+			return -1;
+		else
+			return 1;
+	} else {
+		x = be64_to_cpu(*++a1);
+		y = be64_to_cpu(*++a2);
+		if (x < y)
+			return -1;
+		else if (x > y)
+			return 1;
+		else
+			return 0;
+	}
+#else
+	u32 a, b;
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		if (addr1->s6_addr32[i] != addr2->s6_addr32[i]) {
+			a = ntohl(addr1->s6_addr32[i]);
+			b = ntohl(addr2->s6_addr32[i]);
+			if (a < b)
+				return -1;
+			else if (a > b)
+				return 1;
+		}
+	}
+	return 0;
+#endif
+}
+EXPORT_SYMBOL_GPL(rds_addr_cmp);
diff --git a/net/rds/transport.c b/net/rds/transport.c
index 0b188dd..c9788db 100644
--- a/net/rds/transport.c
+++ b/net/rds/transport.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -33,6 +33,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/in.h>
+#include <linux/ipv6.h>
 
 #include "rds.h"
 #include "loop.h"
@@ -75,20 +76,26 @@ void rds_trans_put(struct rds_transport *trans)
 		module_put(trans->t_owner);
 }
 
-struct rds_transport *rds_trans_get_preferred(struct net *net, __be32 addr)
+struct rds_transport *rds_trans_get_preferred(struct net *net,
+					      const struct in6_addr *addr,
+					      __u32 scope_id)
 {
 	struct rds_transport *ret = NULL;
 	struct rds_transport *trans;
 	unsigned int i;
 
-	if (IN_LOOPBACK(ntohl(addr)))
+	if (ipv6_addr_v4mapped(addr)) {
+		if (*(u_int8_t *)&addr->s6_addr32[3] == IN_LOOPBACKNET)
+			return &rds_loop_transport;
+	} else if (ipv6_addr_loopback(addr)) {
 		return &rds_loop_transport;
+	}
 
 	down_read(&rds_trans_sem);
 	for (i = 0; i < RDS_TRANS_COUNT; i++) {
 		trans = transports[i];
 
-		if (trans && (trans->laddr_check(net, addr) == 0) &&
+		if (trans && (trans->laddr_check(net, addr, scope_id) == 0) &&
 		    (!trans->t_owner || try_module_get(trans->t_owner))) {
 			ret = trans;
 			break;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 net-next 2/3] rds: Enable RDS IPv6 support
  2018-06-27 10:23 [PATCH v2 net-next 0/3] rds: IPv6 support Ka-Cheong Poon
  2018-06-27 10:23 ` [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr Ka-Cheong Poon
@ 2018-06-27 10:23 ` Ka-Cheong Poon
  2018-06-27 10:23 ` [PATCH v2 net-next 3/3] rds: Extend RDS API for " Ka-Cheong Poon
  2018-07-05 17:44 ` [PATCH v2 net-next 0/3] rds: " Sowmini Varadhan
  3 siblings, 0 replies; 18+ messages in thread
From: Ka-Cheong Poon @ 2018-06-27 10:23 UTC (permalink / raw)
  To: netdev; +Cc: santosh.shilimkar, davem, rds-devel

This patch enables RDS to use IPv6 addresses. For RDS/TCP, the
listener is now an IPv6 endpoint which accepts both IPv4 and IPv6
connection requests.  RDS/RDMA/IB uses a private data (struct
rds_ib_connect_private) exchange between endpoints at RDS connection
establishment time to support RDMA. This private data exchange uses a
32 bit integer to represent an IP address. This needs to be changed in
order to support IPv6. A new private data struct
rds6_ib_connect_private is introduced to handle this. To ensure
backward compatibility, an IPv6 capable RDS stack uses another RDMA
listener port (RDS_CM_PORT) to accept IPv6 connection. And it
continues to use the original RDS_PORT for IPv4 RDS connections. When
it needs to communicate with an IPv6 peer, it uses the RDS_CM_PORT to
send the connection set up request.

v2: Fixed bound and peer address scope mismatched issue.
    Added back rds_connect() IPv6 changes.

Signed-off-by: Ka-Cheong Poon <ka-cheong.poon@oracle.com>
---
 net/rds/af_rds.c         | 28 +++++++++++++++++++++++-
 net/rds/bind.c           | 34 +++++++++++++++++++++++++-----
 net/rds/connection.c     | 43 ++++++++++++++++++++++++-------------
 net/rds/ib.c             | 55 +++++++++++++++++++++++++++++++++++++++++-------
 net/rds/ib_cm.c          | 13 ++++++------
 net/rds/rdma_transport.c | 32 ++++++++++++++++++++++++++--
 net/rds/rdma_transport.h |  2 ++
 net/rds/rds.h            | 12 ++++++-----
 net/rds/send.c           | 32 ++++++++++++++++++++++++++--
 net/rds/tcp.c            | 54 +++++++++++++++++++++++++++++------------------
 net/rds/tcp.h            |  4 +---
 net/rds/tcp_connect.c    | 54 ++++++++++++++++++++++++++++++++++++-----------
 net/rds/tcp_listen.c     | 40 +++++++++++++++++++++++++++--------
 13 files changed, 315 insertions(+), 88 deletions(-)

diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c
index fc1a5c6..8ce2d92 100644
--- a/net/rds/af_rds.c
+++ b/net/rds/af_rds.c
@@ -484,7 +484,9 @@ static int rds_connect(struct socket *sock, struct sockaddr *uaddr,
 {
 	struct sock *sk = sock->sk;
 	struct sockaddr_in *sin;
+	struct sockaddr_in6 *sin6;
 	struct rds_sock *rs = rds_sk_to_rs(sk);
+	int addr_type;
 	int ret = 0;
 
 	lock_sock(sk);
@@ -510,7 +512,31 @@ static int rds_connect(struct socket *sock, struct sockaddr *uaddr,
 		break;
 
 	case sizeof(struct sockaddr_in6):
-		ret = -EPROTONOSUPPORT;
+		sin6 = (struct sockaddr_in6 *)uaddr;
+		if (sin6->sin6_family != AF_INET6) {
+			ret = -EAFNOSUPPORT;
+			break;
+		}
+		addr_type = ipv6_addr_type(&sin6->sin6_addr);
+		if (!(addr_type & IPV6_ADDR_UNICAST)) {
+			ret = -EPROTOTYPE;
+			break;
+		}
+		if (addr_type & IPV6_ADDR_LINKLOCAL) {
+			if (sin6->sin6_scope_id == 0 ||
+			    (!ipv6_addr_any(&rs->rs_bound_addr) &&
+			     sin6->sin6_scope_id != rs->rs_bound_scope_id)) {
+				ret = -EINVAL;
+				break;
+			}
+			/* Remember the connected address scope ID.  It will
+			 * be checked against the binding local address when
+			 * the socket is bound.
+			 */
+			rs->rs_bound_scope_id = sin6->sin6_scope_id;
+		}
+		rs->rs_conn_addr = sin6->sin6_addr;
+		rs->rs_conn_port = sin6->sin6_port;
 		break;
 
 	default:
diff --git a/net/rds/bind.c b/net/rds/bind.c
index 3822886..6e6e4ea 100644
--- a/net/rds/bind.c
+++ b/net/rds/bind.c
@@ -127,9 +127,10 @@ static int rds_add_bound(struct rds_sock *rs, const struct in6_addr *addr,
 		if (!rhashtable_insert_fast(&bind_hash_table,
 					    &rs->rs_bound_node, ht_parms)) {
 			*port = rs->rs_bound_port;
+			rs->rs_bound_scope_id = scope_id;
 			ret = 0;
-			rdsdebug("rs %p binding to %pI4:%d\n",
-			  rs, &addr, (int)ntohs(*port));
+			rdsdebug("rs %p binding to %pI6c:%d\n",
+				 rs, addr, (int)ntohs(*port));
 			break;
 		} else {
 			rs->rs_bound_addr = in6addr_any;
@@ -164,11 +165,12 @@ int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 	struct in6_addr v6addr, *binding_addr;
 	struct rds_transport *trans;
 	__u32 scope_id = 0;
+	int addr_type;
 	int ret = 0;
 	__be16 port;
 
-	/* We only allow an RDS socket to be bound to and IPv4 address. IPv6
-	 * address support will be added later.
+	/* We allow an RDS socket to be bound to either IPv4 or IPv6
+	 * address.
 	 */
 	if (addr_len == sizeof(struct sockaddr_in)) {
 		struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
@@ -180,7 +182,21 @@ int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 		binding_addr = &v6addr;
 		port = sin->sin_port;
 	} else if (addr_len == sizeof(struct sockaddr_in6)) {
-		return -EPROTONOSUPPORT;
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)uaddr;
+
+		addr_type = ipv6_addr_type(&sin6->sin6_addr);
+		if (sin6->sin6_family != AF_INET6 ||
+		    !(addr_type & IPV6_ADDR_UNICAST)) {
+			return -EINVAL;
+		}
+		/* The scope ID must be specified for link local address. */
+		if (addr_type & IPV6_ADDR_LINKLOCAL) {
+			if (sin6->sin6_scope_id == 0)
+				return -EINVAL;
+			scope_id = sin6->sin6_scope_id;
+		}
+		binding_addr = &sin6->sin6_addr;
+		port = sin6->sin6_port;
 	} else {
 		return -EINVAL;
 	}
@@ -191,6 +207,14 @@ int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 		ret = -EINVAL;
 		goto out;
 	}
+	/* Socket is connected.  The binding address should have the same
+	 * scope ID as the connected address.
+	 */
+	if (!ipv6_addr_any(&rs->rs_conn_addr) &&
+	    scope_id != rs->rs_bound_scope_id) {
+		ret = -EINVAL;
+		goto out;
+	}
 
 	ret = rds_add_bound(rs, binding_addr, &port, scope_id);
 	if (ret)
diff --git a/net/rds/connection.c b/net/rds/connection.c
index ca72563..8c5d093 100644
--- a/net/rds/connection.c
+++ b/net/rds/connection.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -486,10 +486,17 @@ void rds_conn_destroy(struct rds_connection *conn)
 }
 EXPORT_SYMBOL_GPL(rds_conn_destroy);
 
-static void rds_conn_message_info(struct socket *sock, unsigned int len,
-				  struct rds_info_iterator *iter,
-				  struct rds_info_lengths *lens,
-				  int want_send)
+static void __rds_inc_msg_cp(struct rds_incoming *inc,
+			     struct rds_info_iterator *iter,
+			     void *saddr, void *daddr, int flip)
+{
+	rds_inc_info_copy(inc, iter, *(__be32 *)saddr, *(__be32 *)daddr, flip);
+}
+
+static void rds_conn_message_info_cmn(struct socket *sock, unsigned int len,
+				      struct rds_info_iterator *iter,
+				      struct rds_info_lengths *lens,
+				      int want_send)
 {
 	struct hlist_head *head;
 	struct list_head *list;
@@ -524,18 +531,13 @@ static void rds_conn_message_info(struct socket *sock, unsigned int len,
 
 				/* XXX too lazy to maintain counts.. */
 				list_for_each_entry(rm, list, m_conn_item) {
-					__be32 laddr;
-					__be32 faddr;
-
 					total++;
-					laddr = conn->c_laddr.s6_addr32[3];
-					faddr = conn->c_faddr.s6_addr32[3];
 					if (total <= len)
-						rds_inc_info_copy(&rm->m_inc,
-								  iter,
-								  laddr,
-								  faddr,
-								  0);
+						__rds_inc_msg_cp(&rm->m_inc,
+								 iter,
+								 &conn->c_laddr,
+								 &conn->c_faddr,
+								 0);
 				}
 
 				spin_unlock_irqrestore(&cp->cp_lock, flags);
@@ -548,6 +550,14 @@ static void rds_conn_message_info(struct socket *sock, unsigned int len,
 	lens->each = sizeof(struct rds_info_message);
 }
 
+static void rds_conn_message_info(struct socket *sock, unsigned int len,
+				  struct rds_info_iterator *iter,
+				  struct rds_info_lengths *lens,
+				  int want_send)
+{
+	rds_conn_message_info_cmn(sock, len, iter, lens, want_send);
+}
+
 static void rds_conn_message_info_send(struct socket *sock, unsigned int len,
 				       struct rds_info_iterator *iter,
 				       struct rds_info_lengths *lens)
@@ -655,6 +665,9 @@ static int rds_conn_info_visitor(struct rds_conn_path *cp, void *buffer)
 	struct rds_info_connection *cinfo = buffer;
 	struct rds_connection *conn = cp->cp_conn;
 
+	if (conn->c_isv6)
+		return 0;
+
 	cinfo->next_tx_seq = cp->cp_next_tx_seq;
 	cinfo->next_rx_seq = cp->cp_next_rx_seq;
 	cinfo->laddr = conn->c_laddr.s6_addr32[3];
diff --git a/net/rds/ib.c b/net/rds/ib.c
index c712a84..756225c 100644
--- a/net/rds/ib.c
+++ b/net/rds/ib.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -39,6 +39,7 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <net/addrconf.h>
 
 #include "rds_single_path.h"
 #include "rds.h"
@@ -295,6 +296,8 @@ static int rds_ib_conn_info_visitor(struct rds_connection *conn,
 	/* We will only ever look at IB transports */
 	if (conn->c_trans != &rds_ib_transport)
 		return 0;
+	if (conn->c_isv6)
+		return 0;
 
 	iinfo->src_addr = conn->c_laddr.s6_addr32[3];
 	iinfo->dst_addr = conn->c_faddr.s6_addr32[3];
@@ -330,7 +333,6 @@ static void rds_ib_ic_info(struct socket *sock, unsigned int len,
 				sizeof(struct rds_info_rdma_connection));
 }
 
-
 /*
  * Early RDS/IB was built to only bind to an address if there is an IPoIB
  * device with that address set.
@@ -346,8 +348,12 @@ static int rds_ib_laddr_check(struct net *net, const struct in6_addr *addr,
 {
 	int ret;
 	struct rdma_cm_id *cm_id;
+	struct sockaddr_in6 sin6;
 	struct sockaddr_in sin;
+	struct sockaddr *sa;
+	bool isv4;
 
+	isv4 = ipv6_addr_v4mapped(addr);
 	/* Create a CMA ID and try to bind it. This catches both
 	 * IB and iWARP capable NICs.
 	 */
@@ -356,20 +362,53 @@ static int rds_ib_laddr_check(struct net *net, const struct in6_addr *addr,
 	if (IS_ERR(cm_id))
 		return PTR_ERR(cm_id);
 
-	memset(&sin, 0, sizeof(sin));
-	sin.sin_family = AF_INET;
-	sin.sin_addr.s_addr = addr->s6_addr32[3];
+	if (isv4) {
+		memset(&sin, 0, sizeof(sin));
+		sin.sin_family = AF_INET;
+		sin.sin_addr.s_addr = addr->s6_addr32[3];
+		sa = (struct sockaddr *)&sin;
+	} else {
+		memset(&sin6, 0, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_addr = *addr;
+		sin6.sin6_scope_id = scope_id;
+		sa = (struct sockaddr *)&sin6;
+
+		/* XXX Do a special IPv6 link local address check here.  The
+		 * reason is that rdma_bind_addr() always succeeds with IPv6
+		 * link local address regardless it is indeed configured in a
+		 * system.
+		 */
+		if (ipv6_addr_type(addr) & IPV6_ADDR_LINKLOCAL) {
+			struct net_device *dev;
+
+			if (scope_id == 0)
+				return -EADDRNOTAVAIL;
+
+			/* Use init_net for now as RDS is not network
+			 * name space aware.
+			 */
+			dev = dev_get_by_index(&init_net, scope_id);
+			if (!dev)
+				return -EADDRNOTAVAIL;
+			if (!ipv6_chk_addr(&init_net, addr, dev, 1)) {
+				dev_put(dev);
+				return -EADDRNOTAVAIL;
+			}
+			dev_put(dev);
+		}
+	}
 
 	/* rdma_bind_addr will only succeed for IB & iWARP devices */
-	ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin);
+	ret = rdma_bind_addr(cm_id, sa);
 	/* due to this, we will claim to support iWARP devices unless we
 	   check node_type. */
 	if (ret || !cm_id->device ||
 	    cm_id->device->node_type != RDMA_NODE_IB_CA)
 		ret = -EADDRNOTAVAIL;
 
-	rdsdebug("addr %pI6c ret %d node type %d\n",
-		 addr, ret,
+	rdsdebug("addr %pI6c%%%u ret %d node type %d\n",
+		 addr, scope_id, ret,
 		 cm_id->device ? cm_id->device->node_type : -1);
 
 	rdma_destroy_id(cm_id);
diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
index 5b8b181..250be1c 100644
--- a/net/rds/ib_cm.c
+++ b/net/rds/ib_cm.c
@@ -40,7 +40,6 @@
 #include "rds_single_path.h"
 #include "rds.h"
 #include "ib.h"
-#include "tcp.h"
 
 /*
  * Set the selected protocol version
@@ -679,7 +678,7 @@ static u32 rds_ib_protocol_compatible(struct rdma_cm_event *event, bool isv6)
 	return version;
 }
 
-/* Given an IPv6 address, find the IB net_device which hosts that address and
+/* Given an IPv6 address, find the net_device which hosts that address and
  * return its index.  This is used by the rds_ib_cm_handle_connect() code to
  * find the interface index of where an incoming request comes from when
  * the request is using a link local address.
@@ -696,8 +695,7 @@ static u32 __rds_find_ifindex(struct net *net, const struct in6_addr *addr)
 
 	rcu_read_lock();
 	for_each_netdev_rcu(net, dev) {
-		if (dev->type == ARPHRD_INFINIBAND &&
-		    ipv6_chk_addr(net, addr, dev, 0)) {
+		if (ipv6_chk_addr(net, addr, dev, 0)) {
 			idx = dev->ifindex;
 			break;
 		}
@@ -887,7 +885,10 @@ int rds_ib_conn_path_connect(struct rds_conn_path *cp)
 
 	/* XXX I wonder what affect the port space has */
 	/* delegate cm event handler to rdma_transport */
-	handler = rds_rdma_cm_event_handler;
+	if (conn->c_isv6)
+		handler = rds6_rdma_cm_event_handler;
+	else
+		handler = rds_rdma_cm_event_handler;
 	ic->i_cm_id = rdma_create_id(&init_net, handler, conn,
 				     RDMA_PS_TCP, IB_QPT_RC);
 	if (IS_ERR(ic->i_cm_id)) {
@@ -923,7 +924,7 @@ int rds_ib_conn_path_connect(struct rds_conn_path *cp)
 		sin6 = (struct sockaddr_in6 *)&dest;
 		sin6->sin6_family = AF_INET6;
 		sin6->sin6_addr = conn->c_faddr;
-		sin6->sin6_port = htons(RDS_TCP_PORT);
+		sin6->sin6_port = htons(RDS_CM_PORT);
 		sin6->sin6_scope_id = conn->c_dev_if;
 	}
 
diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c
index aef73e7..bd67e55 100644
--- a/net/rds/rdma_transport.c
+++ b/net/rds/rdma_transport.c
@@ -37,7 +37,9 @@
 #include "rdma_transport.h"
 #include "ib.h"
 
+/* Global IPv4 and IPv6 RDS RDMA listener cm_id */
 static struct rdma_cm_id *rds_rdma_listen_id;
+static struct rdma_cm_id *rds6_rdma_listen_id;
 
 static int rds_rdma_cm_event_handler_cmn(struct rdma_cm_id *cm_id,
 					 struct rdma_cm_event *event,
@@ -153,6 +155,12 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
 	return rds_rdma_cm_event_handler_cmn(cm_id, event, false);
 }
 
+int rds6_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
+			       struct rdma_cm_event *event)
+{
+	return rds_rdma_cm_event_handler_cmn(cm_id, event, true);
+}
+
 static int rds_rdma_listen_init_common(rdma_cm_event_handler handler,
 				       struct sockaddr *sa,
 				       struct rdma_cm_id **ret_cm_id)
@@ -199,13 +207,14 @@ static int rds_rdma_listen_init_common(rdma_cm_event_handler handler,
 
 /* Initialize the RDS RDMA listeners.  We create two listeners for
  * compatibility reason.  The one on RDS_PORT is used for IPv4
- * requests only.  The one on RDS_TCP_PORT is used for IPv6 requests
+ * requests only.  The one on RDS_CM_PORT is used for IPv6 requests
  * only.  So only IPv6 enabled RDS module will communicate using this
  * port.
  */
 static int rds_rdma_listen_init(void)
 {
 	int ret;
+	struct sockaddr_in6 sin6;
 	struct sockaddr_in sin;
 
 	sin.sin_family = PF_INET;
@@ -214,7 +223,21 @@ static int rds_rdma_listen_init(void)
 	ret = rds_rdma_listen_init_common(rds_rdma_cm_event_handler,
 					  (struct sockaddr *)&sin,
 					  &rds_rdma_listen_id);
-	return ret;
+	if (ret != 0)
+		return ret;
+
+	sin6.sin6_family = PF_INET6;
+	sin6.sin6_addr = in6addr_any;
+	sin6.sin6_port = htons(RDS_CM_PORT);
+	sin6.sin6_scope_id = 0;
+	sin6.sin6_flowinfo = 0;
+	ret = rds_rdma_listen_init_common(rds6_rdma_cm_event_handler,
+					  (struct sockaddr *)&sin6,
+					  &rds6_rdma_listen_id);
+	/* Keep going even when IPv6 is not enabled in the system. */
+	if (ret != 0)
+		rdsdebug("Cannot set up IPv6 RDMA listener\n");
+	return 0;
 }
 
 static void rds_rdma_listen_stop(void)
@@ -224,6 +247,11 @@ static void rds_rdma_listen_stop(void)
 		rdma_destroy_id(rds_rdma_listen_id);
 		rds_rdma_listen_id = NULL;
 	}
+	if (rds6_rdma_listen_id) {
+		rdsdebug("cm %p\n", rds6_rdma_listen_id);
+		rdma_destroy_id(rds6_rdma_listen_id);
+		rds6_rdma_listen_id = NULL;
+	}
 }
 
 static int rds_rdma_init(void)
diff --git a/net/rds/rdma_transport.h b/net/rds/rdma_transport.h
index d309c44..bc3c639 100644
--- a/net/rds/rdma_transport.h
+++ b/net/rds/rdma_transport.h
@@ -11,6 +11,8 @@
 int rds_rdma_conn_connect(struct rds_connection *conn);
 int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
 			      struct rdma_cm_event *event);
+int rds6_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
+			       struct rdma_cm_event *event);
 
 /* from ib.c */
 extern struct rds_transport rds_ib_transport;
diff --git a/net/rds/rds.h b/net/rds/rds.h
index 859808a..f5f99d1 100644
--- a/net/rds/rds.h
+++ b/net/rds/rds.h
@@ -24,13 +24,15 @@
 #define RDS_PROTOCOL_MINOR(v)	((v) & 255)
 #define RDS_PROTOCOL(maj, min)	(((maj) << 8) | min)
 
-/*
- * XXX randomly chosen, but at least seems to be unused:
- * #               18464-18768 Unassigned
- * We should do better.  We want a reserved port to discourage unpriv'ed
- * userspace from listening.
+/* The following ports, 16385, 18634, 18635, are registered with IANA as
+ * the ports to be used for RDS over TCP and UDP.  18634 is the historical
+ * value used for the RDMA_CM listener port.  RDS/TCP uses port 16385.  After
+ * IPv6 work, RDMA_CM also uses 16385 as the listener port.  18634 is kept
+ * to ensure compatibility with older RDS modules.
  */
 #define RDS_PORT	18634
+#define RDS_CM_PORT	16385
+#define RDS_TCP_PORT	RDS_CM_PORT
 
 #ifdef ATOMIC64_INIT
 #define KERNEL_HAS_ATOMIC64
diff --git a/net/rds/send.c b/net/rds/send.c
index 6ed2e92..c0e4f0b 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -1105,8 +1105,28 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
 			break;
 
 		case sizeof(*sin6): {
-			ret = -EPROTONOSUPPORT;
-			goto out;
+			int addr_type;
+
+			if (sin6->sin6_family != AF_INET6) {
+				ret = -EINVAL;
+				goto out;
+			}
+			addr_type = ipv6_addr_type(&sin6->sin6_addr);
+			if (!(addr_type & IPV6_ADDR_UNICAST)) {
+				ret = -EINVAL;
+				goto out;
+			}
+			if (addr_type & IPV6_ADDR_LINKLOCAL) {
+				if (sin6->sin6_scope_id == 0) {
+					ret = -EINVAL;
+					goto out;
+				}
+				scope_id = sin6->sin6_scope_id;
+			}
+
+			daddr = sin6->sin6_addr;
+			dport = sin6->sin6_port;
+			break;
 		}
 
 		default:
@@ -1138,6 +1158,14 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
 			ret = -EOPNOTSUPP;
 			goto out;
 		}
+		/* If the socket is already bound to a link local address,
+		 * it can only send to peers on the same link.
+		 */
+		if (scope_id != rs->rs_bound_scope_id) {
+			release_sock(sk);
+			ret = -EINVAL;
+			goto out;
+		}
 	}
 	release_sock(sk);
 
diff --git a/net/rds/tcp.c b/net/rds/tcp.c
index dadb337..890d0e1 100644
--- a/net/rds/tcp.c
+++ b/net/rds/tcp.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -46,7 +46,12 @@
 /* only for info exporting */
 static DEFINE_SPINLOCK(rds_tcp_tc_list_lock);
 static LIST_HEAD(rds_tcp_tc_list);
+
+/* rds_tcp_tc_count counts only IPv4 connections.
+ * rds6_tcp_tc_count counts both IPv4 and IPv6 connections.
+ */
 static unsigned int rds_tcp_tc_count;
+static unsigned int rds6_tcp_tc_count;
 
 /* Track rds_tcp_connection structs so they can be cleaned up */
 static DEFINE_SPINLOCK(rds_tcp_conn_lock);
@@ -113,7 +118,9 @@ void rds_tcp_restore_callbacks(struct socket *sock,
 	/* done under the callback_lock to serialize with write_space */
 	spin_lock(&rds_tcp_tc_list_lock);
 	list_del_init(&tc->t_list_item);
-	rds_tcp_tc_count--;
+	rds6_tcp_tc_count--;
+	if (!tc->t_cpath->cp_conn->c_isv6)
+		rds_tcp_tc_count--;
 	spin_unlock(&rds_tcp_tc_list_lock);
 
 	tc->t_sock = NULL;
@@ -200,7 +207,9 @@ void rds_tcp_set_callbacks(struct socket *sock, struct rds_conn_path *cp)
 	/* done under the callback_lock to serialize with write_space */
 	spin_lock(&rds_tcp_tc_list_lock);
 	list_add_tail(&tc->t_list_item, &rds_tcp_tc_list);
-	rds_tcp_tc_count++;
+	rds6_tcp_tc_count++;
+	if (!tc->t_cpath->cp_conn->c_isv6)
+		rds_tcp_tc_count++;
 	spin_unlock(&rds_tcp_tc_list_lock);
 
 	/* accepted sockets need our listen data ready undone */
@@ -221,6 +230,9 @@ void rds_tcp_set_callbacks(struct socket *sock, struct rds_conn_path *cp)
 	write_unlock_bh(&sock->sk->sk_callback_lock);
 }
 
+/* Handle RDS_INFO_TCP_SOCKETS socket option.  It only returns IPv4
+ * connections for backward compatibility.
+ */
 static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len,
 			    struct rds_info_iterator *iter,
 			    struct rds_info_lengths *lens)
@@ -228,8 +240,6 @@ static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len,
 	struct rds_info_tcp_socket tsinfo;
 	struct rds_tcp_connection *tc;
 	unsigned long flags;
-	struct sockaddr_in sin;
-	struct socket *sock;
 
 	spin_lock_irqsave(&rds_tcp_tc_list_lock, flags);
 
@@ -237,16 +247,15 @@ static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len,
 		goto out;
 
 	list_for_each_entry(tc, &rds_tcp_tc_list, t_list_item) {
+		struct inet_sock *inet = inet_sk(tc->t_sock->sk);
 
-		sock = tc->t_sock;
-		if (sock) {
-			sock->ops->getname(sock, (struct sockaddr *)&sin, 0);
-			tsinfo.local_addr = sin.sin_addr.s_addr;
-			tsinfo.local_port = sin.sin_port;
-			sock->ops->getname(sock, (struct sockaddr *)&sin, 1);
-			tsinfo.peer_addr = sin.sin_addr.s_addr;
-			tsinfo.peer_port = sin.sin_port;
-		}
+		if (tc->t_cpath->cp_conn->c_isv6)
+			continue;
+
+		tsinfo.local_addr = inet->inet_saddr;
+		tsinfo.local_port = inet->inet_sport;
+		tsinfo.peer_addr = inet->inet_daddr;
+		tsinfo.peer_port = inet->inet_dport;
 
 		tsinfo.hdr_rem = tc->t_tinc_hdr_rem;
 		tsinfo.data_rem = tc->t_tinc_data_rem;
@@ -494,13 +503,18 @@ static __net_init int rds_tcp_init_net(struct net *net)
 		err = -ENOMEM;
 		goto fail;
 	}
-	rtn->rds_tcp_listen_sock = rds_tcp_listen_init(net);
+	rtn->rds_tcp_listen_sock = rds_tcp_listen_init(net, true);
 	if (!rtn->rds_tcp_listen_sock) {
-		pr_warn("could not set up listen sock\n");
-		unregister_net_sysctl_table(rtn->rds_tcp_sysctl);
-		rtn->rds_tcp_sysctl = NULL;
-		err = -EAFNOSUPPORT;
-		goto fail;
+		pr_warn("could not set up IPv6 listen sock\n");
+
+		/* Try IPv4 as some systems disable IPv6 */
+		rtn->rds_tcp_listen_sock = rds_tcp_listen_init(net, false);
+		if (!rtn->rds_tcp_listen_sock) {
+			unregister_net_sysctl_table(rtn->rds_tcp_sysctl);
+			rtn->rds_tcp_sysctl = NULL;
+			err = -EAFNOSUPPORT;
+			goto fail;
+		}
 	}
 	INIT_WORK(&rtn->rds_tcp_accept_w, rds_tcp_accept_worker);
 	return 0;
diff --git a/net/rds/tcp.h b/net/rds/tcp.h
index c6fa080..6a948c1 100644
--- a/net/rds/tcp.h
+++ b/net/rds/tcp.h
@@ -2,8 +2,6 @@
 #ifndef _RDS_TCP_H
 #define _RDS_TCP_H
 
-#define RDS_TCP_PORT	16385
-
 struct rds_tcp_incoming {
 	struct rds_incoming	ti_inc;
 	struct sk_buff_head	ti_skb_list;
@@ -67,7 +65,7 @@ void rds_tcp_restore_callbacks(struct socket *sock,
 void rds_tcp_state_change(struct sock *sk);
 
 /* tcp_listen.c */
-struct socket *rds_tcp_listen_init(struct net *);
+struct socket *rds_tcp_listen_init(struct net *net, bool isv6);
 void rds_tcp_listen_stop(struct socket *sock, struct work_struct *acceptor);
 void rds_tcp_listen_data_ready(struct sock *sk);
 int rds_tcp_accept_one(struct socket *sock);
diff --git a/net/rds/tcp_connect.c b/net/rds/tcp_connect.c
index 231ae92..008f50f 100644
--- a/net/rds/tcp_connect.c
+++ b/net/rds/tcp_connect.c
@@ -89,9 +89,11 @@ void rds_tcp_state_change(struct sock *sk)
 int rds_tcp_conn_path_connect(struct rds_conn_path *cp)
 {
 	struct socket *sock = NULL;
+	struct sockaddr_in6 sin6;
 	struct sockaddr_in sin;
 	struct sockaddr *addr;
 	int addrlen;
+	bool isv6;
 	int ret;
 	struct rds_connection *conn = cp->cp_conn;
 	struct rds_tcp_connection *tc = cp->cp_transport_data;
@@ -108,18 +110,36 @@ int rds_tcp_conn_path_connect(struct rds_conn_path *cp)
 		mutex_unlock(&tc->t_conn_path_lock);
 		return 0;
 	}
-	ret = sock_create_kern(rds_conn_net(conn), PF_INET,
-			       SOCK_STREAM, IPPROTO_TCP, &sock);
+	if (ipv6_addr_v4mapped(&conn->c_laddr)) {
+		ret = sock_create_kern(rds_conn_net(conn), PF_INET,
+				       SOCK_STREAM, IPPROTO_TCP, &sock);
+		isv6 = false;
+	} else {
+		ret = sock_create_kern(rds_conn_net(conn), PF_INET6,
+				       SOCK_STREAM, IPPROTO_TCP, &sock);
+		isv6 = true;
+	}
+
 	if (ret < 0)
 		goto out;
 
 	rds_tcp_tune(sock);
 
-	sin.sin_family = AF_INET;
-	sin.sin_addr.s_addr = conn->c_laddr.s6_addr32[3];
-	sin.sin_port = 0;
-	addr = (struct sockaddr *)&sin;
-	addrlen = sizeof(sin);
+	if (isv6) {
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_addr = conn->c_laddr;
+		sin6.sin6_port = 0;
+		sin6.sin6_flowinfo = 0;
+		sin6.sin6_scope_id = conn->c_dev_if;
+		addr = (struct sockaddr *)&sin6;
+		addrlen = sizeof(sin6);
+	} else {
+		sin.sin_family = AF_INET;
+		sin.sin_addr.s_addr = conn->c_laddr.s6_addr32[3];
+		sin.sin_port = 0;
+		addr = (struct sockaddr *)&sin;
+		addrlen = sizeof(sin);
+	}
 
 	ret = sock->ops->bind(sock, addr, addrlen);
 	if (ret) {
@@ -128,11 +148,21 @@ int rds_tcp_conn_path_connect(struct rds_conn_path *cp)
 		goto out;
 	}
 
-	sin.sin_family = AF_INET;
-	sin.sin_addr.s_addr = conn->c_faddr.s6_addr32[3];
-	sin.sin_port = htons(RDS_TCP_PORT);
-	addr = (struct sockaddr *)&sin;
-	addrlen = sizeof(sin);
+	if (isv6) {
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_addr = conn->c_faddr;
+		sin6.sin6_port = htons(RDS_TCP_PORT);
+		sin6.sin6_flowinfo = 0;
+		sin6.sin6_scope_id = conn->c_dev_if;
+		addr = (struct sockaddr *)&sin6;
+		addrlen = sizeof(sin6);
+	} else {
+		sin.sin_family = AF_INET;
+		sin.sin_addr.s_addr = conn->c_faddr.s6_addr32[3];
+		sin.sin_port = htons(RDS_TCP_PORT);
+		addr = (struct sockaddr *)&sin;
+		addrlen = sizeof(sin);
+	}
 
 	/*
 	 * once we call connect() we can start getting callbacks and they
diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c
index 4fdf5b3..0f996e4 100644
--- a/net/rds/tcp_listen.c
+++ b/net/rds/tcp_listen.c
@@ -256,15 +256,22 @@ void rds_tcp_listen_data_ready(struct sock *sk)
 		ready(sk);
 }
 
-struct socket *rds_tcp_listen_init(struct net *net)
+struct socket *rds_tcp_listen_init(struct net *net, bool isv6)
 {
-	struct sockaddr_in sin;
 	struct socket *sock = NULL;
+	struct sockaddr_storage ss;
+	struct sockaddr_in6 *sin6;
+	struct sockaddr_in *sin;
+	int addr_len;
 	int ret;
 
-	ret = sock_create_kern(net, PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
-	if (ret < 0)
+	ret = sock_create_kern(net, isv6 ? PF_INET6 : PF_INET, SOCK_STREAM,
+			       IPPROTO_TCP, &sock);
+	if (ret < 0) {
+		rdsdebug("could not create %s listener socket: %d\n",
+			 isv6 ? "IPv6" : "IPv4", ret);
 		goto out;
+	}
 
 	sock->sk->sk_reuse = SK_CAN_REUSE;
 	rds_tcp_nonagle(sock);
@@ -274,13 +281,28 @@ struct socket *rds_tcp_listen_init(struct net *net)
 	sock->sk->sk_data_ready = rds_tcp_listen_data_ready;
 	write_unlock_bh(&sock->sk->sk_callback_lock);
 
-	sin.sin_family = PF_INET;
-	sin.sin_addr.s_addr = (__force u32)htonl(INADDR_ANY);
-	sin.sin_port = (__force u16)htons(RDS_TCP_PORT);
+	if (isv6) {
+		sin6 = (struct sockaddr_in6 *)&ss;
+		sin6->sin6_family = PF_INET6;
+		sin6->sin6_addr = in6addr_any;
+		sin6->sin6_port = (__force u16)htons(RDS_TCP_PORT);
+		sin6->sin6_scope_id = 0;
+		sin6->sin6_flowinfo = 0;
+		addr_len = sizeof(*sin6);
+	} else {
+		sin = (struct sockaddr_in *)&ss;
+		sin->sin_family = PF_INET;
+		sin->sin_addr.s_addr = INADDR_ANY;
+		sin->sin_port = (__force u16)htons(RDS_TCP_PORT);
+		addr_len = sizeof(*sin);
+	}
 
-	ret = sock->ops->bind(sock, (struct sockaddr *)&sin, sizeof(sin));
-	if (ret < 0)
+	ret = sock->ops->bind(sock, (struct sockaddr *)&ss, addr_len);
+	if (ret < 0) {
+		rdsdebug("could not bind %s listener socket: %d\n",
+			 isv6 ? "IPv6" : "IPv4", ret);
 		goto out;
+	}
 
 	ret = sock->ops->listen(sock, 64);
 	if (ret < 0)
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 net-next 3/3] rds: Extend RDS API for IPv6 support
  2018-06-27 10:23 [PATCH v2 net-next 0/3] rds: IPv6 support Ka-Cheong Poon
  2018-06-27 10:23 ` [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr Ka-Cheong Poon
  2018-06-27 10:23 ` [PATCH v2 net-next 2/3] rds: Enable RDS IPv6 support Ka-Cheong Poon
@ 2018-06-27 10:23 ` Ka-Cheong Poon
  2018-07-05 17:44 ` [PATCH v2 net-next 0/3] rds: " Sowmini Varadhan
  3 siblings, 0 replies; 18+ messages in thread
From: Ka-Cheong Poon @ 2018-06-27 10:23 UTC (permalink / raw)
  To: netdev; +Cc: santosh.shilimkar, davem, rds-devel

There are many data structures (RDS socket options) used by RDS apps
which use a 32 bit integer to store IP address. To support IPv6,
struct in6_addr needs to be used. To ensure backward compatibility, a
new data structure is introduced for each of those data structures
which use a 32 bit integer to represent an IP address. And new socket
options are introduced to use those new structures. This means that
existing apps should work without a problem with the new RDS module.
For apps which want to use IPv6, those new data structures and socket
options can be used. IPv4 mapped address is used to represent IPv4
address in the new data structures.

Signed-off-by: Ka-Cheong Poon <ka-cheong.poon@oracle.com>
---
 include/uapi/linux/rds.h |  71 +++++++++++++++++++++++++++++++--
 net/rds/connection.c     | 100 +++++++++++++++++++++++++++++++++++++++++++----
 net/rds/ib.c             |  52 ++++++++++++++++++++++++
 net/rds/ib_mr.h          |   2 +
 net/rds/ib_rdma.c        |  11 +++++-
 net/rds/rds.h            |   4 ++
 net/rds/recv.c           |  25 ++++++++++++
 net/rds/tcp.c            |  44 +++++++++++++++++++++
 8 files changed, 298 insertions(+), 11 deletions(-)

diff --git a/include/uapi/linux/rds.h b/include/uapi/linux/rds.h
index 20c6bd0..518d40f 100644
--- a/include/uapi/linux/rds.h
+++ b/include/uapi/linux/rds.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR Linux-OpenIB) */
 /*
- * Copyright (c) 2008 Oracle.  All rights reserved.
+ * Copyright (c) 2008, 2018 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -52,7 +52,7 @@
 #define RDS_RECVERR			5
 #define RDS_CONG_MONITOR		6
 #define RDS_GET_MR_FOR_DEST		7
-#define SO_RDS_TRANSPORT		8
+#define SO_RDS_TRANSPORT		9
 
 /* Socket option to tap receive path latency
  *	SO_RDS: SO_RDS_MSG_RXPATH_LATENCY
@@ -118,7 +118,17 @@
 #define RDS_INFO_IB_CONNECTIONS		10008
 #define RDS_INFO_CONNECTION_STATS	10009
 #define RDS_INFO_IWARP_CONNECTIONS	10010
-#define RDS_INFO_LAST			10010
+
+/* PF_RDS6 options */
+#define RDS6_INFO_CONNECTIONS		10011
+#define RDS6_INFO_SEND_MESSAGES		10012
+#define RDS6_INFO_RETRANS_MESSAGES	10013
+#define RDS6_INFO_RECV_MESSAGES		10014
+#define RDS6_INFO_SOCKETS		10015
+#define RDS6_INFO_TCP_SOCKETS		10016
+#define RDS6_INFO_IB_CONNECTIONS	10017
+
+#define RDS_INFO_LAST			10017
 
 struct rds_info_counter {
 	__u8	name[32];
@@ -140,6 +150,15 @@ struct rds_info_connection {
 	__u8		flags;
 } __attribute__((packed));
 
+struct rds6_info_connection {
+	__u64		next_tx_seq;
+	__u64		next_rx_seq;
+	struct in6_addr	laddr;
+	struct in6_addr	faddr;
+	__u8		transport[TRANSNAMSIZ];		/* null term ascii */
+	__u8		flags;
+} __attribute__((packed));
+
 #define RDS_INFO_MESSAGE_FLAG_ACK               0x01
 #define RDS_INFO_MESSAGE_FLAG_FAST_ACK          0x02
 
@@ -153,6 +172,17 @@ struct rds_info_message {
 	__u8		flags;
 } __attribute__((packed));
 
+struct rds6_info_message {
+	__u64	seq;
+	__u32	len;
+	struct in6_addr	laddr;
+	struct in6_addr	faddr;
+	__be16		lport;
+	__be16		fport;
+	__u8		flags;
+	__u8		tos;
+} __attribute__((packed));
+
 struct rds_info_socket {
 	__u32		sndbuf;
 	__be32		bound_addr;
@@ -163,6 +193,16 @@ struct rds_info_socket {
 	__u64		inum;
 } __attribute__((packed));
 
+struct rds6_info_socket {
+	__u32		sndbuf;
+	struct in6_addr	bound_addr;
+	struct in6_addr	connected_addr;
+	__be16		bound_port;
+	__be16		connected_port;
+	__u32		rcvbuf;
+	__u64		inum;
+} __attribute__((packed));
+
 struct rds_info_tcp_socket {
 	__be32          local_addr;
 	__be16          local_port;
@@ -175,6 +215,18 @@ struct rds_info_tcp_socket {
 	__u32           last_seen_una;
 } __attribute__((packed));
 
+struct rds6_info_tcp_socket {
+	struct in6_addr	local_addr;
+	__be16		local_port;
+	struct in6_addr	peer_addr;
+	__be16		peer_port;
+	__u64		hdr_rem;
+	__u64		data_rem;
+	__u32		last_sent_nxt;
+	__u32		last_expected_una;
+	__u32		last_seen_una;
+} __attribute__((packed));
+
 #define RDS_IB_GID_LEN	16
 struct rds_info_rdma_connection {
 	__be32		src_addr;
@@ -189,6 +241,19 @@ struct rds_info_rdma_connection {
 	__u32		rdma_mr_size;
 };
 
+struct rds6_info_rdma_connection {
+	struct in6_addr	src_addr;
+	struct in6_addr	dst_addr;
+	__u8		src_gid[RDS_IB_GID_LEN];
+	__u8		dst_gid[RDS_IB_GID_LEN];
+
+	__u32		max_send_wr;
+	__u32		max_recv_wr;
+	__u32		max_send_sge;
+	__u32		rdma_mr_max;
+	__u32		rdma_mr_size;
+};
+
 /* RDS message Receive Path Latency points */
 enum rds_message_rxpath_latency {
 	RDS_MSG_RX_HDR_TO_DGRAM_START = 0,
diff --git a/net/rds/connection.c b/net/rds/connection.c
index 8c5d093..7151527 100644
--- a/net/rds/connection.c
+++ b/net/rds/connection.c
@@ -488,15 +488,19 @@ void rds_conn_destroy(struct rds_connection *conn)
 
 static void __rds_inc_msg_cp(struct rds_incoming *inc,
 			     struct rds_info_iterator *iter,
-			     void *saddr, void *daddr, int flip)
+			     void *saddr, void *daddr, int flip, bool isv6)
 {
-	rds_inc_info_copy(inc, iter, *(__be32 *)saddr, *(__be32 *)daddr, flip);
+	if (isv6)
+		rds6_inc_info_copy(inc, iter, saddr, daddr, flip);
+	else
+		rds_inc_info_copy(inc, iter, *(__be32 *)saddr,
+				  *(__be32 *)daddr, flip);
 }
 
 static void rds_conn_message_info_cmn(struct socket *sock, unsigned int len,
 				      struct rds_info_iterator *iter,
 				      struct rds_info_lengths *lens,
-				      int want_send)
+				      int want_send, bool isv6)
 {
 	struct hlist_head *head;
 	struct list_head *list;
@@ -507,7 +511,10 @@ static void rds_conn_message_info_cmn(struct socket *sock, unsigned int len,
 	size_t i;
 	int j;
 
-	len /= sizeof(struct rds_info_message);
+	if (isv6)
+		len /= sizeof(struct rds6_info_message);
+	else
+		len /= sizeof(struct rds_info_message);
 
 	rcu_read_lock();
 
@@ -517,6 +524,9 @@ static void rds_conn_message_info_cmn(struct socket *sock, unsigned int len,
 			struct rds_conn_path *cp;
 			int npaths;
 
+			if (!isv6 && conn->c_isv6)
+				continue;
+
 			npaths = (conn->c_trans->t_mp_capable ?
 				 RDS_MPATH_WORKERS : 1);
 
@@ -537,7 +547,7 @@ static void rds_conn_message_info_cmn(struct socket *sock, unsigned int len,
 								 iter,
 								 &conn->c_laddr,
 								 &conn->c_faddr,
-								 0);
+								 0, isv6);
 				}
 
 				spin_unlock_irqrestore(&cp->cp_lock, flags);
@@ -547,7 +557,10 @@ static void rds_conn_message_info_cmn(struct socket *sock, unsigned int len,
 	rcu_read_unlock();
 
 	lens->nr = total;
-	lens->each = sizeof(struct rds_info_message);
+	if (isv6)
+		lens->each = sizeof(struct rds6_info_message);
+	else
+		lens->each = sizeof(struct rds_info_message);
 }
 
 static void rds_conn_message_info(struct socket *sock, unsigned int len,
@@ -555,7 +568,15 @@ static void rds_conn_message_info(struct socket *sock, unsigned int len,
 				  struct rds_info_lengths *lens,
 				  int want_send)
 {
-	rds_conn_message_info_cmn(sock, len, iter, lens, want_send);
+	rds_conn_message_info_cmn(sock, len, iter, lens, want_send, false);
+}
+
+static void rds6_conn_message_info(struct socket *sock, unsigned int len,
+				   struct rds_info_iterator *iter,
+				   struct rds_info_lengths *lens,
+				   int want_send)
+{
+	rds_conn_message_info_cmn(sock, len, iter, lens, want_send, true);
 }
 
 static void rds_conn_message_info_send(struct socket *sock, unsigned int len,
@@ -565,6 +586,13 @@ static void rds_conn_message_info_send(struct socket *sock, unsigned int len,
 	rds_conn_message_info(sock, len, iter, lens, 1);
 }
 
+static void rds6_conn_message_info_send(struct socket *sock, unsigned int len,
+					struct rds_info_iterator *iter,
+					struct rds_info_lengths *lens)
+{
+	rds6_conn_message_info(sock, len, iter, lens, 1);
+}
+
 static void rds_conn_message_info_retrans(struct socket *sock,
 					  unsigned int len,
 					  struct rds_info_iterator *iter,
@@ -573,6 +601,14 @@ static void rds_conn_message_info_retrans(struct socket *sock,
 	rds_conn_message_info(sock, len, iter, lens, 0);
 }
 
+static void rds6_conn_message_info_retrans(struct socket *sock,
+					   unsigned int len,
+					   struct rds_info_iterator *iter,
+					   struct rds_info_lengths *lens)
+{
+	rds6_conn_message_info(sock, len, iter, lens, 0);
+}
+
 void rds_for_each_conn_info(struct socket *sock, unsigned int len,
 			  struct rds_info_iterator *iter,
 			  struct rds_info_lengths *lens,
@@ -688,6 +724,34 @@ static int rds_conn_info_visitor(struct rds_conn_path *cp, void *buffer)
 	return 1;
 }
 
+static int rds6_conn_info_visitor(struct rds_conn_path *cp, void *buffer)
+{
+	struct rds6_info_connection *cinfo6 = buffer;
+	struct rds_connection *conn = cp->cp_conn;
+
+	cinfo6->next_tx_seq = cp->cp_next_tx_seq;
+	cinfo6->next_rx_seq = cp->cp_next_rx_seq;
+	cinfo6->laddr = conn->c_laddr;
+	cinfo6->faddr = conn->c_faddr;
+	strncpy(cinfo6->transport, conn->c_trans->t_name,
+		sizeof(cinfo6->transport));
+	cinfo6->flags = 0;
+
+	rds_conn_info_set(cinfo6->flags, test_bit(RDS_IN_XMIT, &cp->cp_flags),
+			  SENDING);
+	/* XXX Future: return the state rather than these funky bits */
+	rds_conn_info_set(cinfo6->flags,
+			  atomic_read(&cp->cp_state) == RDS_CONN_CONNECTING,
+			  CONNECTING);
+	rds_conn_info_set(cinfo6->flags,
+			  atomic_read(&cp->cp_state) == RDS_CONN_UP,
+			  CONNECTED);
+	/* Just return 1 as there is no error case. This is a helper function
+	 * for rds_walk_conn_path_info() and it wants a return value.
+	 */
+	return 1;
+}
+
 static void rds_conn_info(struct socket *sock, unsigned int len,
 			  struct rds_info_iterator *iter,
 			  struct rds_info_lengths *lens)
@@ -700,6 +764,18 @@ static void rds_conn_info(struct socket *sock, unsigned int len,
 				sizeof(struct rds_info_connection));
 }
 
+static void rds6_conn_info(struct socket *sock, unsigned int len,
+			   struct rds_info_iterator *iter,
+			   struct rds_info_lengths *lens)
+{
+	u64 buffer[(sizeof(struct rds6_info_connection) + 7) / 8];
+
+	rds_walk_conn_path_info(sock, len, iter, lens,
+				rds6_conn_info_visitor,
+				buffer,
+				sizeof(struct rds6_info_connection));
+}
+
 int rds_conn_init(void)
 {
 	rds_conn_slab = kmem_cache_create("rds_connection",
@@ -713,6 +789,11 @@ int rds_conn_init(void)
 			       rds_conn_message_info_send);
 	rds_info_register_func(RDS_INFO_RETRANS_MESSAGES,
 			       rds_conn_message_info_retrans);
+	rds_info_register_func(RDS6_INFO_CONNECTIONS, rds6_conn_info);
+	rds_info_register_func(RDS6_INFO_SEND_MESSAGES,
+			       rds6_conn_message_info_send);
+	rds_info_register_func(RDS6_INFO_RETRANS_MESSAGES,
+			       rds6_conn_message_info_retrans);
 
 	return 0;
 }
@@ -730,6 +811,11 @@ void rds_conn_exit(void)
 				 rds_conn_message_info_send);
 	rds_info_deregister_func(RDS_INFO_RETRANS_MESSAGES,
 				 rds_conn_message_info_retrans);
+	rds_info_deregister_func(RDS6_INFO_CONNECTIONS, rds6_conn_info);
+	rds_info_deregister_func(RDS6_INFO_SEND_MESSAGES,
+				 rds6_conn_message_info_send);
+	rds_info_deregister_func(RDS6_INFO_RETRANS_MESSAGES,
+				 rds6_conn_message_info_retrans);
 }
 
 /*
diff --git a/net/rds/ib.c b/net/rds/ib.c
index 756225c..63d95ea 100644
--- a/net/rds/ib.c
+++ b/net/rds/ib.c
@@ -321,6 +321,43 @@ static int rds_ib_conn_info_visitor(struct rds_connection *conn,
 	return 1;
 }
 
+/* IPv6 version of rds_ib_conn_info_visitor(). */
+static int rds6_ib_conn_info_visitor(struct rds_connection *conn,
+				     void *buffer)
+{
+	struct rds6_info_rdma_connection *iinfo6 = buffer;
+	struct rds_ib_connection *ic;
+
+	/* We will only ever look at IB transports */
+	if (conn->c_trans != &rds_ib_transport)
+		return 0;
+
+	iinfo6->src_addr = conn->c_laddr;
+	iinfo6->dst_addr = conn->c_faddr;
+
+	memset(&iinfo6->src_gid, 0, sizeof(iinfo6->src_gid));
+	memset(&iinfo6->dst_gid, 0, sizeof(iinfo6->dst_gid));
+
+	if (rds_conn_state(conn) == RDS_CONN_UP) {
+		struct rds_ib_device *rds_ibdev;
+		struct rdma_dev_addr *dev_addr;
+
+		ic = conn->c_transport_data;
+		dev_addr = &ic->i_cm_id->route.addr.dev_addr;
+		rdma_addr_get_sgid(dev_addr,
+				   (union ib_gid *)&iinfo6->src_gid);
+		rdma_addr_get_dgid(dev_addr,
+				   (union ib_gid *)&iinfo6->dst_gid);
+
+		rds_ibdev = ic->rds_ibdev;
+		iinfo6->max_send_wr = ic->i_send_ring.w_nr;
+		iinfo6->max_recv_wr = ic->i_recv_ring.w_nr;
+		iinfo6->max_send_sge = rds_ibdev->max_sge;
+		rds6_ib_get_mr_info(rds_ibdev, iinfo6);
+	}
+	return 1;
+}
+
 static void rds_ib_ic_info(struct socket *sock, unsigned int len,
 			   struct rds_info_iterator *iter,
 			   struct rds_info_lengths *lens)
@@ -333,6 +370,19 @@ static void rds_ib_ic_info(struct socket *sock, unsigned int len,
 				sizeof(struct rds_info_rdma_connection));
 }
 
+/* IPv6 version of rds_ib_ic_info(). */
+static void rds6_ib_ic_info(struct socket *sock, unsigned int len,
+			    struct rds_info_iterator *iter,
+			    struct rds_info_lengths *lens)
+{
+	u64 buffer[(sizeof(struct rds6_info_rdma_connection) + 7) / 8];
+
+	rds_for_each_conn_info(sock, len, iter, lens,
+			       rds6_ib_conn_info_visitor,
+			       buffer,
+			       sizeof(struct rds6_info_rdma_connection));
+}
+
 /*
  * Early RDS/IB was built to only bind to an address if there is an IPoIB
  * device with that address set.
@@ -441,6 +491,7 @@ void rds_ib_exit(void)
 	rds_ib_set_unloading();
 	synchronize_rcu();
 	rds_info_deregister_func(RDS_INFO_IB_CONNECTIONS, rds_ib_ic_info);
+	rds_info_deregister_func(RDS6_INFO_IB_CONNECTIONS, rds6_ib_ic_info);
 	rds_ib_unregister_client();
 	rds_ib_destroy_nodev_conns();
 	rds_ib_sysctl_exit();
@@ -502,6 +553,7 @@ int rds_ib_init(void)
 	rds_trans_register(&rds_ib_transport);
 
 	rds_info_register_func(RDS_INFO_IB_CONNECTIONS, rds_ib_ic_info);
+	rds_info_register_func(RDS6_INFO_IB_CONNECTIONS, rds6_ib_ic_info);
 
 	goto out;
 
diff --git a/net/rds/ib_mr.h b/net/rds/ib_mr.h
index 0ea4ab0..f440ace 100644
--- a/net/rds/ib_mr.h
+++ b/net/rds/ib_mr.h
@@ -113,6 +113,8 @@ struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_dev,
 					     int npages);
 void rds_ib_get_mr_info(struct rds_ib_device *rds_ibdev,
 			struct rds_info_rdma_connection *iinfo);
+void rds6_ib_get_mr_info(struct rds_ib_device *rds_ibdev,
+			 struct rds6_info_rdma_connection *iinfo6);
 void rds_ib_destroy_mr_pool(struct rds_ib_mr_pool *);
 void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents,
 		    struct rds_sock *rs, u32 *key_ret);
diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c
index 0ec9df0..e3c8bbb 100644
--- a/net/rds/ib_rdma.c
+++ b/net/rds/ib_rdma.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -180,6 +180,15 @@ void rds_ib_get_mr_info(struct rds_ib_device *rds_ibdev, struct rds_info_rdma_co
 	iinfo->rdma_mr_size = pool_1m->fmr_attr.max_pages;
 }
 
+void rds6_ib_get_mr_info(struct rds_ib_device *rds_ibdev,
+			 struct rds6_info_rdma_connection *iinfo6)
+{
+	struct rds_ib_mr_pool *pool_1m = rds_ibdev->mr_1m_pool;
+
+	iinfo6->rdma_mr_max = pool_1m->max_items;
+	iinfo6->rdma_mr_size = pool_1m->fmr_attr.max_pages;
+}
+
 struct rds_ib_mr *rds_ib_reuse_mr(struct rds_ib_mr_pool *pool)
 {
 	struct rds_ib_mr *ibmr = NULL;
diff --git a/net/rds/rds.h b/net/rds/rds.h
index f5f99d1..fd63b86 100644
--- a/net/rds/rds.h
+++ b/net/rds/rds.h
@@ -875,6 +875,10 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
 void rds_inc_info_copy(struct rds_incoming *inc,
 		       struct rds_info_iterator *iter,
 		       __be32 saddr, __be32 daddr, int flip);
+void rds6_inc_info_copy(struct rds_incoming *inc,
+			struct rds_info_iterator *iter,
+			struct in6_addr *saddr, struct in6_addr *daddr,
+			int flip);
 
 /* send.c */
 int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len);
diff --git a/net/rds/recv.c b/net/rds/recv.c
index 4217961..10a2d0f 100644
--- a/net/rds/recv.c
+++ b/net/rds/recv.c
@@ -792,3 +792,28 @@ void rds_inc_info_copy(struct rds_incoming *inc,
 
 	rds_info_copy(iter, &minfo, sizeof(minfo));
 }
+
+void rds6_inc_info_copy(struct rds_incoming *inc,
+			struct rds_info_iterator *iter,
+			struct in6_addr *saddr, struct in6_addr *daddr,
+			int flip)
+{
+	struct rds6_info_message minfo6;
+
+	minfo6.seq = be64_to_cpu(inc->i_hdr.h_sequence);
+	minfo6.len = be32_to_cpu(inc->i_hdr.h_len);
+
+	if (flip) {
+		minfo6.laddr = *daddr;
+		minfo6.faddr = *saddr;
+		minfo6.lport = inc->i_hdr.h_dport;
+		minfo6.fport = inc->i_hdr.h_sport;
+	} else {
+		minfo6.laddr = *saddr;
+		minfo6.faddr = *daddr;
+		minfo6.lport = inc->i_hdr.h_sport;
+		minfo6.fport = inc->i_hdr.h_dport;
+	}
+
+	rds_info_copy(iter, &minfo6, sizeof(minfo6));
+}
diff --git a/net/rds/tcp.c b/net/rds/tcp.c
index 890d0e1..7028d6e 100644
--- a/net/rds/tcp.c
+++ b/net/rds/tcp.c
@@ -273,6 +273,48 @@ static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len,
 	spin_unlock_irqrestore(&rds_tcp_tc_list_lock, flags);
 }
 
+/* Handle RDS6_INFO_TCP_SOCKETS socket option. It returns both IPv4 and
+ * IPv6 connections. IPv4 connection address is returned in an IPv4 mapped
+ * address.
+ */
+static void rds6_tcp_tc_info(struct socket *sock, unsigned int len,
+			     struct rds_info_iterator *iter,
+			     struct rds_info_lengths *lens)
+{
+	struct rds6_info_tcp_socket tsinfo6;
+	struct rds_tcp_connection *tc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rds_tcp_tc_list_lock, flags);
+
+	if (len / sizeof(tsinfo6) < rds6_tcp_tc_count)
+		goto out;
+
+	list_for_each_entry(tc, &rds_tcp_tc_list, t_list_item) {
+		struct sock *sk = tc->t_sock->sk;
+		struct inet_sock *inet = inet_sk(sk);
+
+		tsinfo6.local_addr = sk->sk_v6_rcv_saddr;
+		tsinfo6.local_port = inet->inet_sport;
+		tsinfo6.peer_addr = sk->sk_v6_daddr;
+		tsinfo6.peer_port = inet->inet_dport;
+
+		tsinfo6.hdr_rem = tc->t_tinc_hdr_rem;
+		tsinfo6.data_rem = tc->t_tinc_data_rem;
+		tsinfo6.last_sent_nxt = tc->t_last_sent_nxt;
+		tsinfo6.last_expected_una = tc->t_last_expected_una;
+		tsinfo6.last_seen_una = tc->t_last_seen_una;
+
+		rds_info_copy(iter, &tsinfo6, sizeof(tsinfo6));
+	}
+
+out:
+	lens->nr = rds6_tcp_tc_count;
+	lens->each = sizeof(tsinfo6);
+
+	spin_unlock_irqrestore(&rds_tcp_tc_list_lock, flags);
+}
+
 static int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
 			       __u32 scope_id)
 {
@@ -628,6 +670,7 @@ static void rds_tcp_exit(void)
 	rds_tcp_set_unloading();
 	synchronize_rcu();
 	rds_info_deregister_func(RDS_INFO_TCP_SOCKETS, rds_tcp_tc_info);
+	rds_info_deregister_func(RDS6_INFO_TCP_SOCKETS, rds6_tcp_tc_info);
 	unregister_pernet_device(&rds_tcp_net_ops);
 	rds_tcp_destroy_conns();
 	rds_trans_unregister(&rds_tcp_transport);
@@ -659,6 +702,7 @@ static int rds_tcp_init(void)
 	rds_trans_register(&rds_tcp_transport);
 
 	rds_info_register_func(RDS_INFO_TCP_SOCKETS, rds_tcp_tc_info);
+	rds_info_register_func(RDS6_INFO_TCP_SOCKETS, rds6_tcp_tc_info);
 
 	goto out;
 out_recv:
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr
  2018-06-27 10:23 ` [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr Ka-Cheong Poon
@ 2018-06-30  8:50   ` David Miller
  2018-07-03 14:02     ` Ka-Cheong Poon
  2018-07-05 17:58   ` Santosh Shilimkar
  1 sibling, 1 reply; 18+ messages in thread
From: David Miller @ 2018-06-30  8:50 UTC (permalink / raw)
  To: ka-cheong.poon; +Cc: netdev, santosh.shilimkar, rds-devel

From: Ka-Cheong Poon <ka-cheong.poon@oracle.com>
Date: Wed, 27 Jun 2018 03:23:27 -0700

> This patch changes the internal representation of an IP address to use
> struct in6_addr.  IPv4 address is stored as an IPv4 mapped address.
> All the functions which take an IP address as argument are also
> changed to use struct in6_addr.  But RDS socket layer is not modified
> such that it still does not accept IPv6 address from an application.
> And RDS layer does not accept nor initiate IPv6 connections.
> 
> v2: Fixed sparse warnings.
> 
> Signed-off-by: Ka-Cheong Poon <ka-cheong.poon@oracle.com>

I really don't like this.

An ipv4 mapped ipv6 address is not the same as an ipv4 address.

You are effectively preventing the use of ipv6 connections
using ipv4 mapped addresses.

Also, assuming the sockaddr type based upon size is wrong.
You have to check the family field, then you can decide
to interpret the rest of the sockaddr in one way or another
and also validate it's length.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr
  2018-06-30  8:50   ` David Miller
@ 2018-07-03 14:02     ` Ka-Cheong Poon
  0 siblings, 0 replies; 18+ messages in thread
From: Ka-Cheong Poon @ 2018-07-03 14:02 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, santosh.shilimkar, rds-devel

On 06/30/2018 04:50 PM, David Miller wrote:
> From: Ka-Cheong Poon <ka-cheong.poon@oracle.com>
> Date: Wed, 27 Jun 2018 03:23:27 -0700
> 
>> This patch changes the internal representation of an IP address to use
>> struct in6_addr.  IPv4 address is stored as an IPv4 mapped address.
>> All the functions which take an IP address as argument are also
>> changed to use struct in6_addr.  But RDS socket layer is not modified
>> such that it still does not accept IPv6 address from an application.
>> And RDS layer does not accept nor initiate IPv6 connections.
>>
>> v2: Fixed sparse warnings.
>>
>> Signed-off-by: Ka-Cheong Poon <ka-cheong.poon@oracle.com>
> 
> I really don't like this.
> 
> An ipv4 mapped ipv6 address is not the same as an ipv4 address.
> 
> You are effectively preventing the use of ipv6 connections
> using ipv4 mapped addresses.


Could you please clarify what is meant by an IPv6
connections using IPv4 mapped address?  An IPv6 packet
cannot use an IPv4 mapped address as source or destination
address.  Do you mean an app uses an IPv4 mapped address
in a struct sockaddr_in6 to set up an IPv4 connection?

Please note that this patch is patch #1.  This patch
alone does not support RDS/IPv6.  Hence this patch has
checks to prevent an app to use IPv6 address.  Those
checks will be removed in patch #2.


> Also, assuming the sockaddr type based upon size is wrong.
> You have to check the family field, then you can decide
> to interpret the rest of the sockaddr in one way or another
> and also validate it's length.
> 


-- 
K. Poon
ka-cheong.poon@oracle.com

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 0/3] rds: IPv6 support
  2018-06-27 10:23 [PATCH v2 net-next 0/3] rds: IPv6 support Ka-Cheong Poon
                   ` (2 preceding siblings ...)
  2018-06-27 10:23 ` [PATCH v2 net-next 3/3] rds: Extend RDS API for " Ka-Cheong Poon
@ 2018-07-05 17:44 ` Sowmini Varadhan
  2018-07-06 14:36   ` Ka-Cheong Poon
  3 siblings, 1 reply; 18+ messages in thread
From: Sowmini Varadhan @ 2018-07-05 17:44 UTC (permalink / raw)
  To: Ka-Cheong Poon; +Cc: netdev, santosh.shilimkar, davem, rds-devel


Some additional comments on this patchset (consolidated here, 
please tease this apart into patch1/patch2/patch3 as appropriate)

I looked at the most of rds-core, and the rds-tcp changes.
Please make sure santosh looks at these carefully, especially.
    - RDS bind key  changes
    - connection.c
    - all the rds_rdma related changes (e.g., the ib* and rdma* files)

- rds_getname(): one of the existing properties of PF_RDS is that a
  connect() does not involve an implicit bind(). Note that you are basing
  the changes in rds_bind() on this behavior, thus I guess we make the
  choice of treating this as a feature, not a bug.

  Since we are choosing to treat this behavior as a feature we
  need to be consistent in rds_getname(). If we find
  (!peer and !ipv6_addr_any(rs_conn_addr)) and the socket is not yet bound, 
  the returned address (address size, address family) should be based
  on the rs_conn_addr. (Otherwise if I connect to abc::1 without doing a 
  bind(), and try to do a getsockname(), I get back a sockaddr_in?!)

- rds_cancel_sent_to() and rds_connect and rds_bind and rds_sendmsg
  As DaveM has already pointed out, we should be using sa_family to figure
  out sockaddr_in vs sockaddr_in6 (not the other way around of inspecting
  len first, and then the family- that way wont work if I pass in
  sockaddr_storage).  E.g., see inet_dgram_connect.

        if (addr_len < sizeof(uaddr->sa_family))
                return -EINVAL;

- In net/rds/rds.h;

  /* The following ports, 16385, 18634, 18635, are registered with IANA as
   * the ports to be used for RDS over TCP and UDP.  18634 is the historical

  What is "RDS over TCP and UDP"? There is no such thing as RDS-over-UDP. 
  IN fact RDS has nothing to do with UDP. The comment is confused. See next
  item below, where the comment disappears.
  
- Also in net/rds/rds.h
  Please dont define transport specific parameters like RD_CM_PORT in the 
  common rds.h header. It is unfortunate that we already have RDS_PORT there,
  and we should try to clean that up as well. NOte that RDS_TCP_PORT 
  is now in the correct transport-module-specific header (net/rds/tcp.h)
  and its unclean to drag it from there, into the common header as you are 
  doing.

  In fact I just tried to move the RDS_PORT definition into 
  net/rds/rdma_transport.h and it built just-fine. So to summarize,
  please do the following:
  1. move RDS_PORT into rdma_transport.h
  2. add RDS_CM_PORT into rdma_transport.h
  3. stop dragging RDS_TCP_PORT from its current happy home in net/rds/tcp.h
     to net/rds/rds.h

- net/rds/connection.c
  As we have discussed offline before, the fact that we cannot report
  TCP seq# etc info via the existing rds-info API is not "a bug in the
  design of MPRDS" but rather a lacking in the API design. Moreover,
  much of the useful information around the TCP socket is already
  available via procfs, TCP_INFO etc, so the info by rds-info is rarely
  used for rds-tcp- the more useful information is around the RDS socket
  itself.  So there is a bug in the comment, would be nice if you removed it.

  Also, while you are there, s/exisiting/existing, please.

General comments:
-----------------
I remain unconvinced by your global <-> link-local arguments.

For UDP sockets we can do this:

         eth0
   host1 -------------------------------------- host2
         abc::1/64                       fe80::2
                                         add abc::/64 as onlink subnet route


  host1# traceroute6 -i eth0 -s abc::1 fe80::2

You just broke this for RDS and are using polemic to defend your case,
but the main thrust of your diatribe seems to be "why would you need 
this?" I'll try to address that briefly here. 

- There may be lot of valid reasons why host2 does not want to be 
  configured with a global prefix. e.g., I only want host2 to be able 
  to talk to onlink hosts.

- RDS mandatorily requires sockets to be bound. So the normal src addr
  selection (that would have caused host1 to use a link-local to talk
  to host2) is suppressed in this case 

  This is exactly the same as a UDP socket bound to abc::1

  Note well, that one of the use-cases for RDS-TCP is to replace 
  existing infra that uses UDP for cluster-IPC. This has come up before
  on netdev:

  See https://www.mail-archive.com/search?l=netdev@vger.kernel.org&q=subject:%22Re%5C%3A+%5C%5BPATCH+net%5C-next+0%5C%2F6%5C%5D+kcm%5C%3A+Kernel+Connection+Multiplexor+%5C%28KCM%5C%29%22&o=newest&f=1

  so feature parity with udp is just as important as feature-parity
  for rds_rdma. 

  I hope that helps you see why we need to not break this gratuituously
  for rds-tcp.

BTW, etiquette is to cc folks who have offered review comments on the
code. Please make sure to cc me in follow-ups to this thread.

Thank you.

--Sowmini

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr
  2018-06-27 10:23 ` [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr Ka-Cheong Poon
  2018-06-30  8:50   ` David Miller
@ 2018-07-05 17:58   ` Santosh Shilimkar
  2018-07-06  9:08     ` Ka-Cheong Poon
  1 sibling, 1 reply; 18+ messages in thread
From: Santosh Shilimkar @ 2018-07-05 17:58 UTC (permalink / raw)
  To: Ka-Cheong Poon, netdev; +Cc: davem, rds-devel

On 6/27/2018 3:23 AM, Ka-Cheong Poon wrote:
> This patch changes the internal representation of an IP address to use
> struct in6_addr.  IPv4 address is stored as an IPv4 mapped address.
> All the functions which take an IP address as argument are also
> changed to use struct in6_addr.  But RDS socket layer is not modified
> such that it still does not accept IPv6 address from an application.
> And RDS layer does not accept nor initiate IPv6 connections.
> 
> v2: Fixed sparse warnings.
> 
> Signed-off-by: Ka-Cheong Poon <ka-cheong.poon@oracle.com>
> ---
>   net/rds/af_rds.c         | 138 ++++++++++++++++------
>   net/rds/bind.c           |  91 +++++++++-----
>   net/rds/cong.c           |  23 ++--
>   net/rds/connection.c     | 132 +++++++++++++--------
>   net/rds/ib.c             |  17 +--
>   net/rds/ib.h             |  45 +++++--
>   net/rds/ib_cm.c          | 300 +++++++++++++++++++++++++++++++++++------------
>   net/rds/ib_rdma.c        |  15 +--
>   net/rds/ib_recv.c        |  18 +--
>   net/rds/ib_send.c        |  10 +-
>   net/rds/loop.c           |   7 +-
>   net/rds/rdma.c           |   6 +-
>   net/rds/rdma_transport.c |  56 ++++++---
>   net/rds/rds.h            |  69 +++++++----
>   net/rds/recv.c           |  51 +++++---
>   net/rds/send.c           |  67 ++++++++---
>   net/rds/tcp.c            |  32 ++++-
>   net/rds/tcp_connect.c    |  34 +++---
>   net/rds/tcp_listen.c     |  18 +--
>   net/rds/tcp_recv.c       |   9 +-
>   net/rds/tcp_send.c       |   4 +-
>   net/rds/threads.c        |  69 +++++++++--
>   net/rds/transport.c      |  15 ++-
>   23 files changed, 857 insertions(+), 369 deletions(-)
> 

> diff --git a/net/rds/bind.c b/net/rds/bind.c
> index 5aa3a64..3822886 100644
> --- a/net/rds/bind.c
> +++ b/net/rds/bind.c
> @@ -1,5 +1,5 @@
>   /*
> - * Copyright (c) 2006 Oracle.  All rights reserved.
> + * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
>    *
>    * This software is available to you under a choice of one of two
>    * licenses.  You may choose to be licensed under the terms of the GNU
> @@ -33,6 +33,7 @@
>   #include <linux/kernel.h>
>   #include <net/sock.h>
>   #include <linux/in.h>
> +#include <linux/ipv6.h>
>   #include <linux/if_arp.h>
>   #include <linux/jhash.h>
>   #include <linux/ratelimit.h>
> @@ -42,42 +43,58 @@
>   
>   static const struct rhashtable_params ht_parms = {
>   	.nelem_hint = 768,
> -	.key_len = sizeof(u64),
> +	.key_len = RDS_BOUND_KEY_LEN,
Do we really need the scope id to be part of the key ? With link
local/global, do you see any collisions. Please educate me
on the actual usecase. This can avoid bunch of changes and hence
the question.

> @@ -114,7 +132,7 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port)
>   			  rs, &addr, (int)ntohs(*port));
>   			break;
>   		} else {
> -			rs->rs_bound_addr = 0;
> +			rs->rs_bound_addr = in6addr_any;
Can you elaborate why 0 is not ok ?

>   			rds_sock_put(rs);
>   			ret = -ENOMEM;
>   			break;
> @@ -127,44 +145,61 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port)
>   void rds_remove_bound(struct rds_sock *rs)
>   {
>   
> -	if (!rs->rs_bound_addr)
> +	if (ipv6_addr_any(&rs->rs_bound_addr))
>   		return;
>   
> -	rdsdebug("rs %p unbinding from %pI4:%d\n",
> +	rdsdebug("rs %p unbinding from %pI6c:%d\n",
>   		 rs, &rs->rs_bound_addr,
>   		 ntohs(rs->rs_bound_port));
>   
>   	rhashtable_remove_fast(&bind_hash_table, &rs->rs_bound_node, ht_parms);
>   	rds_sock_put(rs);
> -	rs->rs_bound_addr = 0;
> +	rs->rs_bound_addr = in6addr_any;
>   }
>   
>   int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
>   {
>   	struct sock *sk = sock->sk;
> -	struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
>   	struct rds_sock *rs = rds_sk_to_rs(sk);
> +	struct in6_addr v6addr, *binding_addr;
>   	struct rds_transport *trans;
> +	__u32 scope_id = 0;
>   	int ret = 0;
> +	__be16 port;
>   
> +	/* We only allow an RDS socket to be bound to and IPv4 address. IPv6
s/'bound to and IPv4'/'bound to an IPv4'
> +	 * address support will be added later.
> +	 */
> +	if (addr_len == sizeof(struct sockaddr_in)) {
> +		struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
> +
> +		if (sin->sin_family != AF_INET ||
> +		    sin->sin_addr.s_addr == htonl(INADDR_ANY))
> +			return -EINVAL;
> +		ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &v6addr);
> +		binding_addr = &v6addr;
> +		port = sin->sin_port;
> +	} else if (addr_len == sizeof(struct sockaddr_in6)) {
> +		return -EPROTONOSUPPORT;
> +	} else {
> +		return -EINVAL;
> +	}
>   	lock_sock(sk);
>   
> -	if (addr_len != sizeof(struct sockaddr_in) ||
> -	    sin->sin_family != AF_INET ||
> -	    rs->rs_bound_addr ||
> -	    sin->sin_addr.s_addr == htonl(INADDR_ANY)) {
> +	/* RDS socket does not allow re-binding. */
> +	if (!ipv6_addr_any(&rs->rs_bound_addr)) {
>   		ret = -EINVAL;
>   		goto out;
>   	}
Seems new behavior you are adding. The statement itself is
true but if we return silently for already bind address, its
ok. May be am missing something above.


> diff --git a/net/rds/connection.c b/net/rds/connection.c
> index abef75d..ca72563 100644
> --- a/net/rds/connection.c
> +++ b/net/rds/connection.c

> @@ -142,9 +151,12 @@ static void __rds_conn_path_init(struct rds_connection *conn,
>    * are torn down as the module is removed, if ever.
>    */
>   static struct rds_connection *__rds_conn_create(struct net *net,
> -						__be32 laddr, __be32 faddr,
> -				       struct rds_transport *trans, gfp_t gfp,
> -				       int is_outgoing)
> +						const struct in6_addr *laddr,
> +						const struct in6_addr *faddr,
> +						struct rds_transport *trans,
> +						gfp_t gfp,
> +						int is_outgoing,
> +						int dev_if)
Am just wondering if we can handle local link address case differently
instead of pushing the interface index everywhere. Did you think about
any alternative. This can also avoid bunch of changes again and hence
the question. Am trying to see if we can minimize the changes to
absolute must have to support IPv6.


> diff --git a/net/rds/ib.c b/net/rds/ib.c
> index b6ad38e..c712a84 100644
> --- a/net/rds/ib.c
> +++ b/net/rds/ib.c
> @@ -1,5 +1,5 @@
>   /*
> - * Copyright (c) 2006 Oracle.  All rights reserved.
> + * Copyright (c) 2006, 2017 Oracle and/or its affiliates. All rights reserved.
>    *
>    * This software is available to you under a choice of one of two
>    * licenses.  You may choose to be licensed under the terms of the GNU
> @@ -296,8 +296,8 @@ static int rds_ib_conn_info_visitor(struct rds_connection *conn,
>   	if (conn->c_trans != &rds_ib_transport)
>   		return 0;
>   
> -	iinfo->src_addr = conn->c_laddr;
> -	iinfo->dst_addr = conn->c_faddr;
> +	iinfo->src_addr = conn->c_laddr.s6_addr32[3];
> +	iinfo->dst_addr = conn->c_faddr.s6_addr32[3];
>   
>   	memset(&iinfo->src_gid, 0, sizeof(iinfo->src_gid));
>   	memset(&iinfo->dst_gid, 0, sizeof(iinfo->dst_gid));
> @@ -341,7 +341,8 @@ static void rds_ib_ic_info(struct socket *sock, unsigned int len,
>    * allowed to influence which paths have priority.  We could call userspace
>    * asserting this policy "routing".
>    */
> -static int rds_ib_laddr_check(struct net *net, __be32 addr)
> +static int rds_ib_laddr_check(struct net *net, const struct in6_addr *addr,
> +			      __u32 scope_id)
>   {
>   	int ret;
>   	struct rdma_cm_id *cm_id;
> @@ -357,7 +358,7 @@ static int rds_ib_laddr_check(struct net *net, __be32 addr)
>   
>   	memset(&sin, 0, sizeof(sin));
>   	sin.sin_family = AF_INET;
> -	sin.sin_addr.s_addr = addr;
> +	sin.sin_addr.s_addr = addr->s6_addr32[3];
>   
>   	/* rdma_bind_addr will only succeed for IB & iWARP devices */
>   	ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin);
> @@ -367,9 +368,9 @@ static int rds_ib_laddr_check(struct net *net, __be32 addr)
>   	    cm_id->device->node_type != RDMA_NODE_IB_CA)
>   		ret = -EADDRNOTAVAIL;
>   
> -	rdsdebug("addr %pI4 ret %d node type %d\n",
> -		&addr, ret,
> -		cm_id->device ? cm_id->device->node_type : -1);
> +	rdsdebug("addr %pI6c ret %d node type %d\n",
> +		 addr, ret,
> +		 cm_id->device ? cm_id->device->node_type : -1);
>   
>   	rdma_destroy_id(cm_id);
>   
> diff --git a/net/rds/ib.h b/net/rds/ib.h
> index a6f4d7d..12f96b3 100644
> --- a/net/rds/ib.h
> +++ b/net/rds/ib.h
> @@ -57,16 +57,38 @@ struct rds_ib_refill_cache {
>   	struct list_head	 *ready;
>   };
>   
> +struct rds_ib_conn_priv_cmn {
> +	u8			ricpc_protocol_major;
> +	u8			ricpc_protocol_minor;
> +	__be16			ricpc_protocol_minor_mask;	/* bitmask */
> +	__be32			ricpc_reserved1;
> +	__be64			ricpc_ack_seq;
> +	__be32			ricpc_credit;	/* non-zero enables flow ctl */
> +};
> +
>   struct rds_ib_connect_private {
>   	/* Add new fields at the end, and don't permute existing fields. */
> -	__be32			dp_saddr;
> -	__be32			dp_daddr;
> -	u8			dp_protocol_major;
> -	u8			dp_protocol_minor;
> -	__be16			dp_protocol_minor_mask; /* bitmask */
> -	__be32			dp_reserved1;
> -	__be64			dp_ack_seq;
> -	__be32			dp_credit;		/* non-zero enables flow ctl */
> +	__be32				dp_saddr;
> +	__be32				dp_daddr;
> +	struct rds_ib_conn_priv_cmn	dp_cmn;
> +};
> +
> +struct rds6_ib_connect_private {
> +	/* Add new fields at the end, and don't permute existing fields. */
> +	struct in6_addr			dp_saddr;
> +	struct in6_addr			dp_daddr;
> +	struct rds_ib_conn_priv_cmn	dp_cmn;
> +};
> +
> +#define dp_protocol_major	dp_cmn.ricpc_protocol_major
> +#define dp_protocol_minor	dp_cmn.ricpc_protocol_minor
> +#define dp_protocol_minor_mask	dp_cmn.ricpc_protocol_minor_mask
> +#define dp_ack_seq		dp_cmn.ricpc_ack_seq
> +#define dp_credit		dp_cmn.ricpc_credit
> +
> +union rds_ib_conn_priv {
> +	struct rds_ib_connect_private	ricp_v4;
> +	struct rds6_ib_connect_private	ricp_v6;
>   };
This change was invetiable. Just add a comment saying the priviate
data size for v6 vs v4 is  different. Also for IPv6 priviate data,
I suggest add some resrve fields for any future extensions so
that things can be added without breaking wire protocol. With IPv4,
we ended up in bit of mess.

> --- a/net/rds/ib_cm.c
> +++ b/net/rds/ib_cm.c
> @@ -1,5 +1,5 @@
>   /*
> - * Copyright (c) 2006 Oracle.  All rights reserved.
> + * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
>    *
>    * This software is available to you under a choice of one of two
>    * licenses.  You may choose to be licensed under the terms of the GNU
> @@ -35,10 +35,12 @@
>   #include <linux/slab.h>
>   #include <linux/vmalloc.h>
>   #include <linux/ratelimit.h>
> +#include <net/addrconf.h>
>   
>   #include "rds_single_path.h"
>   #include "rds.h"
>   #include "ib.h"
> +#include "tcp.h"
>
Hmm. Why do you need to include tcp header in ib transport
code ? If there is any common function just move to core
common file and use it.

> diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c
> index fc59821..aef73e7 100644
> --- a/net/rds/rdma_transport.c
> +++ b/net/rds/rdma_transport.c

>   
> +/* Initialize the RDS RDMA listeners.  We create two listeners for
> + * compatibility reason.  The one on RDS_PORT is used for IPv4
> + * requests only.  The one on RDS_TCP_PORT is used for IPv6 requests
> + * only.  So only IPv6 enabled RDS module will communicate using this
> + * port.
> + */
You did above ti facilitate both v4 and v6 connections to co-exist
together ? Even though potentially there is no practical usecase,
its nice to have this possibility.

> diff --git a/net/rds/rds.h b/net/rds/rds.h
> index f2272fb..859808a 100644
> --- a/net/rds/rds.h
> +++ b/net/rds/rds.h
> @@ -10,6 +10,7 @@
>   #include <linux/rds.h>
>   #include <linux/rhashtable.h>
>   #include <linux/refcount.h>
> +#include <linux/in6.h>
>   

[...]

> @@ -519,7 +522,8 @@ struct rds_transport {
>   				t_mp_capable:1;
>   	unsigned int		t_type;
>   
> -	int (*laddr_check)(struct net *net, __be32 addr);
> +	int (*laddr_check)(struct net *net, const struct in6_addr *addr,
> +			   __u32 scope_id);
I already asked somehwre above if we can avoid scope_id change.

> diff --git a/net/rds/send.c b/net/rds/send.c
> index 94c7f74..6ed2e92 100644
> --- a/net/rds/send.c
> +++ b/net/rds/send.c
> @@ -709,7 +709,7 @@ void rds_send_drop_acked(struct rds_connection *conn, u64 ack,
>   }
>   EXPORT_SYMBOL_GPL(rds_send_drop_acked);
>   
> -void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in *dest)
> +void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in6 *dest)
>   {
>   	struct rds_message *rm, *tmp;
>   	struct rds_connection *conn;
> @@ -721,8 +721,9 @@ void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in *dest)
>   	spin_lock_irqsave(&rs->rs_lock, flags);
>   
>   	list_for_each_entry_safe(rm, tmp, &rs->rs_send_queue, m_sock_item) {
> -		if (dest && (dest->sin_addr.s_addr != rm->m_daddr ||
> -			     dest->sin_port != rm->m_inc.i_hdr.h_dport))
> +		if (dest &&
> +		    (!ipv6_addr_equal(&dest->sin6_addr, &rm->m_daddr) ||
> +		     dest->sin6_port != rm->m_inc.i_hdr.h_dport))
>   			continue;
>   
>   		list_move(&rm->m_sock_item, &list);
> @@ -1059,8 +1060,8 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
>   {
>   	struct sock *sk = sock->sk;
>   	struct rds_sock *rs = rds_sk_to_rs(sk);
> +	DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name);
>   	DECLARE_SOCKADDR(struct sockaddr_in *, usin, msg->msg_name);
> -	__be32 daddr;
>   	__be16 dport;
>   	struct rds_message *rm = NULL;
>   	struct rds_connection *conn;
> @@ -1069,10 +1070,13 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
>   	int nonblock = msg->msg_flags & MSG_DONTWAIT;
>   	long timeo = sock_sndtimeo(sk, nonblock);
>   	struct rds_conn_path *cpath;
> +	struct in6_addr daddr;
> +	__u32 scope_id = 0;
>   	size_t total_payload_len = payload_len, rdma_payload_len = 0;
>   	bool zcopy = ((msg->msg_flags & MSG_ZEROCOPY) &&
>   		      sock_flag(rds_rs_to_sk(rs), SOCK_ZEROCOPY));
>   	int num_sgs = ceil(payload_len, PAGE_SIZE);
> +	int namelen;
>   
>   	/* Mirror Linux UDP mirror of BSD error message compatibility */
>   	/* XXX: Perhaps MSG_MORE someday */
> @@ -1081,27 +1085,59 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
>   		goto out;
>   	}
>   
> -	if (msg->msg_namelen) {
> -		/* XXX fail non-unicast destination IPs? */
> -		if (msg->msg_namelen < sizeof(*usin) || usin->sin_family != AF_INET) {
> +	namelen = msg->msg_namelen;
> +	if (namelen != 0) {
> +		if (namelen < sizeof(*usin)) {
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +		switch (namelen) {
> +		case sizeof(*usin):
> +			if (usin->sin_family != AF_INET ||
> +			    usin->sin_addr.s_addr == htonl(INADDR_ANY) ||
> +			    usin->sin_addr.s_addr == htonl(INADDR_BROADCAST) ||
> +			    IN_MULTICAST(ntohl(usin->sin_addr.s_addr))) {
> +				ret = -EINVAL;
The idea was to fail non-unicast IP someday but can you not add this
change as part of v6 series. We can look at it later unless you need
this change for v6. Again the question is mainly to add only necessary
check and leave the existing behavior as is so please re-check all below
checks too.


> diff --git a/net/rds/transport.c b/net/rds/transport.c
> index 0b188dd..c9788db 100644
> --- a/net/rds/transport.c
> +++ b/net/rds/transport.c

> +	if (ipv6_addr_v4mapped(addr)) {

Dave already commented on ipv6_addr_v4mapped(). Apart from
some of those comments questions, rest of the patch looks
really good and nicely done. Will also look at your
subsequent two patches and try to send you comments in next
few days.

Thanks for getting v6 support going Ka-Cheong !!

Regards,
Santosh

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr
  2018-07-05 17:58   ` Santosh Shilimkar
@ 2018-07-06  9:08     ` Ka-Cheong Poon
  2018-07-06 10:15       ` Sowmini Varadhan
  2018-07-06 14:55       ` santosh.shilimkar
  0 siblings, 2 replies; 18+ messages in thread
From: Ka-Cheong Poon @ 2018-07-06  9:08 UTC (permalink / raw)
  To: Santosh Shilimkar, netdev; +Cc: davem, rds-devel

On 07/06/2018 01:58 AM, Santosh Shilimkar wrote:

>> --- a/net/rds/bind.c
>> +++ b/net/rds/bind.c

>> @@ -42,42 +43,58 @@
>>   static const struct rhashtable_params ht_parms = {
>>       .nelem_hint = 768,
>> -    .key_len = sizeof(u64),
>> +    .key_len = RDS_BOUND_KEY_LEN,
> Do we really need the scope id to be part of the key ? With link
> local/global, do you see any collisions. Please educate me
> on the actual usecase. This can avoid bunch of changes and hence
> the question.


Yes, because link local address is not unique.  The
same address can be in two different links.  The scope
ID is used to differentiate that.


> 
>> @@ -114,7 +132,7 @@ static int rds_add_bound(struct rds_sock *rs, 
>> __be32 addr, __be16 *port)
>>                 rs, &addr, (int)ntohs(*port));
>>               break;
>>           } else {
>> -            rs->rs_bound_addr = 0;
>> +            rs->rs_bound_addr = in6addr_any;
> Can you elaborate why 0 is not ok ?


The type of rs_bound_addr is struct in6_addr.  So 0
cannot be used.


>>   int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
>>   {
>>       struct sock *sk = sock->sk;
>> -    struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
>>       struct rds_sock *rs = rds_sk_to_rs(sk);
>> +    struct in6_addr v6addr, *binding_addr;
>>       struct rds_transport *trans;
>> +    __u32 scope_id = 0;
>>       int ret = 0;
>> +    __be16 port;
>> +    /* We only allow an RDS socket to be bound to and IPv4 address. IPv6
> s/'bound to and IPv4'/'bound to an IPv4'


Changed.  But this comment will be removed in patch #2
anyway.


>> +     * address support will be added later.
>> +     */
>> +    if (addr_len == sizeof(struct sockaddr_in)) {
>> +        struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
>> +
>> +        if (sin->sin_family != AF_INET ||
>> +            sin->sin_addr.s_addr == htonl(INADDR_ANY))
>> +            return -EINVAL;
>> +        ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &v6addr);
>> +        binding_addr = &v6addr;
>> +        port = sin->sin_port;
>> +    } else if (addr_len == sizeof(struct sockaddr_in6)) {
>> +        return -EPROTONOSUPPORT;
>> +    } else {
>> +        return -EINVAL;
>> +    }
>>       lock_sock(sk);
>> -    if (addr_len != sizeof(struct sockaddr_in) ||
>> -        sin->sin_family != AF_INET ||
>> -        rs->rs_bound_addr ||
>> -        sin->sin_addr.s_addr == htonl(INADDR_ANY)) {
>> +    /* RDS socket does not allow re-binding. */
>> +    if (!ipv6_addr_any(&rs->rs_bound_addr)) {
>>           ret = -EINVAL;
>>           goto out;
>>       }
> Seems new behavior you are adding. The statement itself is
> true but if we return silently for already bind address, its
> ok. May be am missing something above.


This is the existing behavior.  The old code checks if
rs_bound_addr is non-zero or not.  If it is non-zero,
it returns an error.


>> diff --git a/net/rds/connection.c b/net/rds/connection.c
>> index abef75d..ca72563 100644
>> --- a/net/rds/connection.c
>> +++ b/net/rds/connection.c
> 
>> @@ -142,9 +151,12 @@ static void __rds_conn_path_init(struct 
>> rds_connection *conn,
>>    * are torn down as the module is removed, if ever.
>>    */
>>   static struct rds_connection *__rds_conn_create(struct net *net,
>> -                        __be32 laddr, __be32 faddr,
>> -                       struct rds_transport *trans, gfp_t gfp,
>> -                       int is_outgoing)
>> +                        const struct in6_addr *laddr,
>> +                        const struct in6_addr *faddr,
>> +                        struct rds_transport *trans,
>> +                        gfp_t gfp,
>> +                        int is_outgoing,
>> +                        int dev_if)
> Am just wondering if we can handle local link address case differently
> instead of pushing the interface index everywhere. Did you think about
> any alternative. This can also avoid bunch of changes again and hence
> the question. Am trying to see if we can minimize the changes to
> absolute must have to support IPv6.


If link local address is supported, scoped ID must be used.
Unless we remove the support of link local address, we need
to keep scope ID.


>> diff --git a/net/rds/ib.h b/net/rds/ib.h
>> index a6f4d7d..12f96b3 100644
>> --- a/net/rds/ib.h
>> +++ b/net/rds/ib.h

>> +union rds_ib_conn_priv {
>> +    struct rds_ib_connect_private    ricp_v4;
>> +    struct rds6_ib_connect_private    ricp_v6;
>>   };
> This change was invetiable. Just add a comment saying the priviate
> data size for v6 vs v4 is  different. Also for IPv6 priviate data,
> I suggest add some resrve fields for any future extensions so
> that things can be added without breaking wire protocol. With IPv4,
> we ended up in bit of mess.


Will add some comments.  Unfortunately the IB private data
exchange has only a limited space.  I don't think there is
any more space left for reserved field.  I think we should
think of a different way to handle extensions in future.


>> --- a/net/rds/ib_cm.c
>> +++ b/net/rds/ib_cm.c
>> @@ -1,5 +1,5 @@
>>   /*
>> - * Copyright (c) 2006 Oracle.  All rights reserved.
>> + * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights 
>> reserved.
>>    *
>>    * This software is available to you under a choice of one of two
>>    * licenses.  You may choose to be licensed under the terms of the GNU
>> @@ -35,10 +35,12 @@
>>   #include <linux/slab.h>
>>   #include <linux/vmalloc.h>
>>   #include <linux/ratelimit.h>
>> +#include <net/addrconf.h>
>>   #include "rds_single_path.h"
>>   #include "rds.h"
>>   #include "ib.h"
>> +#include "tcp.h"
>>
> Hmm. Why do you need to include tcp header in ib transport
> code ? If there is any common function just move to core
> common file and use it.


I think it can be removed as it is left over from earlier
changes when the IB IPv6 listener port was RDS_TCP_PORT.
Now all the port definitions are in rds.h.


>> diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c
>> index fc59821..aef73e7 100644
>> --- a/net/rds/rdma_transport.c
>> +++ b/net/rds/rdma_transport.c
> 
>> +/* Initialize the RDS RDMA listeners.  We create two listeners for
>> + * compatibility reason.  The one on RDS_PORT is used for IPv4
>> + * requests only.  The one on RDS_TCP_PORT is used for IPv6 requests
>> + * only.  So only IPv6 enabled RDS module will communicate using this
>> + * port.
>> + */
> You did above ti facilitate both v4 and v6 connections to co-exist
> together ? Even though potentially there is no practical usecase,
> its nice to have this possibility.


Yes, as there is no way to differentiate the IB private
data exchange.  So another port is used for IPv6 IB private
exchange.  Doing this allow both IPv4 and IPv6 connections
to coexist.


>> diff --git a/net/rds/send.c b/net/rds/send.c
>> index 94c7f74..6ed2e92 100644
>> --- a/net/rds/send.c
>> +++ b/net/rds/send.c

>> @@ -1081,27 +1085,59 @@ int rds_sendmsg(struct socket *sock, struct 
>> msghdr *msg, size_t payload_len)
>>           goto out;
>>       }
>> -    if (msg->msg_namelen) {
>> -        /* XXX fail non-unicast destination IPs? */
>> -        if (msg->msg_namelen < sizeof(*usin) || usin->sin_family != 
>> AF_INET) {
>> +    namelen = msg->msg_namelen;
>> +    if (namelen != 0) {
>> +        if (namelen < sizeof(*usin)) {
>> +            ret = -EINVAL;
>> +            goto out;
>> +        }
>> +        switch (namelen) {
>> +        case sizeof(*usin):
>> +            if (usin->sin_family != AF_INET ||
>> +                usin->sin_addr.s_addr == htonl(INADDR_ANY) ||
>> +                usin->sin_addr.s_addr == htonl(INADDR_BROADCAST) ||
>> +                IN_MULTICAST(ntohl(usin->sin_addr.s_addr))) {
>> +                ret = -EINVAL;
> The idea was to fail non-unicast IP someday but can you not add this
> change as part of v6 series. We can look at it later unless you need
> this change for v6. Again the question is mainly to add only necessary
> check and leave the existing behavior as is so please re-check all below
> checks too.


This is to match the same IPv6 check.  In IPv6 code, it checks
if the address is a unicast address.  I can remove the IPv4
checks but if an app does send to a multicast address, the
connection will be stuck forever.


>> diff --git a/net/rds/transport.c b/net/rds/transport.c
>> index 0b188dd..c9788db 100644
>> --- a/net/rds/transport.c
>> +++ b/net/rds/transport.c
> 
>> +    if (ipv6_addr_v4mapped(addr)) {
> 
> Dave already commented on ipv6_addr_v4mapped(). Apart from
> some of those comments questions, rest of the patch looks
> really good and nicely done. Will also look at your
> subsequent two patches and try to send you comments in next
> few days.


Do you mean using mapped address to create IPv4 connections?
I already have it in my work space.  Will be in v3.


-- 
K. Poon
ka-cheong.poon@oracle.com

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr
  2018-07-06  9:08     ` Ka-Cheong Poon
@ 2018-07-06 10:15       ` Sowmini Varadhan
  2018-07-06 15:08         ` Ka-Cheong Poon
  2018-07-06 14:55       ` santosh.shilimkar
  1 sibling, 1 reply; 18+ messages in thread
From: Sowmini Varadhan @ 2018-07-06 10:15 UTC (permalink / raw)
  To: Santosh Shilimkar, davem; +Cc: Ka-Cheong Poon, netdev, rds-devel

On (07/06/18 17:08), Ka-Cheong Poon wrote:
> >Hmm. Why do you need to include tcp header in ib transport
> >code ? If there is any common function just move to core
> >common file and use it.
> 
> I think it can be removed as it is left over from earlier
> changes when the IB IPv6 listener port was RDS_TCP_PORT.
> Now all the port definitions are in rds.h.

Transport specific information such as port numbers should not
be smeared into the common rds-module.  Please fix that.

If IB is also piggybacking on port 16385 (why?), please use your
own definitions for it in IB specific header files. 

Santosh, David, I have to NACK this if it is not changed.

--Sowmini

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 0/3] rds: IPv6 support
  2018-07-05 17:44 ` [PATCH v2 net-next 0/3] rds: " Sowmini Varadhan
@ 2018-07-06 14:36   ` Ka-Cheong Poon
  2018-07-06 15:14     ` Sowmini Varadhan
  0 siblings, 1 reply; 18+ messages in thread
From: Ka-Cheong Poon @ 2018-07-06 14:36 UTC (permalink / raw)
  To: Sowmini Varadhan; +Cc: netdev, santosh.shilimkar, davem, rds-devel

On 07/06/2018 01:44 AM, Sowmini Varadhan wrote:

> - rds_getname(): one of the existing properties of PF_RDS is that a
>    connect() does not involve an implicit bind(). Note that you are basing
>    the changes in rds_bind() on this behavior, thus I guess we make the
>    choice of treating this as a feature, not a bug.


This patch series does not change existing behavior.  But
I think this is a strange RDS semantics as it differs from
other types of socket.  But this is not about IPv6 support
and can be dealt with later.


>    Since we are choosing to treat this behavior as a feature we
>    need to be consistent in rds_getname(). If we find
>    (!peer and !ipv6_addr_any(rs_conn_addr)) and the socket is not yet bound,
>    the returned address (address size, address family) should be based
>    on the rs_conn_addr. (Otherwise if I connect to abc::1 without doing a
>    bind(), and try to do a getsockname(), I get back a sockaddr_in?!)


OK, will change that.


> - rds_cancel_sent_to() and rds_connect and rds_bind and rds_sendmsg
>    As DaveM has already pointed out, we should be using sa_family to figure
>    out sockaddr_in vs sockaddr_in6 (not the other way around of inspecting
>    len first, and then the family- that way wont work if I pass in
>    sockaddr_storage).  E.g., see inet_dgram_connect.
> 
>          if (addr_len < sizeof(uaddr->sa_family))
>                  return -EINVAL;


OK, will change that.


> - In net/rds/rds.h;
> 
>    /* The following ports, 16385, 18634, 18635, are registered with IANA as
>     * the ports to be used for RDS over TCP and UDP.  18634 is the historical
> 
>    What is "RDS over TCP and UDP"? There is no such thing as RDS-over-UDP.
>    IN fact RDS has nothing to do with UDP. The comment is confused. See next
>    item below, where the comment disappears.


As mentioned in the above comments, they are registered with IANA.
There is no RDS/UDP in Linux but the port numbers are registered
nevertheless.  And RDS can work over UDP AFAICT.  BTW, it is really
strange that RDS/TCP does not use the port number already registered.
Anyway, the above comments are just a note on this fact in the IANA
database.  The following is a link to the IANA assignment.

https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt


> - Also in net/rds/rds.h
>    Please dont define transport specific parameters like RD_CM_PORT in the
>    common rds.h header. It is unfortunate that we already have RDS_PORT there,
>    and we should try to clean that up as well. NOte that RDS_TCP_PORT
>    is now in the correct transport-module-specific header (net/rds/tcp.h)
>    and its unclean to drag it from there, into the common header as you are
>    doing.
> 
>    In fact I just tried to move the RDS_PORT definition into
>    net/rds/rdma_transport.h and it built just-fine. So to summarize,
>    please do the following:
>    1. move RDS_PORT into rdma_transport.h
>    2. add RDS_CM_PORT into rdma_transport.h
>    3. stop dragging RDS_TCP_PORT from its current happy home in net/rds/tcp.h
>       to net/rds/rds.h


IMHO, there should only be RDS_PORT in the first place.  And it
can be used for all transports.  For example, if RDS/SCTP is added,
it can also use the same port number.  There is no need to define
a new port number for each transport.  What is the rationale that
each transport needs to have its own number and be defined in its
own header file?


> - net/rds/connection.c
>    As we have discussed offline before, the fact that we cannot report
>    TCP seq# etc info via the existing rds-info API is not "a bug in the
>    design of MPRDS" but rather a lacking in the API design. Moreover,
>    much of the useful information around the TCP socket is already
>    available via procfs, TCP_INFO etc, so the info by rds-info is rarely
>    used for rds-tcp- the more useful information is around the RDS socket
>    itself.  So there is a bug in the comment, would be nice if you removed it.


If the behavior of a software component is modified/enhanced such
that the existing API of this component has problems dealing with
this new behavior, should the API be modified or a new API be added
to handle that?  If neither is done, shouldn't this be considered
a bug?


>    Also, while you are there, s/exisiting/existing, please.


OK, with change that.


> General comments:
> -----------------
> I remain unconvinced by your global <-> link-local arguments.
> 
> For UDP sockets we can do this:
> 
>           eth0
>     host1 -------------------------------------- host2
>           abc::1/64                       fe80::2
>                                           add abc::/64 as onlink subnet route
> 
> 
>    host1# traceroute6 -i eth0 -s abc::1 fe80::2
> 
> You just broke this for RDS and are using polemic to defend your case,
> but the main thrust of your diatribe seems to be "why would you need
> this?" I'll try to address that briefly here.


It is unclear why you need to use words like "polemic"
and "diatribe".  What I wrote is in public and folks can
judge whether they are personal in nature.  I'd suggest
that discussion should be done in a professional manner.


> - There may be lot of valid reasons why host2 does not want to be
>    configured with a global prefix. e.g., I only want host2 to be able
>    to talk to onlink hosts.


Right.  It can use link local address to do this.  And
peers can also use link local address to communicate with
host2.


> - RDS mandatorily requires sockets to be bound. So the normal src addr
>    selection (that would have caused host1 to use a link-local to talk
>    to host2) is suppressed in this case


This requirement may be something to be fixed in future.  For
example, one can do "rds-ping fe80::x%y" without specifying
the source address to use and it works fine.  The trick is
that rds-ping uses an UDP socket to find the source address to
use and then bind() the RDS socket to that address.  So in this
rds-ping example, it will use the correct link local address
as specified in RFC 6724 to set up the RDS connection.  This
trick really should not be needed.  But as I mentioned, this
patch series does not change existing behavior.


>    This is exactly the same as a UDP socket bound to abc::1
> 
>    Note well, that one of the use-cases for RDS-TCP is to replace
>    existing infra that uses UDP for cluster-IPC. This has come up before
>    on netdev:
> 
>    See https://www.mail-archive.com/search?l=netdev@vger.kernel.org&q=subject:%22Re%5C%3A+%5C%5BPATCH+net%5C-next+0%5C%2F6%5C%5D+kcm%5C%3A+Kernel+Connection+Multiplexor+%5C%28KCM%5C%29%22&o=newest&f=1
> 
>    so feature parity with udp is just as important as feature-parity
>    for rds_rdma.


FWIW, RDS is on top of other transport protocols.  It does not
really need to provide all services provided by the underlying
protocols.


>    I hope that helps you see why we need to not break this gratuituously
>    for rds-tcp.


Please note that I understand what you wrote.  My objection
is that it requires special set up and can lead to problems
when people actually try to do that.  RDS runs on other
platforms too.  This will frustrate users and generate calls.
And RDS apps do not really need that as link local address i
always available.  Having said this, I will add the support
to handle it to save further discussion.




-- 
K. Poon
ka-cheong.poon@oracle.com

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr
  2018-07-06  9:08     ` Ka-Cheong Poon
  2018-07-06 10:15       ` Sowmini Varadhan
@ 2018-07-06 14:55       ` santosh.shilimkar
  1 sibling, 0 replies; 18+ messages in thread
From: santosh.shilimkar @ 2018-07-06 14:55 UTC (permalink / raw)
  To: Ka-Cheong Poon, netdev; +Cc: davem, rds-devel

On 7/6/18 2:08 AM, Ka-Cheong Poon wrote:
> On 07/06/2018 01:58 AM, Santosh Shilimkar wrote:
> 

> 
>>> diff --git a/net/rds/connection.c b/net/rds/connection.c
>>> index abef75d..ca72563 100644
>>> --- a/net/rds/connection.c
>>> +++ b/net/rds/connection.c
>>
>>> @@ -142,9 +151,12 @@ static void __rds_conn_path_init(struct 
>>> rds_connection *conn,
>>>    * are torn down as the module is removed, if ever.
>>>    */
>>>   static struct rds_connection *__rds_conn_create(struct net *net,
>>> -                        __be32 laddr, __be32 faddr,
>>> -                       struct rds_transport *trans, gfp_t gfp,
>>> -                       int is_outgoing)
>>> +                        const struct in6_addr *laddr,
>>> +                        const struct in6_addr *faddr,
>>> +                        struct rds_transport *trans,
>>> +                        gfp_t gfp,
>>> +                        int is_outgoing,
>>> +                        int dev_if)
>> Am just wondering if we can handle local link address case differently
>> instead of pushing the interface index everywhere. Did you think about
>> any alternative. This can also avoid bunch of changes again and hence
>> the question. Am trying to see if we can minimize the changes to
>> absolute must have to support IPv6.
> 
> 
> If link local address is supported, scoped ID must be used.
> Unless we remove the support of link local address, we need
> to keep scope ID.
>
Ok.

> 
>>> diff --git a/net/rds/ib.h b/net/rds/ib.h
>>> index a6f4d7d..12f96b3 100644
>>> --- a/net/rds/ib.h
>>> +++ b/net/rds/ib.h
> 
>>> +union rds_ib_conn_priv {
>>> +    struct rds_ib_connect_private    ricp_v4;
>>> +    struct rds6_ib_connect_private    ricp_v6;
>>>   };
>> This change was invetiable. Just add a comment saying the priviate
>> data size for v6 vs v4 is  different. Also for IPv6 priviate data,
>> I suggest add some resrve fields for any future extensions so
>> that things can be added without breaking wire protocol. With IPv4,
>> we ended up in bit of mess.
> 
> 
> Will add some comments.  Unfortunately the IB private data
> exchange has only a limited space.  I don't think there is
> any more space left for reserved field.  I think we should
> think of a different way to handle extensions in future.
>
There is enough space. You can send upto 512 bytes iirc. Please
add some reserve fields and ping me if you run into issues.

>>> diff --git a/net/rds/send.c b/net/rds/send.c
>>> index 94c7f74..6ed2e92 100644
>>> --- a/net/rds/send.c
>>> +++ b/net/rds/send.c
> 
>>> @@ -1081,27 +1085,59 @@ int rds_sendmsg(struct socket *sock, struct 
>>> msghdr *msg, size_t payload_len)
>>>           goto out;
>>>       }
>>> -    if (msg->msg_namelen) {
>>> -        /* XXX fail non-unicast destination IPs? */
>>> -        if (msg->msg_namelen < sizeof(*usin) || usin->sin_family != 
>>> AF_INET) {
>>> +    namelen = msg->msg_namelen;
>>> +    if (namelen != 0) {
>>> +        if (namelen < sizeof(*usin)) {
>>> +            ret = -EINVAL;
>>> +            goto out;
>>> +        }
>>> +        switch (namelen) {
>>> +        case sizeof(*usin):
>>> +            if (usin->sin_family != AF_INET ||
>>> +                usin->sin_addr.s_addr == htonl(INADDR_ANY) ||
>>> +                usin->sin_addr.s_addr == htonl(INADDR_BROADCAST) ||
>>> +                IN_MULTICAST(ntohl(usin->sin_addr.s_addr))) {
>>> +                ret = -EINVAL;
>> The idea was to fail non-unicast IP someday but can you not add this
>> change as part of v6 series. We can look at it later unless you need
>> this change for v6. Again the question is mainly to add only necessary
>> check and leave the existing behavior as is so please re-check all below
>> checks too.
> 
> 
> This is to match the same IPv6 check.  In IPv6 code, it checks
> if the address is a unicast address.  I can remove the IPv4
> checks but if an app does send to a multicast address, the
> connection will be stuck forever.
>
OK. Lets keep it then.

> 
>>> diff --git a/net/rds/transport.c b/net/rds/transport.c
>>> index 0b188dd..c9788db 100644
>>> --- a/net/rds/transport.c
>>> +++ b/net/rds/transport.c
>>
>>> +    if (ipv6_addr_v4mapped(addr)) {
>>
>> Dave already commented on ipv6_addr_v4mapped(). Apart from
>> some of those comments questions, rest of the patch looks
>> really good and nicely done. Will also look at your
>> subsequent two patches and try to send you comments in next
>> few days.
> 
> 
> Do you mean using mapped address to create IPv4 connections?
> I already have it in my work space.  Will be in v3.

yeah. Thanks !!

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr
  2018-07-06 10:15       ` Sowmini Varadhan
@ 2018-07-06 15:08         ` Ka-Cheong Poon
  2018-07-06 15:25           ` Sowmini Varadhan
  0 siblings, 1 reply; 18+ messages in thread
From: Ka-Cheong Poon @ 2018-07-06 15:08 UTC (permalink / raw)
  To: Sowmini Varadhan, Santosh Shilimkar, davem; +Cc: netdev, rds-devel

On 07/06/2018 06:15 PM, Sowmini Varadhan wrote:
> On (07/06/18 17:08), Ka-Cheong Poon wrote:
>>> Hmm. Why do you need to include tcp header in ib transport
>>> code ? If there is any common function just move to core
>>> common file and use it.
>>
>> I think it can be removed as it is left over from earlier
>> changes when the IB IPv6 listener port was RDS_TCP_PORT.
>> Now all the port definitions are in rds.h.
> 
> Transport specific information such as port numbers should not
> be smeared into the common rds-module.  Please fix that.
> 
> If IB is also piggybacking on port 16385 (why?), please use your
> own definitions for it in IB specific header files.


As mentioned in a previous mail, it is unclear why the
port number is transport specific.  Most Internet services
use the same port number running over TCP/UDP as shown
in the IANA database.  And the IANA RDS registration is
the same.  What is the rationale of having a transport
specific number in the RDS implementation?


-- 
K. Poon
ka-cheong.poon@oracle.com

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 0/3] rds: IPv6 support
  2018-07-06 14:36   ` Ka-Cheong Poon
@ 2018-07-06 15:14     ` Sowmini Varadhan
  2018-07-09 12:24       ` Ka-Cheong Poon
  0 siblings, 1 reply; 18+ messages in thread
From: Sowmini Varadhan @ 2018-07-06 15:14 UTC (permalink / raw)
  To: Ka-Cheong Poon; +Cc: netdev, santosh.shilimkar, davem, rds-devel

On (07/06/18 22:36), Ka-Cheong Poon wrote:
> This patch series does not change existing behavior.  But
> I think this is a strange RDS semantics as it differs from
> other types of socket.  But this is not about IPv6 support
> and can be dealt with later.

sure, 

> >   Since we are choosing to treat this behavior as a feature we
> >   need to be consistent in rds_getname(). If we find
> >   (!peer and !ipv6_addr_any(rs_conn_addr)) and the socket is not yet bound,
> >   the returned address (address size, address family) should be based
> >   on the rs_conn_addr. (Otherwise if I connect to abc::1 without doing a
> >   bind(), and try to do a getsockname(), I get back a sockaddr_in?!)
> 
> 
> OK, will change that.
> >- rds_cancel_sent_to() and rds_connect and rds_bind and rds_sendmsg
> >   As DaveM has already pointed out, we should be using sa_family to figure
> >   out sockaddr_in vs sockaddr_in6 (not the other way around of inspecting
> >   len first, and then the family- that way wont work if I pass in
> >   sockaddr_storage).  E.g., see inet_dgram_connect.
> >
> >         if (addr_len < sizeof(uaddr->sa_family))
> >                 return -EINVAL;
> OK, will change that.

thank you

> >- In net/rds/rds.h;
> >
> >   /* The following ports, 16385, 18634, 18635, are registered with IANA as
> >    * the ports to be used for RDS over TCP and UDP.  18634 is the historical
> >
> >   What is "RDS over TCP and UDP"? There is no such thing as RDS-over-UDP.
> >   IN fact RDS has nothing to do with UDP. The comment is confused. See next
> >   item below, where the comment disappears.
> 
> 
> As mentioned in the above comments, they are registered with IANA.

16385 is registered for  RDS-TCP. 

what does it mean to run rds-tcp and rds_rdma with the CM at the same time? 
Is this possible?  (if it is, why not poach some other number like 2049
from NFS?!)

18635 is actually not used in the RDS code.
Why can you not use that for RDS_CM_PORT? 

In general, please do NOT pull up these definitions into net/rds/rds.h
They may change in the future- and the really belong in the transport
specific header file - you question this further below, see my answer
there.

> There is no RDS/UDP in Linux but the port numbers are registered
> nevertheless.  And RDS can work over UDP AFAICT.  BTW, it is really

No it cannot. RDS needs a reliable transport.  If you start using
UDP (or pigeon-carrier) for the transport you have to build the
reliability yourself.

The Oracle DB libraries either use RDS-TCP or RDS-IB or UDP (in the UDP
case they do their own congestion management in userspace, and 
history has shown that it is hard to get that right, thus the desire to
switch to rds-tcp).

Anyway, even though Andy Grover registered 18635 for "RDS-IB UDP",
that is probably the most harmless one to use for this case, because
- it is unused, 
- it calls itself rds-Infiniband,  
- the likelihood of doing rds-udp is infinitesmally small (it is more likely
  that we will need to send packets from abc::1 -> fe80:2 before we need
  rds-udp :-) :-) :-))
- and if rds-udp happens, we can use 16385/udp for rds-udp

so please use 18635 for your RDS_CM_PORT and move it to IB specific
header files and lets converge this thread.

Towing RDS_TCP_PORT around has absolutely nothing to do with your
ipv6 patch set.

> strange that RDS/TCP does not use the port number already registered.
> Anyway, the above comments are just a note on this fact in the IANA

16385 was in defacto use for a long time (since 2.6 kernels), thus I
registered it as well when I started fixing this up. 

the 18634/18635 are registered for rds-iB, (caps intended) not rds-tcp.
They are available for your use.

> database.  The following is a link to the IANA assignment.

yes, I am aware

> IMHO, there should only be RDS_PORT in the first place.  And it
> can be used for all transports.  For example, if RDS/SCTP is added,
> it can also use the same port number.  There is no need to define

if/when we add rds/sctp, we shall keep that in mind. 

This is getting to be "arguing for the sake of arguing". I dont have
the time for that.

> a new port number for each transport.  What is the rationale that
> each transport needs to have its own number and be defined in its
> own header file?

Some transports may not even need a port number. Some may need
several. Some may use sysctl (suggested by Tom Herbert) to make
this configurable. Until recently, we had rds/iWARP, that may need
its own transport hooks, it does not make sense to peanut-butter
that into the core module.

That is why it has to be in the transport. I hope that answers the
question.

> If the behavior of a software component is modified/enhanced such
> that the existing API of this component has problems dealing with
> this new behavior, should the API be modified or a new API be added
> to handle that?  If neither is done, shouldn't this be considered
> a bug?

whatever. The design (parallelization for perf) is fine. Some
parts of the API are work in progress based on priority/need. 
I dont want to bicker over this one, except to note that the judgemental
nature of the comment is interesting.

> >   Also, while you are there, s/exisiting/existing, please.
> 
> 
> OK, with change that.

Wonderful.

For the rest, I repeat: Oracle Clusters are using UDP/IPV6 today
(with no RDS). You need feature compat with UDP for that reason.

--Sowmini

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr
  2018-07-06 15:08         ` Ka-Cheong Poon
@ 2018-07-06 15:25           ` Sowmini Varadhan
  2018-07-06 17:26             ` Santosh Shilimkar
  0 siblings, 1 reply; 18+ messages in thread
From: Sowmini Varadhan @ 2018-07-06 15:25 UTC (permalink / raw)
  To: Ka-Cheong Poon; +Cc: Santosh Shilimkar, davem, netdev, rds-devel

On (07/06/18 23:08), Ka-Cheong Poon wrote:
> 
> As mentioned in a previous mail, it is unclear why the
> port number is transport specific.  Most Internet services
> use the same port number running over TCP/UDP as shown
> in the IANA database.  And the IANA RDS registration is
> the same.  What is the rationale of having a transport
> specific number in the RDS implementation?

because not every transport may need a port number.

e.g., "RDS over pigeon carrier" may not need a port number.

in a different design (e.g., KCM) the listen port is configurable
with sysctl

Some may need more than one, e.g., rds_rdma, evidently.

What is the problem with using the unused 18635 for the RDS_CM_PORT?

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr
  2018-07-06 15:25           ` Sowmini Varadhan
@ 2018-07-06 17:26             ` Santosh Shilimkar
  2018-07-09 12:26               ` Ka-Cheong Poon
  0 siblings, 1 reply; 18+ messages in thread
From: Santosh Shilimkar @ 2018-07-06 17:26 UTC (permalink / raw)
  To: Ka-Cheong Poon; +Cc: Sowmini Varadhan, davem, netdev, rds-devel

Hi Ka-Cheong,

On 7/6/2018 8:25 AM, Sowmini Varadhan wrote:
> On (07/06/18 23:08), Ka-Cheong Poon wrote:
>>
>> As mentioned in a previous mail, it is unclear why the
>> port number is transport specific.  Most Internet services
>> use the same port number running over TCP/UDP as shown
>> in the IANA database.  And the IANA RDS registration is
>> the same.  What is the rationale of having a transport
>> specific number in the RDS implementation?
> 
> because not every transport may need a port number.
> 
Lets keep separate port for RDMA and TCP transport. This has been
already useful for wireshark dissector and can also help for eBPF
like external tooling. The fragment format and re-assembly is
different across transports.

I do see your point and also agree that port number isn't transport
specific and in case we need to add another transport, what port
to use. But may be till then lets keep the existing behavior.
As such this port switch is not related to IPv6 support as such
so lets deal with it separately.

Regards,
Santosh

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 0/3] rds: IPv6 support
  2018-07-06 15:14     ` Sowmini Varadhan
@ 2018-07-09 12:24       ` Ka-Cheong Poon
  0 siblings, 0 replies; 18+ messages in thread
From: Ka-Cheong Poon @ 2018-07-09 12:24 UTC (permalink / raw)
  To: Sowmini Varadhan; +Cc: netdev, santosh.shilimkar, davem, rds-devel

On 07/06/2018 11:14 PM, Sowmini Varadhan wrote:

>>> - In net/rds/rds.h;
>>>
>>>    /* The following ports, 16385, 18634, 18635, are registered with IANA as
>>>     * the ports to be used for RDS over TCP and UDP.  18634 is the historical
>>>
>>>    What is "RDS over TCP and UDP"? There is no such thing as RDS-over-UDP.
>>>    IN fact RDS has nothing to do with UDP. The comment is confused. See next
>>>    item below, where the comment disappears.
>>
>>
>> As mentioned in the above comments, they are registered with IANA.
> 
> 16385 is registered for  RDS-TCP.


Yes, and it is noted in the comments.  The above comment is stating
the fact so that folks know what is already done when checking the
code.


> what does it mean to run rds-tcp and rds_rdma with the CM at the same time?
> Is this possible?  (if it is, why not poach some other number like 2049
> from NFS?!)


RDS can run over TCP and RDMA at the same time just like other
Internet services running over different transports.


> 18635 is actually not used in the RDS code.
> Why can you not use that for RDS_CM_PORT?


Just like any other Internet services, as long as a number is free,
it can use that number and register it with IANA.  There is no special
meaning about the number itself.  It is only a number to use and need
to be agreed on.  And the normal practice is that the same number be
used for a service running over different Internet transports.  This
is a good practice to avoid confusion.


> In general, please do NOT pull up these definitions into net/rds/rds.h
> They may change in the future- and the really belong in the transport
> specific header file - you question this further below, see my answer
> there.


If the number is changed, it will break compatibility.  Just like
any other Internet services, the registered service port number
won't change unless under very special circumstances.


>> There is no RDS/UDP in Linux but the port numbers are registered
>> nevertheless.  And RDS can work over UDP AFAICT.  BTW, it is really
> 
> No it cannot. RDS needs a reliable transport.  If you start using
> UDP (or pigeon-carrier) for the transport you have to build the
> reliability yourself.


As you noted above, if one build the reliability on top of UDP, one
can run RDS over UDP.  Is there something which is preventing RDS
to be run over other Internet transports?


> The Oracle DB libraries either use RDS-TCP or RDS-IB or UDP (in the UDP
> case they do their own congestion management in userspace, and
> history has shown that it is hard to get that right, thus the desire to
> switch to rds-tcp).


So RDS can in fact run over UDP and there is already an implementation.
Is there any problem with the comment then?


> Anyway, even though Andy Grover registered 18635 for "RDS-IB UDP",
> that is probably the most harmless one to use for this case, because
> - it is unused,
> - it calls itself rds-Infiniband,
> - the likelihood of doing rds-udp is infinitesmally small (it is more likely
>    that we will need to send packets from abc::1 -> fe80:2 before we need
>    rds-udp :-) :-) :-))
> - and if rds-udp happens, we can use 16385/udp for rds-udp
> 
> so please use 18635 for your RDS_CM_PORT and move it to IB specific
> header files and lets converge this thread.


I don't understand the rationale.  What is the problem of using 16385?
The above points do not show any problem in using this number.


> Towing RDS_TCP_PORT around has absolutely nothing to do with your
> ipv6 patch set.


Could you please elaborate this?  For example, is it a problem of
using port 80 for a HTPP web server running over SCTP?  I don't
believe there is.  It is a normal practice in Internet services.


>> strange that RDS/TCP does not use the port number already registered.
>> Anyway, the above comments are just a note on this fact in the IANA
> 
> 16385 was in defacto use for a long time (since 2.6 kernels), thus I
> registered it as well when I started fixing this up.


So this is a fix for a previous issue.  This is exactly the point
I made above.  The service port number won't change.  I think this
answers the "they may change in the future" point above.


> the 18634/18635 are registered for rds-iB, (caps intended) not rds-tcp.
> They are available for your use.


As there is really no problem is using 16385 and Oracle Linux is
already using it, I cannot agree on changing it.  It will just
cause inter-operability problem without any reason.


>> database.  The following is a link to the IANA assignment.
> 
> yes, I am aware
> 
>> IMHO, there should only be RDS_PORT in the first place.  And it
>> can be used for all transports.  For example, if RDS/SCTP is added,
>> it can also use the same port number.  There is no need to define
> 
> if/when we add rds/sctp, we shall keep that in mind.


This is the point of the comment.  I'm glad that it is noted.


> This is getting to be "arguing for the sake of arguing". I dont have
> the time for that.
> 
>> a new port number for each transport.  What is the rationale that
>> each transport needs to have its own number and be defined in its
>> own header file?
> 
> Some transports may not even need a port number. Some may need
> several. Some may use sysctl (suggested by Tom Herbert) to make
> this configurable. Until recently, we had rds/iWARP, that may need
> its own transport hooks, it does not make sense to peanut-butter
> that into the core module.
> 
> That is why it has to be in the transport. I hope that answers the
> question.


The above comment seems to suggest that any future extension of RDS
must make use of a port number and cannot be changed just because
a #define is put in the rds.h header file.  I cannot see how this
can be true.  Anyway, if just moving the #define to a different file
makes folks happier, I can do it to save time.



-- 
K. Poon
ka-cheong.poon@oracle.com

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr
  2018-07-06 17:26             ` Santosh Shilimkar
@ 2018-07-09 12:26               ` Ka-Cheong Poon
  0 siblings, 0 replies; 18+ messages in thread
From: Ka-Cheong Poon @ 2018-07-09 12:26 UTC (permalink / raw)
  To: Santosh Shilimkar; +Cc: Sowmini Varadhan, davem, netdev, rds-devel

On 07/07/2018 01:26 AM, Santosh Shilimkar wrote:
> Hi Ka-Cheong,
> 
> On 7/6/2018 8:25 AM, Sowmini Varadhan wrote:
>> On (07/06/18 23:08), Ka-Cheong Poon wrote:
>>>
>>> As mentioned in a previous mail, it is unclear why the
>>> port number is transport specific.  Most Internet services
>>> use the same port number running over TCP/UDP as shown
>>> in the IANA database.  And the IANA RDS registration is
>>> the same.  What is the rationale of having a transport
>>> specific number in the RDS implementation?
>>
>> because not every transport may need a port number.
>>
> Lets keep separate port for RDMA and TCP transport. This has been
> already useful for wireshark dissector and can also help for eBPF
> like external tooling. The fragment format and re-assembly is
> different across transports.


But does it have anything to do with the fact that a #define
is in the rds.h file?


> I do see your point and also agree that port number isn't transport
> specific and in case we need to add another transport, what port
> to use. But may be till then lets keep the existing behavior.
> As such this port switch is not related to IPv6 support as such
> so lets deal with it separately.


As I mentioned in a previous mail, I can move the #define if
it makes folks happy.  But the number cannot be changed as it
is already being used.


-- 
K. Poon
ka-cheong.poon@oracle.com

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2018-07-09 12:26 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-27 10:23 [PATCH v2 net-next 0/3] rds: IPv6 support Ka-Cheong Poon
2018-06-27 10:23 ` [PATCH v2 net-next 1/3] rds: Changing IP address internal representation to struct in6_addr Ka-Cheong Poon
2018-06-30  8:50   ` David Miller
2018-07-03 14:02     ` Ka-Cheong Poon
2018-07-05 17:58   ` Santosh Shilimkar
2018-07-06  9:08     ` Ka-Cheong Poon
2018-07-06 10:15       ` Sowmini Varadhan
2018-07-06 15:08         ` Ka-Cheong Poon
2018-07-06 15:25           ` Sowmini Varadhan
2018-07-06 17:26             ` Santosh Shilimkar
2018-07-09 12:26               ` Ka-Cheong Poon
2018-07-06 14:55       ` santosh.shilimkar
2018-06-27 10:23 ` [PATCH v2 net-next 2/3] rds: Enable RDS IPv6 support Ka-Cheong Poon
2018-06-27 10:23 ` [PATCH v2 net-next 3/3] rds: Extend RDS API for " Ka-Cheong Poon
2018-07-05 17:44 ` [PATCH v2 net-next 0/3] rds: " Sowmini Varadhan
2018-07-06 14:36   ` Ka-Cheong Poon
2018-07-06 15:14     ` Sowmini Varadhan
2018-07-09 12:24       ` Ka-Cheong Poon

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.