bind.c 7.5 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 83
	if (rs && !sock_flag(rds_rs_to_sk(rs), SOCK_DEAD))
		rds_sock_addref(rs);
	else
A
Andy Grover 已提交
84
		rs = NULL;
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
	 */
K
Ka-Cheong Poon 已提交
176
	if (uaddr->sa_family == AF_INET) {
177 178
		struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;

K
Ka-Cheong Poon 已提交
179 180 181 182
		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)))
183 184 185 186
			return -EINVAL;
		ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &v6addr);
		binding_addr = &v6addr;
		port = sin->sin_port;
K
Ka-Cheong Poon 已提交
187
#if IS_ENABLED(CONFIG_IPV6)
K
Ka-Cheong Poon 已提交
188 189
	} else if (uaddr->sa_family == AF_INET6) {
		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)uaddr;
K
Ka-Cheong Poon 已提交
190
		int addr_type;
K
Ka-Cheong Poon 已提交
191 192 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

		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 已提交
218
#endif
219 220 221
	} else {
		return -EINVAL;
	}
A
Andy Grover 已提交
222 223
	lock_sock(sk);

224 225
	/* RDS socket does not allow re-binding. */
	if (!ipv6_addr_any(&rs->rs_bound_addr)) {
A
Andy Grover 已提交
226 227 228
		ret = -EINVAL;
		goto out;
	}
K
Ka-Cheong Poon 已提交
229 230 231 232 233 234 235 236 237 238
	/* 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 已提交
239

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

245
	if (rs->rs_transport) { /* previously bound */
246 247
		trans = rs->rs_transport;
		if (trans->laddr_check(sock_net(sock->sk),
248
				       binding_addr, scope_id) != 0) {
249 250 251 252 253
			ret = -ENOPROTOOPT;
			rds_remove_bound(rs);
		} else {
			ret = 0;
		}
254 255
		goto out;
	}
256 257
	trans = rds_trans_get_preferred(sock_net(sock->sk), binding_addr,
					scope_id);
258
	if (!trans) {
A
Andy Grover 已提交
259 260
		ret = -EADDRNOTAVAIL;
		rds_remove_bound(rs);
261 262
		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 已提交
263 264 265 266 267 268 269 270 271 272
		goto out;
	}

	rs->rs_transport = trans;
	ret = 0;

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

274
void rds_bind_lock_destroy(void)
275
{
276 277
	rhashtable_destroy(&bind_hash_table);
}
278

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