source: mainline/uspace/srv/net/udp/service.c@ 1f2b07a

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1f2b07a was 1f2b07a, checked in by Jiri Svoboda <jiri@…>, 11 years ago

TCP and UDP client code needs to make sure callback connection handler has terminated before freeing session-related data.

  • Property mode set to 100644
File size: 12.6 KB
Line 
1/*
2 * Copyright (c) 2015 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup udp
30 * @{
31 */
32
33/**
34 * @file HelenOS service implementation
35 */
36
37#include <async.h>
38#include <errno.h>
39#include <inet/endpoint.h>
40#include <io/log.h>
41#include <ipc/services.h>
42#include <ipc/udp.h>
43#include <loc.h>
44#include <macros.h>
45#include <stdlib.h>
46
47#include "assoc.h"
48#include "msg.h"
49#include "service.h"
50#include "udp_type.h"
51
52#define NAME "udp"
53
54#define MAX_MSG_SIZE DATA_XFER_LIMIT
55
56static void udp_cassoc_recv_msg(void *, inet_ep2_t *, udp_msg_t *);
57
58static udp_assoc_cb_t udp_cassoc_cb = {
59 .recv_msg = udp_cassoc_recv_msg
60};
61
62static int udp_cassoc_queue_msg(udp_cassoc_t *cassoc, inet_ep2_t *epp,
63 udp_msg_t *msg)
64{
65 udp_crcv_queue_entry_t *rqe;
66
67 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_cassoc_queue_msg(%p, %p, %p)",
68 cassoc, epp, msg);
69
70 rqe = calloc(1, sizeof(udp_crcv_queue_entry_t));
71 if (rqe == NULL)
72 return ENOMEM;
73
74 link_initialize(&rqe->link);
75 rqe->epp = *epp;
76 rqe->msg = msg;
77 rqe->cassoc = cassoc;
78
79// fibril_mutex_lock(&assoc->lock);
80 list_append(&rqe->link, &cassoc->client->crcv_queue);
81// fibril_mutex_unlock(&assoc->lock);
82
83// fibril_condvar_broadcast(&assoc->rcv_queue_cv);
84
85 return EOK;
86}
87
88static void udp_ev_data(udp_client_t *client)
89{
90 async_exch_t *exch;
91
92 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_ev_data()");
93
94 exch = async_exchange_begin(client->sess);
95 aid_t req = async_send_0(exch, UDP_EV_DATA, NULL);
96 async_exchange_end(exch);
97
98 async_forget(req);
99}
100
101static int udp_cassoc_create(udp_client_t *client, udp_assoc_t *assoc,
102 udp_cassoc_t **rcassoc)
103{
104 udp_cassoc_t *cassoc;
105 sysarg_t id;
106
107 cassoc = calloc(1, sizeof(udp_cassoc_t));
108 if (cassoc == NULL)
109 return ENOMEM;
110
111 /* Allocate new ID */
112 id = 0;
113 list_foreach (client->cassoc, lclient, udp_cassoc_t, cassoc) {
114 if (cassoc->id >= id)
115 id = cassoc->id + 1;
116 }
117
118 cassoc->id = id;
119 cassoc->client = client;
120 cassoc->assoc = assoc;
121
122 list_append(&cassoc->lclient, &client->cassoc);
123 *rcassoc = cassoc;
124 return EOK;
125}
126
127static void udp_cassoc_destroy(udp_cassoc_t *cassoc)
128{
129 list_remove(&cassoc->lclient);
130 free(cassoc);
131}
132
133static int udp_cassoc_get(udp_client_t *client, sysarg_t id,
134 udp_cassoc_t **rcassoc)
135{
136 list_foreach (client->cassoc, lclient, udp_cassoc_t, cassoc) {
137 if (cassoc->id == id) {
138 *rcassoc = cassoc;
139 return EOK;
140 }
141 }
142
143 return ENOENT;
144}
145
146static void udp_cassoc_recv_msg(void *arg, inet_ep2_t *epp, udp_msg_t *msg)
147{
148 udp_cassoc_t *cassoc = (udp_cassoc_t *) arg;
149
150 udp_cassoc_queue_msg(cassoc, epp, msg);
151 udp_ev_data(cassoc->client);
152}
153
154static int udp_assoc_create_impl(udp_client_t *client, inet_ep2_t *epp,
155 sysarg_t *rassoc_id)
156{
157 udp_assoc_t *assoc;
158 udp_cassoc_t *cassoc;
159 int rc;
160
161 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_create_impl");
162
163 assoc = udp_assoc_new(epp, NULL, NULL);
164 if (assoc == NULL)
165 return EIO;
166
167 if (epp->local_link != 0)
168 udp_assoc_set_iplink(assoc, epp->local_link);
169
170 rc = udp_cassoc_create(client, assoc, &cassoc);
171 if (rc != EOK) {
172 assert(rc == ENOMEM);
173 udp_assoc_delete(assoc);
174 return ENOMEM;
175 }
176
177 assoc->cb = &udp_cassoc_cb;
178 assoc->cb_arg = cassoc;
179
180 rc = udp_assoc_add(assoc);
181 if (rc != EOK) {
182 udp_cassoc_destroy(cassoc);
183 udp_assoc_delete(assoc);
184 return rc;
185 }
186
187 *rassoc_id = cassoc->id;
188 return EOK;
189}
190
191static int udp_assoc_destroy_impl(udp_client_t *client, sysarg_t assoc_id)
192{
193 udp_cassoc_t *cassoc;
194 int rc;
195
196 rc = udp_cassoc_get(client, assoc_id, &cassoc);
197 if (rc != EOK) {
198 assert(rc == ENOENT);
199 return ENOENT;
200 }
201
202 udp_assoc_remove(cassoc->assoc);
203 udp_assoc_reset(cassoc->assoc);
204 udp_assoc_delete(cassoc->assoc);
205 udp_cassoc_destroy(cassoc);
206 return EOK;
207}
208
209static int udp_assoc_send_msg_impl(udp_client_t *client, sysarg_t assoc_id,
210 inet_ep_t *dest, void *data, size_t size)
211{
212 udp_msg_t msg;
213 udp_cassoc_t *cassoc;
214 int rc;
215
216 rc = udp_cassoc_get(client, assoc_id, &cassoc);
217 if (rc != EOK)
218 return rc;
219
220 msg.data = data;
221 msg.data_size = size;
222 rc = udp_assoc_send(cassoc->assoc, dest, &msg);
223 if (rc != EOK)
224 return rc;
225
226 return EOK;
227}
228
229static void udp_callback_create_srv(udp_client_t *client, ipc_callid_t iid,
230 ipc_call_t *icall)
231{
232 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_callback_create_srv()");
233
234 async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
235 if (sess == NULL) {
236 async_answer_0(iid, ENOMEM);
237 return;
238 }
239
240 client->sess = sess;
241 async_answer_0(iid, EOK);
242}
243
244static void udp_assoc_create_srv(udp_client_t *client, ipc_callid_t iid,
245 ipc_call_t *icall)
246{
247 ipc_callid_t callid;
248 size_t size;
249 inet_ep2_t epp;
250 sysarg_t assoc_id;
251 int rc;
252
253 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_create_srv()");
254
255 if (!async_data_write_receive(&callid, &size)) {
256 async_answer_0(callid, EREFUSED);
257 async_answer_0(iid, EREFUSED);
258 return;
259 }
260
261 if (size != sizeof(inet_ep2_t)) {
262 async_answer_0(callid, EINVAL);
263 async_answer_0(iid, EINVAL);
264 return;
265 }
266
267 rc = async_data_write_finalize(callid, &epp, size);
268 if (rc != EOK) {
269 async_answer_0(callid, rc);
270 async_answer_0(iid, rc);
271 return;
272 }
273
274 rc = udp_assoc_create_impl(client, &epp, &assoc_id);
275 if (rc != EOK) {
276 async_answer_0(iid, rc);
277 return;
278 }
279
280 async_answer_1(iid, EOK, assoc_id);
281}
282
283static void udp_assoc_destroy_srv(udp_client_t *client, ipc_callid_t iid,
284 ipc_call_t *icall)
285{
286 sysarg_t assoc_id;
287 int rc;
288
289 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_destroy_srv()");
290
291 assoc_id = IPC_GET_ARG1(*icall);
292 rc = udp_assoc_destroy_impl(client, assoc_id);
293 async_answer_0(iid, rc);
294}
295
296static void udp_assoc_send_msg_srv(udp_client_t *client, ipc_callid_t iid,
297 ipc_call_t *icall)
298{
299 ipc_callid_t callid;
300 size_t size;
301 inet_ep_t dest;
302 sysarg_t assoc_id;
303 void *data;
304 int rc;
305
306 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send_msg_srv()");
307
308 /* Receive dest */
309
310 if (!async_data_write_receive(&callid, &size)) {
311 async_answer_0(callid, EREFUSED);
312 async_answer_0(iid, EREFUSED);
313 return;
314 }
315
316 if (size != sizeof(inet_ep_t)) {
317 async_answer_0(callid, EINVAL);
318 async_answer_0(iid, EINVAL);
319 return;
320 }
321
322 rc = async_data_write_finalize(callid, &dest, size);
323 if (rc != EOK) {
324 async_answer_0(callid, rc);
325 async_answer_0(iid, rc);
326 return;
327 }
328
329 /* Receive message data */
330
331 if (!async_data_write_receive(&callid, &size)) {
332 async_answer_0(callid, EREFUSED);
333 async_answer_0(iid, EREFUSED);
334 return;
335 }
336
337 if (size > MAX_MSG_SIZE) {
338 async_answer_0(callid, EINVAL);
339 async_answer_0(iid, EINVAL);
340 return;
341 }
342
343 data = malloc(size);
344 if (data == NULL) {
345 async_answer_0(callid, ENOMEM);
346 async_answer_0(iid, ENOMEM);
347 }
348
349 rc = async_data_write_finalize(callid, data, size);
350 if (rc != EOK) {
351 async_answer_0(callid, rc);
352 async_answer_0(iid, rc);
353 free(data);
354 return;
355 }
356
357 assoc_id = IPC_GET_ARG1(*icall);
358
359 rc = udp_assoc_send_msg_impl(client, assoc_id, &dest, data, size);
360 if (rc != EOK) {
361 async_answer_0(iid, rc);
362 free(data);
363 return;
364 }
365
366 async_answer_0(iid, EOK);
367 free(data);
368}
369
370static udp_crcv_queue_entry_t *udp_rmsg_get_next(udp_client_t *client)
371{
372 link_t *link;
373
374 link = list_first(&client->crcv_queue);
375 if (link == NULL)
376 return NULL;
377
378 return list_get_instance(link, udp_crcv_queue_entry_t, link);
379}
380
381static void udp_rmsg_info_srv(udp_client_t *client, ipc_callid_t iid,
382 ipc_call_t *icall)
383{
384 ipc_callid_t callid;
385 size_t size;
386 udp_crcv_queue_entry_t *enext;
387 sysarg_t assoc_id;
388 int rc;
389
390 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_info_srv()");
391 enext = udp_rmsg_get_next(client);
392
393 if (!async_data_read_receive(&callid, &size)) {
394 async_answer_0(callid, EREFUSED);
395 async_answer_0(iid, EREFUSED);
396 return;
397 }
398
399 if (enext == NULL) {
400 async_answer_0(callid, ENOENT);
401 async_answer_0(iid, ENOENT);
402 return;
403 }
404
405 rc = async_data_read_finalize(callid, &enext->epp.remote,
406 max(size, (ssize_t)sizeof(inet_ep_t)));
407 if (rc != EOK) {
408 async_answer_0(iid, rc);
409 return;
410 }
411
412 assoc_id = enext->cassoc->id;
413 size = enext->msg->data_size;
414
415 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_info_srv(): assoc_id=%zu, "
416 "size=%zu", assoc_id, size);
417 async_answer_2(iid, EOK, assoc_id, size);
418}
419
420static void udp_rmsg_read_srv(udp_client_t *client, ipc_callid_t iid,
421 ipc_call_t *icall)
422{
423 ipc_callid_t callid;
424 ssize_t msg_size;
425 udp_crcv_queue_entry_t *enext;
426 void *data;
427 size_t size;
428 ssize_t off;
429 int rc;
430
431 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_read_srv()");
432 off = IPC_GET_ARG1(*icall);
433
434 enext = udp_rmsg_get_next(client);
435
436 if (!async_data_read_receive(&callid, &size)) {
437 async_answer_0(callid, EREFUSED);
438 async_answer_0(iid, EREFUSED);
439 return;
440 }
441
442 if (enext == NULL) {
443 async_answer_0(callid, ENOENT);
444 async_answer_0(iid, ENOENT);
445 return;
446 }
447
448 data = enext->msg->data + off;
449 msg_size = enext->msg->data_size;
450
451 rc = async_data_read_finalize(callid, data, max(msg_size - off,
452 (ssize_t)size));
453 if (rc != EOK) {
454 async_answer_0(iid, rc);
455 return;
456 }
457
458 async_answer_0(iid, EOK);
459 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_read_srv(): OK");
460}
461
462static void udp_rmsg_discard_srv(udp_client_t *client, ipc_callid_t iid,
463 ipc_call_t *icall)
464{
465 udp_crcv_queue_entry_t *enext;
466
467 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_discard_srv()");
468
469 enext = udp_rmsg_get_next(client);
470 if (enext == NULL) {
471 log_msg(LOG_DEFAULT, LVL_DEBUG, "usg_rmsg_discard_srv: enext==NULL");
472 async_answer_0(iid, ENOENT);
473 return;
474 }
475
476 list_remove(&enext->link);
477 udp_msg_delete(enext->msg);
478 free(enext);
479 async_answer_0(iid, EOK);
480}
481
482static void udp_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
483{
484 udp_client_t client;
485 size_t n;
486
487 /* Accept the connection */
488 async_answer_0(iid, EOK);
489
490 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn()");
491
492 client.sess = NULL;
493 list_initialize(&client.cassoc);
494 list_initialize(&client.crcv_queue);
495
496 while (true) {
497 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: wait req");
498 ipc_call_t call;
499 ipc_callid_t callid = async_get_call(&call);
500 sysarg_t method = IPC_GET_IMETHOD(call);
501
502 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: method=%d",
503 (int)method);
504 if (!method) {
505 /* The other side has hung up */
506 async_answer_0(callid, EOK);
507 break;
508 }
509
510 switch (method) {
511 case UDP_CALLBACK_CREATE:
512 udp_callback_create_srv(&client, callid, &call);
513 break;
514 case UDP_ASSOC_CREATE:
515 udp_assoc_create_srv(&client, callid, &call);
516 break;
517 case UDP_ASSOC_DESTROY:
518 udp_assoc_destroy_srv(&client, callid, &call);
519 break;
520 case UDP_ASSOC_SEND_MSG:
521 udp_assoc_send_msg_srv(&client, callid, &call);
522 break;
523 case UDP_RMSG_INFO:
524 udp_rmsg_info_srv(&client, callid, &call);
525 break;
526 case UDP_RMSG_READ:
527 udp_rmsg_read_srv(&client, callid, &call);
528 break;
529 case UDP_RMSG_DISCARD:
530 udp_rmsg_discard_srv(&client, callid, &call);
531 break;
532 default:
533 async_answer_0(callid, ENOTSUP);
534 break;
535 }
536 }
537
538 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: terminated");
539
540 n = list_count(&client.cassoc);
541 if (n != 0) {
542 log_msg(LOG_DEFAULT, LVL_WARN, "udp_client_conn: "
543 "Client with %zu active associations closed session.", n);
544 /* XXX Clean up */
545 }
546
547 /* XXX Clean up client receive queue */
548
549 if (client.sess != NULL)
550 async_hangup(client.sess);
551}
552
553int udp_service_init(void)
554{
555 int rc;
556 service_id_t sid;
557
558 async_set_client_connection(udp_client_conn);
559
560 rc = loc_server_register(NAME);
561 if (rc != EOK) {
562 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering server.");
563 return EIO;
564 }
565
566 rc = loc_service_register(SERVICE_NAME_UDP, &sid);
567 if (rc != EOK) {
568 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service.");
569 return EIO;
570 }
571
572 return EOK;
573}
574
575/**
576 * @}
577 */
Note: See TracBrowser for help on using the repository browser.