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