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 79
	__rds_create_bind_key(key, addr, port, scope_id);
	rs = rhashtable_lookup_fast(&bind_hash_table, key, ht_parms);
80 81 82
	if (rs && !sock_flag(rds_rs_to_sk(rs), SOCK_DEAD))
		rds_sock_addref(rs);
	else
A
Andy Grover 已提交
83 84
		rs = NULL;

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

A
Andy Grover 已提交
88 89 90 91
	return rs;
}

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

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

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

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

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

	return ret;
}

void rds_remove_bound(struct rds_sock *rs)
{

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

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

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

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);
165
	struct in6_addr v6addr, *binding_addr;
A
Andy Grover 已提交
166
	struct rds_transport *trans;
167
	__u32 scope_id = 0;
A
Andy Grover 已提交
168
	int ret = 0;
169
	__be16 port;
A
Andy Grover 已提交
170

K
Ka-Cheong Poon 已提交
171 172
	/* We allow an RDS socket to be bound to either IPv4 or IPv6
	 * address.
173
	 */
K
Ka-Cheong Poon 已提交
174
	if (uaddr->sa_family == AF_INET) {
175 176
		struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;

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

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

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

238
	ret = rds_add_bound(rs, binding_addr, &port, scope_id);
A
Andy Grover 已提交
239 240 241
	if (ret)
		goto out;

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

	rs->rs_transport = trans;
	ret = 0;

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

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

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