18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Service connection management
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include "ar-internal.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistatic struct rxrpc_bundle rxrpc_service_dummy_bundle = {
128c2ecf20Sopenharmony_ci	.ref		= REFCOUNT_INIT(1),
138c2ecf20Sopenharmony_ci	.debug_id	= UINT_MAX,
148c2ecf20Sopenharmony_ci	.channel_lock	= __SPIN_LOCK_UNLOCKED(&rxrpc_service_dummy_bundle.channel_lock),
158c2ecf20Sopenharmony_ci};
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci * Find a service connection under RCU conditions.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * We could use a hash table, but that is subject to bucket stuffing by an
218c2ecf20Sopenharmony_ci * attacker as the client gets to pick the epoch and cid values and would know
228c2ecf20Sopenharmony_ci * the hash function.  So, instead, we use a hash table for the peer and from
238c2ecf20Sopenharmony_ci * that an rbtree to find the service connection.  Under ordinary circumstances
248c2ecf20Sopenharmony_ci * it might be slower than a large hash table, but it is at least limited in
258c2ecf20Sopenharmony_ci * depth.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_cistruct rxrpc_connection *rxrpc_find_service_conn_rcu(struct rxrpc_peer *peer,
288c2ecf20Sopenharmony_ci						     struct sk_buff *skb)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct rxrpc_connection *conn = NULL;
318c2ecf20Sopenharmony_ci	struct rxrpc_conn_proto k;
328c2ecf20Sopenharmony_ci	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
338c2ecf20Sopenharmony_ci	struct rb_node *p;
348c2ecf20Sopenharmony_ci	unsigned int seq = 1;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	k.epoch	= sp->hdr.epoch;
378c2ecf20Sopenharmony_ci	k.cid	= sp->hdr.cid & RXRPC_CIDMASK;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	do {
408c2ecf20Sopenharmony_ci		/* Unfortunately, rbtree walking doesn't give reliable results
418c2ecf20Sopenharmony_ci		 * under just the RCU read lock, so we have to check for
428c2ecf20Sopenharmony_ci		 * changes.
438c2ecf20Sopenharmony_ci		 */
448c2ecf20Sopenharmony_ci		seq++; /* 2 on the 1st/lockless path, otherwise odd */
458c2ecf20Sopenharmony_ci		read_seqbegin_or_lock(&peer->service_conn_lock, &seq);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci		p = rcu_dereference_raw(peer->service_conns.rb_node);
488c2ecf20Sopenharmony_ci		while (p) {
498c2ecf20Sopenharmony_ci			conn = rb_entry(p, struct rxrpc_connection, service_node);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci			if (conn->proto.index_key < k.index_key)
528c2ecf20Sopenharmony_ci				p = rcu_dereference_raw(p->rb_left);
538c2ecf20Sopenharmony_ci			else if (conn->proto.index_key > k.index_key)
548c2ecf20Sopenharmony_ci				p = rcu_dereference_raw(p->rb_right);
558c2ecf20Sopenharmony_ci			else
568c2ecf20Sopenharmony_ci				break;
578c2ecf20Sopenharmony_ci			conn = NULL;
588c2ecf20Sopenharmony_ci		}
598c2ecf20Sopenharmony_ci	} while (need_seqretry(&peer->service_conn_lock, seq));
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	done_seqretry(&peer->service_conn_lock, seq);
628c2ecf20Sopenharmony_ci	_leave(" = %d", conn ? conn->debug_id : -1);
638c2ecf20Sopenharmony_ci	return conn;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/*
678c2ecf20Sopenharmony_ci * Insert a service connection into a peer's tree, thereby making it a target
688c2ecf20Sopenharmony_ci * for incoming packets.
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_cistatic void rxrpc_publish_service_conn(struct rxrpc_peer *peer,
718c2ecf20Sopenharmony_ci				       struct rxrpc_connection *conn)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct rxrpc_connection *cursor = NULL;
748c2ecf20Sopenharmony_ci	struct rxrpc_conn_proto k = conn->proto;
758c2ecf20Sopenharmony_ci	struct rb_node **pp, *parent;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	write_seqlock_bh(&peer->service_conn_lock);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	pp = &peer->service_conns.rb_node;
808c2ecf20Sopenharmony_ci	parent = NULL;
818c2ecf20Sopenharmony_ci	while (*pp) {
828c2ecf20Sopenharmony_ci		parent = *pp;
838c2ecf20Sopenharmony_ci		cursor = rb_entry(parent,
848c2ecf20Sopenharmony_ci				  struct rxrpc_connection, service_node);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci		if (cursor->proto.index_key < k.index_key)
878c2ecf20Sopenharmony_ci			pp = &(*pp)->rb_left;
888c2ecf20Sopenharmony_ci		else if (cursor->proto.index_key > k.index_key)
898c2ecf20Sopenharmony_ci			pp = &(*pp)->rb_right;
908c2ecf20Sopenharmony_ci		else
918c2ecf20Sopenharmony_ci			goto found_extant_conn;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	rb_link_node_rcu(&conn->service_node, parent, pp);
958c2ecf20Sopenharmony_ci	rb_insert_color(&conn->service_node, &peer->service_conns);
968c2ecf20Sopenharmony_ciconn_published:
978c2ecf20Sopenharmony_ci	set_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags);
988c2ecf20Sopenharmony_ci	write_sequnlock_bh(&peer->service_conn_lock);
998c2ecf20Sopenharmony_ci	_leave(" = %d [new]", conn->debug_id);
1008c2ecf20Sopenharmony_ci	return;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cifound_extant_conn:
1038c2ecf20Sopenharmony_ci	if (refcount_read(&cursor->ref) == 0)
1048c2ecf20Sopenharmony_ci		goto replace_old_connection;
1058c2ecf20Sopenharmony_ci	write_sequnlock_bh(&peer->service_conn_lock);
1068c2ecf20Sopenharmony_ci	/* We should not be able to get here.  rxrpc_incoming_connection() is
1078c2ecf20Sopenharmony_ci	 * called in a non-reentrant context, so there can't be a race to
1088c2ecf20Sopenharmony_ci	 * insert a new connection.
1098c2ecf20Sopenharmony_ci	 */
1108c2ecf20Sopenharmony_ci	BUG();
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cireplace_old_connection:
1138c2ecf20Sopenharmony_ci	/* The old connection is from an outdated epoch. */
1148c2ecf20Sopenharmony_ci	_debug("replace conn");
1158c2ecf20Sopenharmony_ci	rb_replace_node_rcu(&cursor->service_node,
1168c2ecf20Sopenharmony_ci			    &conn->service_node,
1178c2ecf20Sopenharmony_ci			    &peer->service_conns);
1188c2ecf20Sopenharmony_ci	clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &cursor->flags);
1198c2ecf20Sopenharmony_ci	goto conn_published;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/*
1238c2ecf20Sopenharmony_ci * Preallocate a service connection.  The connection is placed on the proc and
1248c2ecf20Sopenharmony_ci * reap lists so that we don't have to get the lock from BH context.
1258c2ecf20Sopenharmony_ci */
1268c2ecf20Sopenharmony_cistruct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *rxnet,
1278c2ecf20Sopenharmony_ci							   gfp_t gfp)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	struct rxrpc_connection *conn = rxrpc_alloc_connection(gfp);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (conn) {
1328c2ecf20Sopenharmony_ci		/* We maintain an extra ref on the connection whilst it is on
1338c2ecf20Sopenharmony_ci		 * the rxrpc_connections list.
1348c2ecf20Sopenharmony_ci		 */
1358c2ecf20Sopenharmony_ci		conn->state = RXRPC_CONN_SERVICE_PREALLOC;
1368c2ecf20Sopenharmony_ci		refcount_set(&conn->ref, 2);
1378c2ecf20Sopenharmony_ci		conn->bundle = rxrpc_get_bundle(&rxrpc_service_dummy_bundle);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		atomic_inc(&rxnet->nr_conns);
1408c2ecf20Sopenharmony_ci		write_lock(&rxnet->conn_lock);
1418c2ecf20Sopenharmony_ci		list_add_tail(&conn->link, &rxnet->service_conns);
1428c2ecf20Sopenharmony_ci		list_add_tail(&conn->proc_link, &rxnet->conn_proc_list);
1438c2ecf20Sopenharmony_ci		write_unlock(&rxnet->conn_lock);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		trace_rxrpc_conn(conn->debug_id, rxrpc_conn_new_service,
1468c2ecf20Sopenharmony_ci				 refcount_read(&conn->ref),
1478c2ecf20Sopenharmony_ci				 __builtin_return_address(0));
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return conn;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci/*
1548c2ecf20Sopenharmony_ci * Set up an incoming connection.  This is called in BH context with the RCU
1558c2ecf20Sopenharmony_ci * read lock held.
1568c2ecf20Sopenharmony_ci */
1578c2ecf20Sopenharmony_civoid rxrpc_new_incoming_connection(struct rxrpc_sock *rx,
1588c2ecf20Sopenharmony_ci				   struct rxrpc_connection *conn,
1598c2ecf20Sopenharmony_ci				   const struct rxrpc_security *sec,
1608c2ecf20Sopenharmony_ci				   struct key *key,
1618c2ecf20Sopenharmony_ci				   struct sk_buff *skb)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	_enter("");
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	conn->proto.epoch	= sp->hdr.epoch;
1688c2ecf20Sopenharmony_ci	conn->proto.cid		= sp->hdr.cid & RXRPC_CIDMASK;
1698c2ecf20Sopenharmony_ci	conn->params.service_id	= sp->hdr.serviceId;
1708c2ecf20Sopenharmony_ci	conn->service_id	= sp->hdr.serviceId;
1718c2ecf20Sopenharmony_ci	conn->security_ix	= sp->hdr.securityIndex;
1728c2ecf20Sopenharmony_ci	conn->out_clientflag	= 0;
1738c2ecf20Sopenharmony_ci	conn->security		= sec;
1748c2ecf20Sopenharmony_ci	conn->server_key	= key_get(key);
1758c2ecf20Sopenharmony_ci	if (conn->security_ix)
1768c2ecf20Sopenharmony_ci		conn->state	= RXRPC_CONN_SERVICE_UNSECURED;
1778c2ecf20Sopenharmony_ci	else
1788c2ecf20Sopenharmony_ci		conn->state	= RXRPC_CONN_SERVICE;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* See if we should upgrade the service.  This can only happen on the
1818c2ecf20Sopenharmony_ci	 * first packet on a new connection.  Once done, it applies to all
1828c2ecf20Sopenharmony_ci	 * subsequent calls on that connection.
1838c2ecf20Sopenharmony_ci	 */
1848c2ecf20Sopenharmony_ci	if (sp->hdr.userStatus == RXRPC_USERSTATUS_SERVICE_UPGRADE &&
1858c2ecf20Sopenharmony_ci	    conn->service_id == rx->service_upgrade.from)
1868c2ecf20Sopenharmony_ci		conn->service_id = rx->service_upgrade.to;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* Make the connection a target for incoming packets. */
1898c2ecf20Sopenharmony_ci	rxrpc_publish_service_conn(conn->params.peer, conn);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	_net("CONNECTION new %d {%x}", conn->debug_id, conn->proto.cid);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/*
1958c2ecf20Sopenharmony_ci * Remove the service connection from the peer's tree, thereby removing it as a
1968c2ecf20Sopenharmony_ci * target for incoming packets.
1978c2ecf20Sopenharmony_ci */
1988c2ecf20Sopenharmony_civoid rxrpc_unpublish_service_conn(struct rxrpc_connection *conn)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct rxrpc_peer *peer = conn->params.peer;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	write_seqlock_bh(&peer->service_conn_lock);
2038c2ecf20Sopenharmony_ci	if (test_and_clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags))
2048c2ecf20Sopenharmony_ci		rb_erase(&conn->service_node, &peer->service_conns);
2058c2ecf20Sopenharmony_ci	write_sequnlock_bh(&peer->service_conn_lock);
2068c2ecf20Sopenharmony_ci}
207