bind.c 7.7 KB
Newer Older
A
Andy Grover 已提交
1
/*
2
 * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
A
Andy Grover 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 *
 * 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
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */
#include <linux/kernel.h>
#include <net/sock.h>
#include <linux/in.h>
36
#include <linux/ipv6.h>
A
Andy Grover 已提交
37
#include <linux/if_arp.h>
38
#include <linux/jhash.h>
39
#include <linux/ratelimit.h>
A
Andy Grover 已提交
40 41
#include "rds.h"

42 43
static struct rhashtable bind_hash_table;

44
static const struct rhashtable_params ht_parms = {
45
	.nelem_hint = 768,
46
	.key_len = RDS_BOUND_KEY_LEN,
47 48 49 50
	.key_offset = offsetof(struct rds_sock, rs_bound_key),
	.head_offset = offsetof(struct rds_sock, rs_bound_node),
	.max_size = 16384,
	.min_size = 1024,
51 52
};

53 54 55 56 57 58 59 60 61 62 63 64 65
/* 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));
}

A
Andy Grover 已提交
66 67 68 69 70 71
/*
 * 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.
 */
72 73
struct rds_sock *rds_find_bound(const struct in6_addr *addr, __be16 port,
				__u32 scope_id)
A
Andy Grover 已提交
74
{
75
	u8 key[RDS_BOUND_KEY_LEN];
A
Andy Grover 已提交
76
	struct rds_sock *rs;
77

78
	__rds_create_bind_key(key, addr, port, scope_id);
C
Cong Wang 已提交
79 80
	rcu_read_lock();
	rs = rhashtable_lookup(&bind_hash_table, key, ht_parms);
81 82
	if (rs && (sock_flag(rds_rs_to_sk(rs), SOCK_DEAD) ||
		   !refcount_inc_not_zero(&rds_rs_to_sk(rs)->sk_refcnt)))
A
Andy Grover 已提交
83
		rs = NULL;
84

C
Cong Wang 已提交
85
	rcu_read_unlock();
A
Andy Grover 已提交
86

87 88
	rdsdebug("returning rs %p for %pI6c:%u\n", rs, addr,
		 ntohs(port));
89

A
Andy Grover 已提交
90 91 92 93
	return rs;
}

/* returns -ve errno or +ve port */
94 95
static int rds_add_bound(struct rds_sock *rs, const struct in6_addr *addr,
			 __be16 *port, __u32 scope_id)
A
Andy Grover 已提交
96 97 98
{
	int ret = -EADDRINUSE;
	u16 rover, last;
99
	u8 key[RDS_BOUND_KEY_LEN];
A
Andy Grover 已提交
100 101 102

	if (*port != 0) {
		rover = be16_to_cpu(*port);
103 104
		if (rover == RDS_FLAG_PROBE_PORT)
			return -EINVAL;
A
Andy Grover 已提交
105 106
		last = rover;
	} else {
107
		rover = max_t(u16, prandom_u32(), 2);
A
Andy Grover 已提交
108 109 110 111 112 113
		last = rover - 1;
	}

	do {
		if (rover == 0)
			rover++;
114

115 116
		if (rover == RDS_FLAG_PROBE_PORT)
			continue;
117 118 119
		__rds_create_bind_key(key, addr, cpu_to_be16(rover),
				      scope_id);
		if (rhashtable_lookup_fast(&bind_hash_table, key, ht_parms))
120 121
			continue;

122 123
		memcpy(rs->rs_bound_key, key, sizeof(rs->rs_bound_key));
		rs->rs_bound_addr = *addr;
124 125
		net_get_random_once(&rs->rs_hash_initval,
				    sizeof(rs->rs_hash_initval));
126 127 128 129 130
		rs->rs_bound_port = cpu_to_be16(rover);
		rs->rs_bound_node.next = NULL;
		rds_sock_addref(rs);
		if (!rhashtable_insert_fast(&bind_hash_table,
					    &rs->rs_bound_node, ht_parms)) {
131
			*port = rs->rs_bound_port;
K
Ka-Cheong Poon 已提交
132
			rs->rs_bound_scope_id = scope_id;
A
Andy Grover 已提交
133
			ret = 0;
K
Ka-Cheong Poon 已提交
134 135
			rdsdebug("rs %p binding to %pI6c:%d\n",
				 rs, addr, (int)ntohs(*port));
A
Andy Grover 已提交
136
			break;
137
		} else {
138
			rs->rs_bound_addr = in6addr_any;
139 140 141
			rds_sock_put(rs);
			ret = -ENOMEM;
			break;
A
Andy Grover 已提交
142 143 144 145 146 147 148 149 150
		}
	} while (rover++ != last);

	return ret;
}

void rds_remove_bound(struct rds_sock *rs)
{

151
	if (ipv6_addr_any(&rs->rs_bound_addr))
152
		return;
A
Andy Grover 已提交
153

154
	rdsdebug("rs %p unbinding from %pI6c:%d\n",
155 156
		 rs, &rs->rs_bound_addr,
		 ntohs(rs->rs_bound_port));
A
Andy Grover 已提交
157

158 159
	rhashtable_remove_fast(&bind_hash_table, &rs->rs_bound_node, ht_parms);
	rds_sock_put(rs);
160
	rs->rs_bound_addr = in6addr_any;
A
Andy Grover 已提交
161 162 163 164 165 166
}

