aboutsummaryrefslogtreecommitdiffstats
path: root/net/rxrpc/ar-local.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2007-04-26 18:48:28 -0400
committerDavid S. Miller <davem@davemloft.net>2007-04-26 18:48:28 -0400
commit17926a79320afa9b95df6b977b40cca6d8713cea (patch)
tree5cedff43b69520ad17b86783d3752053686ec99c /net/rxrpc/ar-local.c
parente19dff1fdd99a25819af74cf0710e147fff4fd3a (diff)
[AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both
Provide AF_RXRPC sockets that can be used to talk to AFS servers, or serve answers to AFS clients. KerberosIV security is fully supported. The patches and some example test programs can be found in: http://people.redhat.com/~dhowells/rxrpc/ This will eventually replace the old implementation of kernel-only RxRPC currently resident in net/rxrpc/. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/rxrpc/ar-local.c')
-rw-r--r--net/rxrpc/ar-local.c309
1 files changed, 309 insertions, 0 deletions
diff --git a/net/rxrpc/ar-local.c b/net/rxrpc/ar-local.c
new file mode 100644
index 000000000000..a20a2c0fe105
--- /dev/null
+++ b/net/rxrpc/ar-local.c
@@ -0,0 +1,309 @@
1/* AF_RXRPC local endpoint management
2 *
3 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/module.h>
13#include <linux/net.h>
14#include <linux/skbuff.h>
15#include <net/sock.h>
16#include <net/af_rxrpc.h>
17#include "ar-internal.h"
18
19static LIST_HEAD(rxrpc_locals);
20DEFINE_RWLOCK(rxrpc_local_lock);
21static DECLARE_RWSEM(rxrpc_local_sem);
22static DECLARE_WAIT_QUEUE_HEAD(rxrpc_local_wq);
23
24static void rxrpc_destroy_local(struct work_struct *work);
25
26/*
27 * allocate a new local
28 */
29static
30struct rxrpc_local *rxrpc_alloc_local(struct sockaddr_rxrpc *srx)
31{
32 struct rxrpc_local *local;
33
34 local = kzalloc(sizeof(struct rxrpc_local), GFP_KERNEL);
35 if (local) {
36 INIT_WORK(&local->destroyer, &rxrpc_destroy_local);
37 INIT_WORK(&local->acceptor, &rxrpc_accept_incoming_calls);
38 INIT_WORK(&local->rejecter, &rxrpc_reject_packets);
39 INIT_LIST_HEAD(&local->services);
40 INIT_LIST_HEAD(&local->link);
41 init_rwsem(&local->defrag_sem);
42 skb_queue_head_init(&local->accept_queue);
43 skb_queue_head_init(&local->reject_queue);
44 spin_lock_init(&local->lock);
45 rwlock_init(&local->services_lock);
46 atomic_set(&local->usage, 1);
47 local->debug_id = atomic_inc_return(&rxrpc_debug_id);
48 memcpy(&local->srx, srx, sizeof(*srx));
49 }
50
51 _leave(" = %p", local);
52 return local;
53}
54
55/*
56 * create the local socket
57 * - must be called with rxrpc_local_sem writelocked
58 */
59static int rxrpc_create_local(struct rxrpc_local *local)
60{
61 struct sock *sock;
62 int ret, opt;
63
64 _enter("%p{%d}", local, local->srx.transport_type);
65
66 /* create a socket to represent the local endpoint */
67 ret = sock_create_kern(PF_INET, local->srx.transport_type, IPPROTO_UDP,
68 &local->socket);
69 if (ret < 0) {
70 _leave(" = %d [socket]", ret);
71 return ret;
72 }
73
74 /* if a local address was supplied then bind it */
75 if (local->srx.transport_len > sizeof(sa_family_t)) {
76 _debug("bind");
77 ret = kernel_bind(local->socket,
78 (struct sockaddr *) &local->srx.transport,
79 local->srx.transport_len);
80 if (ret < 0) {
81 _debug("bind failed");
82 goto error;
83 }
84 }
85
86 /* we want to receive ICMP errors */
87 opt = 1;
88 ret = kernel_setsockopt(local->socket, SOL_IP, IP_RECVERR,
89 (char *) &opt, sizeof(opt));
90 if (ret < 0) {
91 _debug("setsockopt failed");
92 goto error;
93 }
94
95 /* we want to set the don't fragment bit */
96 opt = IP_PMTUDISC_DO;
97 ret = kernel_setsockopt(local->socket, SOL_IP, IP_MTU_DISCOVER,
98 (char *) &opt, sizeof(opt));
99 if (ret < 0) {
100 _debug("setsockopt failed");
101 goto error;
102 }
103
104 write_lock_bh(&rxrpc_local_lock);
105 list_add(&local->link, &rxrpc_locals);
106 write_unlock_bh(&rxrpc_local_lock);
107
108 /* set the socket up */
109 sock = local->socket->sk;
110 sock->sk_user_data = local;
111 sock->sk_data_ready = rxrpc_data_ready;
112 sock->sk_error_report = rxrpc_UDP_error_report;
113 _leave(" = 0");
114 return 0;
115
116error:
117 local->socket->ops->shutdown(local->socket, 2);
118 local->socket->sk->sk_user_data = NULL;
119 sock_release(local->socket);
120 local->socket = NULL;
121
122 _leave(" = %d", ret);
123 return ret;
124}
125
126/*
127 * create a new local endpoint using the specified UDP address
128 */
129struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *srx)
130{
131 struct rxrpc_local *local;
132 int ret;
133
134 _enter("{%d,%u,%u.%u.%u.%u+%hu}",
135 srx->transport_type,
136 srx->transport.family,
137 NIPQUAD(srx->transport.sin.sin_addr),
138 ntohs(srx->transport.sin.sin_port));
139
140 down_write(&rxrpc_local_sem);
141
142 /* see if we have a suitable local local endpoint already */
143 read_lock_bh(&rxrpc_local_lock);
144
145 list_for_each_entry(local, &rxrpc_locals, link) {
146 _debug("CMP {%d,%u,%u.%u.%u.%u+%hu}",
147 local->srx.transport_type,
148 local->srx.transport.family,
149 NIPQUAD(local->srx.transport.sin.sin_addr),
150 ntohs(local->srx.transport.sin.sin_port));
151
152 if (local->srx.transport_type != srx->transport_type ||
153 local->srx.transport.family != srx->transport.family)
154 continue;
155
156 switch (srx->transport.family) {
157 case AF_INET:
158 if (local->srx.transport.sin.sin_port !=
159 srx->transport.sin.sin_port)
160 continue;
161 if (memcmp(&local->srx.transport.sin.sin_addr,
162 &srx->transport.sin.sin_addr,
163 sizeof(struct in_addr)) != 0)
164 continue;
165 goto found_local;
166
167 default:
168 BUG();
169 }
170 }
171
172 read_unlock_bh(&rxrpc_local_lock);
173
174 /* we didn't find one, so we need to create one */
175 local = rxrpc_alloc_local(srx);
176 if (!local) {
177 up_write(&rxrpc_local_sem);
178 return ERR_PTR(-ENOMEM);
179 }
180
181 ret = rxrpc_create_local(local);
182 if (ret < 0) {
183 up_write(&rxrpc_local_sem);
184 kfree(local);
185 _leave(" = %d", ret);
186 return ERR_PTR(ret);
187 }
188
189 up_write(&rxrpc_local_sem);
190
191 _net("LOCAL new %d {%d,%u,%u.%u.%u.%u+%hu}",
192 local->debug_id,
193 local->srx.transport_type,
194 local->srx.transport.family,
195 NIPQUAD(local->srx.transport.sin.sin_addr),
196 ntohs(local->srx.transport.sin.sin_port));
197
198 _leave(" = %p [new]", local);
199 return local;
200
201found_local:
202 rxrpc_get_local(local);
203 read_unlock_bh(&rxrpc_local_lock);
204 up_write(&rxrpc_local_sem);
205
206 _net("LOCAL old %d {%d,%u,%u.%u.%u.%u+%hu}",
207 local->debug_id,
208 local->srx.transport_type,
209 local->srx.transport.family,
210 NIPQUAD(local->srx.transport.sin.sin_addr),
211 ntohs(local->srx.transport.sin.sin_port));
212
213 _leave(" = %p [reuse]", local);
214 return local;
215}
216
217/*
218 * release a local endpoint
219 */
220void rxrpc_put_local(struct rxrpc_local *local)
221{
222 _enter("%p{u=%d}", local, atomic_read(&local->usage));
223
224 ASSERTCMP(atomic_read(&local->usage), >, 0);
225
226 /* to prevent a race, the decrement and the dequeue must be effectively
227 * atomic */
228 write_lock_bh(&rxrpc_local_lock);
229 if (unlikely(atomic_dec_and_test(&local->usage))) {
230 _debug("destroy local");
231 schedule_work(&local->destroyer);
232 }
233 write_unlock_bh(&rxrpc_local_lock);
234 _leave("");
235}
236
237/*
238 * destroy a local endpoint
239 */
240static void rxrpc_destroy_local(struct work_struct *work)
241{
242 struct rxrpc_local *local =
243 container_of(work, struct rxrpc_local, destroyer);
244
245 _enter("%p{%d}", local, atomic_read(&local->usage));
246
247 down_write(&rxrpc_local_sem);
248
249 write_lock_bh(&rxrpc_local_lock);
250 if (atomic_read(&local->usage) > 0) {
251 write_unlock_bh(&rxrpc_local_lock);
252 up_read(&rxrpc_local_sem);
253 _leave(" [resurrected]");
254 return;
255 }
256
257 list_del(&local->link);
258 local->socket->sk->sk_user_data = NULL;
259 write_unlock_bh(&rxrpc_local_lock);
260
261 downgrade_write(&rxrpc_local_sem);
262
263 ASSERT(list_empty(&local->services));
264 ASSERT(!work_pending(&local->acceptor));
265 ASSERT(!work_pending(&local->rejecter));
266
267 /* finish cleaning up the local descriptor */
268 rxrpc_purge_queue(&local->accept_queue);
269 rxrpc_purge_queue(&local->reject_queue);
270 local->socket->ops->shutdown(local->socket, 2);
271 sock_release(local->socket);
272
273 up_read(&rxrpc_local_sem);
274
275 _net("DESTROY LOCAL %d", local->debug_id);
276 kfree(local);
277
278 if (list_empty(&rxrpc_locals))
279 wake_up_all(&rxrpc_local_wq);
280
281 _leave("");
282}
283
284/*
285 * preemptively destroy all local local endpoint rather than waiting for
286 * them to be destroyed
287 */
288void __exit rxrpc_destroy_all_locals(void)
289{
290 DECLARE_WAITQUEUE(myself,current);
291
292 _enter("");
293
294 /* we simply have to wait for them to go away */
295 if (!list_empty(&rxrpc_locals)) {
296 set_current_state(TASK_UNINTERRUPTIBLE);
297 add_wait_queue(&rxrpc_local_wq, &myself);
298
299 while (!list_empty(&rxrpc_locals)) {
300 schedule();
301 set_current_state(TASK_UNINTERRUPTIBLE);
302 }
303
304 remove_wait_queue(&rxrpc_local_wq, &myself);
305 set_current_state(TASK_RUNNING);
306 }
307
308 _leave("");
309}