1 /* Service connection management
3 * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public Licence
8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version.
12 #include <linux/slab.h>
13 #include "ar-internal.h"
16 * Find a service connection under RCU conditions.
18 * We could use a hash table, but that is subject to bucket stuffing by an
19 * attacker as the client gets to pick the epoch and cid values and would know
20 * the hash function. So, instead, we use a hash table for the peer and from
21 * that an rbtree to find the service connection. Under ordinary circumstances
22 * it might be slower than a large hash table, but it is at least limited in
25 struct rxrpc_connection
*rxrpc_find_service_conn_rcu(struct rxrpc_peer
*peer
,
28 struct rxrpc_connection
*conn
= NULL
;
29 struct rxrpc_conn_proto k
;
30 struct rxrpc_skb_priv
*sp
= rxrpc_skb(skb
);
34 k
.epoch
= sp
->hdr
.epoch
;
35 k
.cid
= sp
->hdr
.cid
& RXRPC_CIDMASK
;
38 /* Unfortunately, rbtree walking doesn't give reliable results
39 * under just the RCU read lock, so we have to check for
42 read_seqbegin_or_lock(&peer
->service_conn_lock
, &seq
);
44 p
= rcu_dereference_raw(peer
->service_conns
.rb_node
);
46 conn
= rb_entry(p
, struct rxrpc_connection
, service_node
);
48 if (conn
->proto
.index_key
< k
.index_key
)
49 p
= rcu_dereference_raw(p
->rb_left
);
50 else if (conn
->proto
.index_key
> k
.index_key
)
51 p
= rcu_dereference_raw(p
->rb_right
);
56 } while (need_seqretry(&peer
->service_conn_lock
, seq
));
59 done_seqretry(&peer
->service_conn_lock
, seq
);
60 _leave(" = %d", conn
? conn
->debug_id
: -1);
65 * Insert a service connection into a peer's tree, thereby making it a target
66 * for incoming packets.
68 static struct rxrpc_connection
*
69 rxrpc_publish_service_conn(struct rxrpc_peer
*peer
,
70 struct rxrpc_connection
*conn
)
72 struct rxrpc_connection
*cursor
= NULL
;
73 struct rxrpc_conn_proto k
= conn
->proto
;
74 struct rb_node
**pp
, *parent
;
76 write_seqlock_bh(&peer
->service_conn_lock
);
78 pp
= &peer
->service_conns
.rb_node
;
82 cursor
= rb_entry(parent
,
83 struct rxrpc_connection
, service_node
);
85 if (cursor
->proto
.index_key
< k
.index_key
)
87 else if (cursor
->proto
.index_key
> k
.index_key
)
88 pp
= &(*pp
)->rb_right
;
90 goto found_extant_conn
;
93 rb_link_node_rcu(&conn
->service_node
, parent
, pp
);
94 rb_insert_color(&conn
->service_node
, &peer
->service_conns
);
96 set_bit(RXRPC_CONN_IN_SERVICE_CONNS
, &conn
->flags
);
97 write_sequnlock_bh(&peer
->service_conn_lock
);
98 _leave(" = %d [new]", conn
->debug_id
);
102 if (atomic_read(&cursor
->usage
) == 0)
103 goto replace_old_connection
;
104 write_sequnlock_bh(&peer
->service_conn_lock
);
105 /* We should not be able to get here. rxrpc_incoming_connection() is
106 * called in a non-reentrant context, so there can't be a race to
107 * insert a new connection.
111 replace_old_connection
:
112 /* The old connection is from an outdated epoch. */
113 _debug("replace conn");
114 rb_replace_node_rcu(&cursor
->service_node
,
116 &peer
->service_conns
);
117 clear_bit(RXRPC_CONN_IN_SERVICE_CONNS
, &cursor
->flags
);
122 * get a record of an incoming connection
124 struct rxrpc_connection
*rxrpc_incoming_connection(struct rxrpc_local
*local
,
125 struct sockaddr_rxrpc
*srx
,
128 struct rxrpc_connection
*conn
;
129 struct rxrpc_skb_priv
*sp
= rxrpc_skb(skb
);
130 struct rxrpc_peer
*peer
;
131 const char *new = "old";
135 peer
= rxrpc_lookup_peer(local
, srx
, GFP_NOIO
);
138 return ERR_PTR(-EBUSY
);
141 ASSERT(sp
->hdr
.flags
& RXRPC_CLIENT_INITIATED
);
144 peer
= rxrpc_lookup_peer_rcu(local
, srx
);
146 conn
= rxrpc_find_service_conn_rcu(peer
, skb
);
148 if (sp
->hdr
.securityIndex
!= conn
->security_ix
)
149 goto security_mismatch_rcu
;
150 if (rxrpc_get_connection_maybe(conn
))
151 goto found_extant_connection_rcu
;
153 /* The conn has expired but we can't remove it without
154 * the appropriate lock, so we attempt to replace it
155 * when we have a new candidate.
159 if (!rxrpc_get_peer_maybe(peer
))
165 peer
= rxrpc_lookup_peer(local
, srx
, GFP_NOIO
);
170 /* We don't have a matching record yet. */
171 conn
= rxrpc_alloc_connection(GFP_NOIO
);
175 conn
->proto
.epoch
= sp
->hdr
.epoch
;
176 conn
->proto
.cid
= sp
->hdr
.cid
& RXRPC_CIDMASK
;
177 conn
->params
.local
= local
;
178 conn
->params
.peer
= peer
;
179 conn
->params
.service_id
= sp
->hdr
.serviceId
;
180 conn
->security_ix
= sp
->hdr
.securityIndex
;
181 conn
->out_clientflag
= 0;
182 conn
->state
= RXRPC_CONN_SERVICE
;
183 if (conn
->params
.service_id
)
184 conn
->state
= RXRPC_CONN_SERVICE_UNSECURED
;
186 rxrpc_get_local(local
);
188 write_lock(&rxrpc_connection_lock
);
189 list_add_tail(&conn
->link
, &rxrpc_connections
);
190 write_unlock(&rxrpc_connection_lock
);
192 /* Make the connection a target for incoming packets. */
193 rxrpc_publish_service_conn(peer
, conn
);
198 _net("CONNECTION %s %d {%x}", new, conn
->debug_id
, conn
->proto
.cid
);
199 _leave(" = %p {u=%d}", conn
, atomic_read(&conn
->usage
));
202 found_extant_connection_rcu
:
206 security_mismatch_rcu
:
208 _leave(" = -EKEYREJECTED");
209 return ERR_PTR(-EKEYREJECTED
);
212 rxrpc_put_peer(peer
);
214 _leave(" = -ENOMEM");
215 return ERR_PTR(-ENOMEM
);
219 * Remove the service connection from the peer's tree, thereby removing it as a
220 * target for incoming packets.
222 void rxrpc_unpublish_service_conn(struct rxrpc_connection
*conn
)
224 struct rxrpc_peer
*peer
= conn
->params
.peer
;
226 write_seqlock_bh(&peer
->service_conn_lock
);
227 if (test_and_clear_bit(RXRPC_CONN_IN_SERVICE_CONNS
, &conn
->flags
))
228 rb_erase(&conn
->service_node
, &peer
->service_conns
);
229 write_sequnlock_bh(&peer
->service_conn_lock
);