bind.c 7.6 KB
Newer Older
A
Andy Grover 已提交
1
/*
2
 * Copyright (c) 2006, 2019 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

242 243 244 245
	/* The transport can be set using SO_RDS_TRANSPORT option before the
	 * socket is bound.
	 */
	if (rs->rs_transport) {
246 247
		trans = rs->rs_transport;
		if (trans->laddr_check(sock_net(sock->sk),
248
				       binding_addr, scope_id) != 0) {
249
			ret = -ENOPROTOOPT;
250
			goto out;
251
		}
252 253 254 255 256 257 258 259 260 261
	} else {
		trans = rds_trans_get_preferred(sock_net(sock->sk),
						binding_addr, scope_id);
		if (!trans) {
			ret = -EADDRNOTAVAIL;
			pr_info_ratelimited("RDS: %s could not find a transport for %pI6c, load rds_tcp or rds_rdma?\n",
					    __func__, binding_addr);
			goto out;
		}
		rs->rs_transport = trans;
A
Andy Grover 已提交
262 263
	}

264 265
	sock_set_flag(sk, SOCK_RCU_FREE);
	ret = rds_add_bound(rs, binding_addr, &port, scope_id);
A
Andy Grover 已提交
266 267 268 269 270

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

272
void rds_bind_lock_destroy(void)
273
{
274 275
	rhashtable_destroy(&bind_hash_table);
}
276

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