162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Service connection management
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include "ar-internal.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * Find a service connection under RCU conditions.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * We could use a hash table, but that is subject to bucket stuffing by an
1562306a36Sopenharmony_ci * attacker as the client gets to pick the epoch and cid values and would know
1662306a36Sopenharmony_ci * the hash function.  So, instead, we use a hash table for the peer and from
1762306a36Sopenharmony_ci * that an rbtree to find the service connection.  Under ordinary circumstances
1862306a36Sopenharmony_ci * it might be slower than a large hash table, but it is at least limited in
1962306a36Sopenharmony_ci * depth.
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_cistruct rxrpc_connection *rxrpc_find_service_conn_rcu(struct rxrpc_peer *peer,
2262306a36Sopenharmony_ci						     struct sk_buff *skb)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct rxrpc_connection *conn = NULL;
2562306a36Sopenharmony_ci	struct rxrpc_conn_proto k;
2662306a36Sopenharmony_ci	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
2762306a36Sopenharmony_ci	struct rb_node *p;
2862306a36Sopenharmony_ci	unsigned int seq = 1;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	k.epoch	= sp->hdr.epoch;
3162306a36Sopenharmony_ci	k.cid	= sp->hdr.cid & RXRPC_CIDMASK;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	do {
3462306a36Sopenharmony_ci		/* Unfortunately, rbtree walking doesn't give reliable results
3562306a36Sopenharmony_ci		 * under just the RCU read lock, so we have to check for
3662306a36Sopenharmony_ci		 * changes.
3762306a36Sopenharmony_ci		 */
3862306a36Sopenharmony_ci		seq++; /* 2 on the 1st/lockless path, otherwise odd */
3962306a36Sopenharmony_ci		read_seqbegin_or_lock(&peer->service_conn_lock, &seq);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci		p = rcu_dereference_raw(peer->service_conns.rb_node);
4262306a36Sopenharmony_ci		while (p) {
4362306a36Sopenharmony_ci			conn = rb_entry(p, struct rxrpc_connection, service_node);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci			if (conn->proto.index_key < k.index_key)
4662306a36Sopenharmony_ci				p = rcu_dereference_raw(p->rb_left);
4762306a36Sopenharmony_ci			else if (conn->proto.index_key > k.index_key)
4862306a36Sopenharmony_ci				p = rcu_dereference_raw(p->rb_right);
4962306a36Sopenharmony_ci			else
5062306a36Sopenharmony_ci				break;
5162306a36Sopenharmony_ci			conn = NULL;
5262306a36Sopenharmony_ci		}
5362306a36Sopenharmony_ci	} while (need_seqretry(&peer->service_conn_lock, seq));
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	done_seqretry(&peer->service_conn_lock, seq);
5662306a36Sopenharmony_ci	_leave(" = %d", conn ? conn->debug_id : -1);
5762306a36Sopenharmony_ci	return conn;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/*
6162306a36Sopenharmony_ci * Insert a service connection into a peer's tree, thereby making it a target
6262306a36Sopenharmony_ci * for incoming packets.
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_cistatic void rxrpc_publish_service_conn(struct rxrpc_peer *peer,
6562306a36Sopenharmony_ci				       struct rxrpc_connection *conn)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct rxrpc_connection *cursor = NULL;
6862306a36Sopenharmony_ci	struct rxrpc_conn_proto k = conn->proto;
6962306a36Sopenharmony_ci	struct rb_node **pp, *parent;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	write_seqlock(&peer->service_conn_lock);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	pp = &peer->service_conns.rb_node;
7462306a36Sopenharmony_ci	parent = NULL;
7562306a36Sopenharmony_ci	while (*pp) {
7662306a36Sopenharmony_ci		parent = *pp;
7762306a36Sopenharmony_ci		cursor = rb_entry(parent,
7862306a36Sopenharmony_ci				  struct rxrpc_connection, service_node);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		if (cursor->proto.index_key < k.index_key)
8162306a36Sopenharmony_ci			pp = &(*pp)->rb_left;
8262306a36Sopenharmony_ci		else if (cursor->proto.index_key > k.index_key)
8362306a36Sopenharmony_ci			pp = &(*pp)->rb_right;
8462306a36Sopenharmony_ci		else
8562306a36Sopenharmony_ci			goto found_extant_conn;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	rb_link_node_rcu(&conn->service_node, parent, pp);
8962306a36Sopenharmony_ci	rb_insert_color(&conn->service_node, &peer->service_conns);
9062306a36Sopenharmony_ciconn_published:
9162306a36Sopenharmony_ci	set_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags);
9262306a36Sopenharmony_ci	write_sequnlock(&peer->service_conn_lock);
9362306a36Sopenharmony_ci	_leave(" = %d [new]", conn->debug_id);
9462306a36Sopenharmony_ci	return;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cifound_extant_conn:
9762306a36Sopenharmony_ci	if (refcount_read(&cursor->ref) == 0)
9862306a36Sopenharmony_ci		goto replace_old_connection;
9962306a36Sopenharmony_ci	write_sequnlock(&peer->service_conn_lock);
10062306a36Sopenharmony_ci	/* We should not be able to get here.  rxrpc_incoming_connection() is
10162306a36Sopenharmony_ci	 * called in a non-reentrant context, so there can't be a race to
10262306a36Sopenharmony_ci	 * insert a new connection.
10362306a36Sopenharmony_ci	 */
10462306a36Sopenharmony_ci	BUG();
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cireplace_old_connection:
10762306a36Sopenharmony_ci	/* The old connection is from an outdated epoch. */
10862306a36Sopenharmony_ci	_debug("replace conn");
10962306a36Sopenharmony_ci	rb_replace_node_rcu(&cursor->service_node,
11062306a36Sopenharmony_ci			    &conn->service_node,
11162306a36Sopenharmony_ci			    &peer->service_conns);
11262306a36Sopenharmony_ci	clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &cursor->flags);
11362306a36Sopenharmony_ci	goto conn_published;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/*
11762306a36Sopenharmony_ci * Preallocate a service connection.  The connection is placed on the proc and
11862306a36Sopenharmony_ci * reap lists so that we don't have to get the lock from BH context.
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_cistruct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *rxnet,
12162306a36Sopenharmony_ci							   gfp_t gfp)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct rxrpc_connection *conn = rxrpc_alloc_connection(rxnet, gfp);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (conn) {
12662306a36Sopenharmony_ci		/* We maintain an extra ref on the connection whilst it is on
12762306a36Sopenharmony_ci		 * the rxrpc_connections list.
12862306a36Sopenharmony_ci		 */
12962306a36Sopenharmony_ci		conn->state = RXRPC_CONN_SERVICE_PREALLOC;
13062306a36Sopenharmony_ci		refcount_set(&conn->ref, 2);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci		atomic_inc(&rxnet->nr_conns);
13362306a36Sopenharmony_ci		write_lock(&rxnet->conn_lock);
13462306a36Sopenharmony_ci		list_add_tail(&conn->link, &rxnet->service_conns);
13562306a36Sopenharmony_ci		list_add_tail(&conn->proc_link, &rxnet->conn_proc_list);
13662306a36Sopenharmony_ci		write_unlock(&rxnet->conn_lock);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		rxrpc_see_connection(conn, rxrpc_conn_new_service);
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	return conn;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/*
14562306a36Sopenharmony_ci * Set up an incoming connection.  This is called in BH context with the RCU
14662306a36Sopenharmony_ci * read lock held.
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_civoid rxrpc_new_incoming_connection(struct rxrpc_sock *rx,
14962306a36Sopenharmony_ci				   struct rxrpc_connection *conn,
15062306a36Sopenharmony_ci				   const struct rxrpc_security *sec,
15162306a36Sopenharmony_ci				   struct sk_buff *skb)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	_enter("");
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	conn->proto.epoch	= sp->hdr.epoch;
15862306a36Sopenharmony_ci	conn->proto.cid		= sp->hdr.cid & RXRPC_CIDMASK;
15962306a36Sopenharmony_ci	conn->orig_service_id	= sp->hdr.serviceId;
16062306a36Sopenharmony_ci	conn->service_id	= sp->hdr.serviceId;
16162306a36Sopenharmony_ci	conn->security_ix	= sp->hdr.securityIndex;
16262306a36Sopenharmony_ci	conn->out_clientflag	= 0;
16362306a36Sopenharmony_ci	conn->security		= sec;
16462306a36Sopenharmony_ci	if (conn->security_ix)
16562306a36Sopenharmony_ci		conn->state	= RXRPC_CONN_SERVICE_UNSECURED;
16662306a36Sopenharmony_ci	else
16762306a36Sopenharmony_ci		conn->state	= RXRPC_CONN_SERVICE;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/* See if we should upgrade the service.  This can only happen on the
17062306a36Sopenharmony_ci	 * first packet on a new connection.  Once done, it applies to all
17162306a36Sopenharmony_ci	 * subsequent calls on that connection.
17262306a36Sopenharmony_ci	 */
17362306a36Sopenharmony_ci	if (sp->hdr.userStatus == RXRPC_USERSTATUS_SERVICE_UPGRADE &&
17462306a36Sopenharmony_ci	    conn->service_id == rx->service_upgrade.from)
17562306a36Sopenharmony_ci		conn->service_id = rx->service_upgrade.to;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	atomic_set(&conn->active, 1);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* Make the connection a target for incoming packets. */
18062306a36Sopenharmony_ci	rxrpc_publish_service_conn(conn->peer, conn);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/*
18462306a36Sopenharmony_ci * Remove the service connection from the peer's tree, thereby removing it as a
18562306a36Sopenharmony_ci * target for incoming packets.
18662306a36Sopenharmony_ci */
18762306a36Sopenharmony_civoid rxrpc_unpublish_service_conn(struct rxrpc_connection *conn)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct rxrpc_peer *peer = conn->peer;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	write_seqlock(&peer->service_conn_lock);
19262306a36Sopenharmony_ci	if (test_and_clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags))
19362306a36Sopenharmony_ci		rb_erase(&conn->service_node, &peer->service_conns);
19462306a36Sopenharmony_ci	write_sequnlock(&peer->service_conn_lock);
19562306a36Sopenharmony_ci}
196