int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
	struct sock *sk = sock->sk;
	struct rds_sock *rs = rds_sk_to_rs(sk);
167
	struct in6_addr v6addr, *binding_addr;
A
Andy Grover 已提交
168
	struct rds_transport *trans;
169
	__u32 scope_id = 0;
A
Andy Grover 已提交
170
	int ret = 0;
171
	__be16 port;
A
Andy Grover 已提交
172

K
Ka-Cheong Poon 已提交
173 174
	/* We allow an RDS socket to be bound to either IPv4 or IPv6
	 * address.
175
	 */
176 177
	if (addr_len < offsetofend(struct sockaddr, sa_family))
		return -EINVAL;
K
Ka-Cheong Poon 已提交
178
	if (uaddr->sa_family == AF_INET) {
179 180
		struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;

K
Ka-Cheong Poon 已提交
181 182 183 184
		if (addr_len < sizeof(struct sockaddr_in) ||
		    sin->sin_addr.s_addr == htonl(INADDR_ANY) ||
		    sin->sin_addr.s_addr == htonl(INADDR_BROADCAST) ||
		    IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
185 186 187 188
			return -EINVAL;
		ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &v6addr);
		binding_addr = &v6addr;
		port = sin->sin_port;
K
Ka-Cheong Poon 已提交
189
#if IS_ENABLED(CONFIG_IPV6)
K
Ka-Cheong Poon 已提交
190 191
	} else if (uaddr->sa_family == AF_INET6) {
		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)uaddr;
K
Ka-Cheong Poon 已提交
192
		int addr_type;
K
Ka-Cheong Poon 已提交
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219

		if (addr_len < sizeof(struct sockaddr_in6))
			return -EINVAL;
		addr_type = ipv6_addr_type(&sin6->sin6_addr);
		if (!(addr_type & IPV6_ADDR_UNICAST)) {
			__be32 addr4;

			if (!(addr_type & IPV6_ADDR_MAPPED))
				return -EINVAL;

			/* It is a mapped address.  Need to do some sanity
			 * checks.
			 */
			addr4 = sin6->sin6_addr.s6_addr32[3];
			if (addr4 == htonl(INADDR_ANY) ||
			    addr4 == htonl(INADDR_BROADCAST) ||
			    IN_MULTICAST(ntohl(addr4)))
				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;
K
Ka-Cheong Poon 已提交
220
#endif
221 222 223
	} else {
		return -EINVAL;
	}
A
Andy Grover 已提交
224 225
	lock_sock(sk);

226 227
	/* RDS socket does not allow re-binding. */
	if (!ipv6_addr_any(&rs->rs_bound_addr)) {
A
Andy Grover 已提交
228 229 230
		ret = -EINVAL;
		goto out;
	}
K
Ka-Cheong Poon 已提交
231 232 233 234 235 236 237 238 239 240
	/* Socket is connected.  The binding address should have the same
	 * scope ID as the connected address, except the case when one is
	 * non-link local address (scope_id is 0).
	 */
	if (!ipv6_addr_any(&rs->rs_conn_addr) && scope_id &&
	    rs->rs_bound_scope_id &&
	    scope_id != rs->rs_bound_scope_id) {
		ret = -EINVAL;
		goto out;
	}
A
Andy Grover 已提交
241

C
Cong Wang 已提交
242
	sock_set_flag(sk, SOCK_RCU_FREE);
243
	ret = rds_add_bound(rs, binding_addr, &port, scope_id);
A
Andy Grover 已提交
244 245 246
	if (ret)
		goto out;

247
	if (rs->rs_transport) { /* previously bound */
248 249
		trans = rs->rs_transport;
		if (trans->laddr_check(sock_net(sock->sk),
250
				       binding_addr, scope_id) != 0) {
251 252 253 254 255
			ret = -ENOPROTOOPT;
			rds_remove_bound(rs);
		} else {
			ret = 0;
		}
256 257
		goto out;
	}
258 259
	trans = rds_trans_get_preferred(sock_net(sock->sk), binding_addr,
					scope_id);
260
	if (!trans) {
A
Andy Grover 已提交
261 262
		ret = -EADDRNOTAVAIL;
		rds_remove_bound(rs);
263 264
		pr_info_ratelimited("RDS: %s could not find a transport for %pI6c, load rds_tcp or rds_rdma?\n",
				    __func__, binding_addr);
A
Andy Grover 已提交
265 266 267 268 269 270 271 272 273 274
		goto out;
	}

	rs->rs_transport = trans;
	ret = 0;

out:
	release_sock(sk);
	return ret;
}
275

276
void rds_bind_lock_destroy(void)
277
{
278 279
	rhashtable_destroy(&bind_hash_table);
}
280

281 282 283
int rds_bind_lock_init(void)
{
	return rhashtable_init(&bind_hash_table, &ht_parms);
284
}