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