source: mainline/uspace/srv/net/udp/service.c@ 984a9ba

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 984a9ba was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • Property mode set to 100644
File size: 17.1 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/** Maximum message size */
55#define MAX_MSG_SIZE DATA_XFER_LIMIT
56
57static void udp_cassoc_recv_msg(void *, inet_ep2_t *, udp_msg_t *);
58
59/** Callbacks to tie us to association layer */
60static udp_assoc_cb_t udp_cassoc_cb = {
61 .recv_msg = udp_cassoc_recv_msg
62};
63
64/** Add message to client receive queue.
65 *
66 * @param cassoc Client association
67 * @param epp Endpoint pair on which message was received
68 * @param msg Message
69 *
70 * @return EOK on success, ENOMEM if out of memory
71 */
72static errno_t udp_cassoc_queue_msg(udp_cassoc_t *cassoc, inet_ep2_t *epp,
73 udp_msg_t *msg)
74{
75 udp_crcv_queue_entry_t *rqe;
76
77 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_cassoc_queue_msg(%p, %p, %p)",
78 cassoc, epp, msg);
79
80 rqe = calloc(1, sizeof(udp_crcv_queue_entry_t));
81 if (rqe == NULL)
82 return ENOMEM;
83
84 link_initialize(&rqe->link);
85 rqe->epp = *epp;
86 rqe->msg = msg;
87 rqe->cassoc = cassoc;
88
89 list_append(&rqe->link, &cassoc->client->crcv_queue);
90 return EOK;
91}
92
93/** Send 'data' event to client.
94 *
95 * @param client Client
96 */
97static void udp_ev_data(udp_client_t *client)
98{
99 async_exch_t *exch;
100
101 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_ev_data()");
102
103 exch = async_exchange_begin(client->sess);
104 aid_t req = async_send_0(exch, UDP_EV_DATA, NULL);
105 async_exchange_end(exch);
106
107 async_forget(req);
108}
109
110/** Create client association.
111 *
112 * This effectively adds an association into a client's namespace.
113 *
114 * @param client Client
115 * @param assoc Association
116 * @param rcassoc Place to store pointer to new client association
117 *
118 * @return EOK on soccess, ENOMEM if out of memory
119 */
120static errno_t udp_cassoc_create(udp_client_t *client, udp_assoc_t *assoc,
121 udp_cassoc_t **rcassoc)
122{
123 udp_cassoc_t *cassoc;
124 sysarg_t id;
125
126 cassoc = calloc(1, sizeof(udp_cassoc_t));
127 if (cassoc == NULL)
128 return ENOMEM;
129
130 /* Allocate new ID */
131 id = 0;
132 list_foreach (client->cassoc, lclient, udp_cassoc_t, cassoc) {
133 if (cassoc->id >= id)
134 id = cassoc->id + 1;
135 }
136
137 cassoc->id = id;
138 cassoc->client = client;
139 cassoc->assoc = assoc;
140
141 list_append(&cassoc->lclient, &client->cassoc);
142 *rcassoc = cassoc;
143 return EOK;
144}
145
146/** Destroy client association.
147 *
148 * @param cassoc Client association
149 */
150static void udp_cassoc_destroy(udp_cassoc_t *cassoc)
151{
152 list_remove(&cassoc->lclient);
153 free(cassoc);
154}
155
156/** Get client association by ID.
157 *
158 * @param client Client
159 * @param id Client association ID
160 * @param rcassoc Place to store pointer to client association
161 *
162 * @return EOK on success, ENOENT if no client association with the given ID
163 * is found.
164 */
165static errno_t udp_cassoc_get(udp_client_t *client, sysarg_t id,
166 udp_cassoc_t **rcassoc)
167{
168 list_foreach (client->cassoc, lclient, udp_cassoc_t, cassoc) {
169 if (cassoc->id == id) {
170 *rcassoc = cassoc;
171 return EOK;
172 }
173 }
174
175 return ENOENT;
176}
177
178/** Message received on client association.
179 *
180 * Used as udp_assoc_cb.recv_msg callback.
181 *
182 * @param arg Callback argument, client association
183 * @param epp Endpoint pair where message was received
184 * @param msg Message
185 */
186static void udp_cassoc_recv_msg(void *arg, inet_ep2_t *epp, udp_msg_t *msg)
187{
188 udp_cassoc_t *cassoc = (udp_cassoc_t *) arg;
189
190 udp_cassoc_queue_msg(cassoc, epp, msg);
191 udp_ev_data(cassoc->client);
192}
193
194/** Create association.
195 *
196 * Handle client request to create association (with parameters unmarshalled).
197 *
198 * @param client UDP client
199 * @param epp Endpoint pair
200 * @param rassoc_id Place to store ID of new association
201 *
202 * @return EOK on success or an error code
203 */
204static errno_t udp_assoc_create_impl(udp_client_t *client, inet_ep2_t *epp,
205 sysarg_t *rassoc_id)
206{
207 udp_assoc_t *assoc;
208 udp_cassoc_t *cassoc;
209 errno_t rc;
210
211 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_create_impl");
212
213 assoc = udp_assoc_new(epp, NULL, NULL);
214 if (assoc == NULL)
215 return EIO;
216
217 if (epp->local_link != 0)
218 udp_assoc_set_iplink(assoc, epp->local_link);
219
220 rc = udp_cassoc_create(client, assoc, &cassoc);
221 if (rc != EOK) {
222 assert(rc == ENOMEM);
223 udp_assoc_delete(assoc);
224 return ENOMEM;
225 }
226
227 assoc->cb = &udp_cassoc_cb;
228 assoc->cb_arg = cassoc;
229
230 rc = udp_assoc_add(assoc);
231 if (rc != EOK) {
232 udp_cassoc_destroy(cassoc);
233 udp_assoc_delete(assoc);
234 return rc;
235 }
236
237 *rassoc_id = cassoc->id;
238 return EOK;
239}
240
241/** Destroy association.
242 *
243 * Handle client request to destroy association (with parameters unmarshalled).
244 *
245 * @param client UDP client
246 * @param assoc_id Association ID
247 * @return EOK on success, ENOENT if no such association is found
248 */
249static errno_t udp_assoc_destroy_impl(udp_client_t *client, sysarg_t assoc_id)
250{
251 udp_cassoc_t *cassoc;
252 errno_t rc;
253
254 rc = udp_cassoc_get(client, assoc_id, &cassoc);
255 if (rc != EOK) {
256 assert(rc == ENOENT);
257 return ENOENT;
258 }
259
260 udp_assoc_remove(cassoc->assoc);
261 udp_assoc_reset(cassoc->assoc);
262 udp_assoc_delete(cassoc->assoc);
263 udp_cassoc_destroy(cassoc);
264 return EOK;
265}
266
267/** Set association sending messages with no local address.
268 *
269 * Handle client request to set nolocal flag (with parameters unmarshalled).
270 *
271 * @param client UDP client
272 * @param assoc_id Association ID
273 * @return EOK on success, ENOENT if no such association is found
274 */
275static errno_t udp_assoc_set_nolocal_impl(udp_client_t *client, sysarg_t assoc_id)
276{
277 udp_cassoc_t *cassoc;
278 errno_t rc;
279
280 rc = udp_cassoc_get(client, assoc_id, &cassoc);
281 if (rc != EOK) {
282 assert(rc == ENOENT);
283 return ENOENT;
284 }
285
286 log_msg(LOG_DEFAULT, LVL_NOTE, "Setting nolocal to true");
287 cassoc->assoc->nolocal = true;
288 return EOK;
289}
290
291/** Send message via association.
292 *
293 * Handle client request to send message (with parameters unmarshalled).
294 *
295 * @param client UDP client
296 * @param assoc_id Association ID
297 * @param dest Destination endpoint or @c NULL to use the default from
298 * association
299 * @param data Message data
300 * @param size Message size
301 *
302 * @return EOK on success or an error code
303 */
304static errno_t udp_assoc_send_msg_impl(udp_client_t *client, sysarg_t assoc_id,
305 inet_ep_t *dest, void *data, size_t size)
306{
307 udp_msg_t msg;
308 udp_cassoc_t *cassoc;
309 errno_t rc;
310
311 rc = udp_cassoc_get(client, assoc_id, &cassoc);
312 if (rc != EOK)
313 return rc;
314
315 msg.data = data;
316 msg.data_size = size;
317 rc = udp_assoc_send(cassoc->assoc, dest, &msg);
318 if (rc != EOK)
319 return rc;
320
321 return EOK;
322}
323
324/** Create callback session.
325 *
326 * Handle client request to create callback session.
327 *
328 * @param client UDP client
329 * @param icall Async request data
330 *
331 */
332static void udp_callback_create_srv(udp_client_t *client, ipc_call_t *icall)
333{
334 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_callback_create_srv()");
335
336 async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
337 if (sess == NULL) {
338 async_answer_0(icall, ENOMEM);
339 return;
340 }
341
342 client->sess = sess;
343 async_answer_0(icall, EOK);
344}
345
346/** Create association.
347 *
348 * Handle client request to create association.
349 *
350 * @param client UDP client
351 * @param icall Async request data
352 *
353 */
354static void udp_assoc_create_srv(udp_client_t *client, ipc_call_t *icall)
355{
356 ipc_call_t call;
357 size_t size;
358 inet_ep2_t epp;
359 sysarg_t assoc_id;
360 errno_t rc;
361
362 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_create_srv()");
363
364 if (!async_data_write_receive(&call, &size)) {
365 async_answer_0(&call, EREFUSED);
366 async_answer_0(icall, EREFUSED);
367 return;
368 }
369
370 if (size != sizeof(inet_ep2_t)) {
371 async_answer_0(&call, EINVAL);
372 async_answer_0(icall, EINVAL);
373 return;
374 }
375
376 rc = async_data_write_finalize(&call, &epp, size);
377 if (rc != EOK) {
378 async_answer_0(&call, rc);
379 async_answer_0(icall, rc);
380 return;
381 }
382
383 rc = udp_assoc_create_impl(client, &epp, &assoc_id);
384 if (rc != EOK) {
385 async_answer_0(icall, rc);
386 return;
387 }
388
389 async_answer_1(icall, EOK, assoc_id);
390}
391
392/** Destroy association.
393 *
394 * Handle client request to destroy association.
395 *
396 * @param client UDP client
397 * @param icall Async request data
398 *
399 */
400static void udp_assoc_destroy_srv(udp_client_t *client, ipc_call_t *icall)
401{
402 sysarg_t assoc_id;
403 errno_t rc;
404
405 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_destroy_srv()");
406
407 assoc_id = IPC_GET_ARG1(*icall);
408 rc = udp_assoc_destroy_impl(client, assoc_id);
409 async_answer_0(icall, rc);
410}
411
412/** Set association with no local address.
413 *
414 * Handle client request to set no local address flag.
415 *
416 * @param client UDP client
417 * @param icall Async request data
418 *
419 */
420static void udp_assoc_set_nolocal_srv(udp_client_t *client, ipc_call_t *icall)
421{
422 sysarg_t assoc_id;
423 errno_t rc;
424
425 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_set_nolocal_srv()");
426
427 assoc_id = IPC_GET_ARG1(*icall);
428 rc = udp_assoc_set_nolocal_impl(client, assoc_id);
429 async_answer_0(icall, rc);
430}
431
432/** Send message via association.
433 *
434 * Handle client request to send message.
435 *
436 * @param client UDP client
437 * @param icall Async request data
438 *
439 */
440static void udp_assoc_send_msg_srv(udp_client_t *client, ipc_call_t *icall)
441{
442 ipc_call_t call;
443 size_t size;
444 inet_ep_t dest;
445 sysarg_t assoc_id;
446 void *data;
447 errno_t rc;
448
449 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send_msg_srv()");
450
451 /* Receive dest */
452
453 if (!async_data_write_receive(&call, &size)) {
454 async_answer_0(&call, EREFUSED);
455 async_answer_0(icall, EREFUSED);
456 return;
457 }
458
459 if (size != sizeof(inet_ep_t)) {
460 async_answer_0(&call, EINVAL);
461 async_answer_0(icall, EINVAL);
462 return;
463 }
464
465 rc = async_data_write_finalize(&call, &dest, size);
466 if (rc != EOK) {
467 async_answer_0(&call, rc);
468 async_answer_0(icall, rc);
469 return;
470 }
471
472 /* Receive message data */
473
474 if (!async_data_write_receive(&call, &size)) {
475 async_answer_0(&call, EREFUSED);
476 async_answer_0(icall, EREFUSED);
477 return;
478 }
479
480 if (size > MAX_MSG_SIZE) {
481 async_answer_0(&call, EINVAL);
482 async_answer_0(icall, EINVAL);
483 return;
484 }
485
486 data = malloc(size);
487 if (data == NULL) {
488 async_answer_0(&call, ENOMEM);
489 async_answer_0(icall, ENOMEM);
490 }
491
492 rc = async_data_write_finalize(&call, data, size);
493 if (rc != EOK) {
494 async_answer_0(&call, rc);
495 async_answer_0(icall, rc);
496 free(data);
497 return;
498 }
499
500 assoc_id = IPC_GET_ARG1(*icall);
501
502 rc = udp_assoc_send_msg_impl(client, assoc_id, &dest, data, size);
503 if (rc != EOK) {
504 async_answer_0(icall, rc);
505 free(data);
506 return;
507 }
508
509 async_answer_0(icall, EOK);
510 free(data);
511}
512
513/** Get next received message.
514 *
515 * @param client UDP Client
516 * @return Pointer to queue entry for next received message
517 */
518static udp_crcv_queue_entry_t *udp_rmsg_get_next(udp_client_t *client)
519{
520 link_t *link;
521
522 link = list_first(&client->crcv_queue);
523 if (link == NULL)
524 return NULL;
525
526 return list_get_instance(link, udp_crcv_queue_entry_t, link);
527}
528
529/** Get info on first received message.
530 *
531 * Handle client request to get information on received message.
532 *
533 * @param client UDP client
534 * @param icall Async request data
535 *
536 */
537static void udp_rmsg_info_srv(udp_client_t *client, ipc_call_t *icall)
538{
539 ipc_call_t call;
540 size_t size;
541 udp_crcv_queue_entry_t *enext;
542 sysarg_t assoc_id;
543 errno_t rc;
544
545 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_info_srv()");
546 enext = udp_rmsg_get_next(client);
547
548 if (!async_data_read_receive(&call, &size)) {
549 async_answer_0(&call, EREFUSED);
550 async_answer_0(icall, EREFUSED);
551 return;
552 }
553
554 if (enext == NULL) {
555 async_answer_0(&call, ENOENT);
556 async_answer_0(icall, ENOENT);
557 return;
558 }
559
560 rc = async_data_read_finalize(&call, &enext->epp.remote,
561 max(size, (size_t)sizeof(inet_ep_t)));
562 if (rc != EOK) {
563 async_answer_0(icall, rc);
564 return;
565 }
566
567 assoc_id = enext->cassoc->id;
568 size = enext->msg->data_size;
569
570 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_info_srv(): assoc_id=%zu, "
571 "size=%zu", assoc_id, size);
572 async_answer_2(icall, EOK, assoc_id, size);
573}
574
575/** Read data from first received message.
576 *
577 * Handle client request to read data from first received message.
578 *
579 * @param client UDP client
580 * @param icall Async request data
581 *
582 */
583static void udp_rmsg_read_srv(udp_client_t *client, ipc_call_t *icall)
584{
585 ipc_call_t call;
586 size_t msg_size;
587 udp_crcv_queue_entry_t *enext;
588 void *data;
589 size_t size;
590 size_t off;
591 errno_t rc;
592
593 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_read_srv()");
594 off = IPC_GET_ARG1(*icall);
595
596 enext = udp_rmsg_get_next(client);
597
598 if (!async_data_read_receive(&call, &size)) {
599 async_answer_0(&call, EREFUSED);
600 async_answer_0(icall, EREFUSED);
601 return;
602 }
603
604 if (enext == NULL) {
605 async_answer_0(&call, ENOENT);
606 async_answer_0(icall, ENOENT);
607 return;
608 }
609
610 data = enext->msg->data + off;
611 msg_size = enext->msg->data_size;
612
613 if (off > msg_size) {
614 async_answer_0(&call, EINVAL);
615 async_answer_0(icall, EINVAL);
616 return;
617 }
618
619 rc = async_data_read_finalize(&call, data, min(msg_size - off, size));
620 if (rc != EOK) {
621 async_answer_0(icall, rc);
622 return;
623 }
624
625 async_answer_0(icall, EOK);
626 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_read_srv(): OK");
627}
628
629/** Discard first received message.
630 *
631 * Handle client request to discard first received message, advancing
632 * to the next one.
633 *
634 * @param client UDP client
635 * @param icall Async request data
636 *
637 */
638static void udp_rmsg_discard_srv(udp_client_t *client, ipc_call_t *icall)
639{
640 udp_crcv_queue_entry_t *enext;
641
642 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_discard_srv()");
643
644 enext = udp_rmsg_get_next(client);
645 if (enext == NULL) {
646 log_msg(LOG_DEFAULT, LVL_DEBUG, "usg_rmsg_discard_srv: enext==NULL");
647 async_answer_0(icall, ENOENT);
648 return;
649 }
650
651 list_remove(&enext->link);
652 udp_msg_delete(enext->msg);
653 free(enext);
654 async_answer_0(icall, EOK);
655}
656
657/** Handle UDP client connection.
658 *
659 * @param icall Connect call data
660 * @param arg Connection argument
661 *
662 */
663static void udp_client_conn(ipc_call_t *icall, void *arg)
664{
665 udp_client_t client;
666 unsigned long n;
667
668 /* Accept the connection */
669 async_answer_0(icall, EOK);
670
671 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn()");
672
673 client.sess = NULL;
674 list_initialize(&client.cassoc);
675 list_initialize(&client.crcv_queue);
676
677 while (true) {
678 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: wait req");
679 ipc_call_t call;
680 async_get_call(&call);
681 sysarg_t method = IPC_GET_IMETHOD(call);
682
683 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: method=%d",
684 (int)method);
685 if (!method) {
686 /* The other side has hung up */
687 async_answer_0(&call, EOK);
688 break;
689 }
690
691 switch (method) {
692 case UDP_CALLBACK_CREATE:
693 udp_callback_create_srv(&client, &call);
694 break;
695 case UDP_ASSOC_CREATE:
696 udp_assoc_create_srv(&client, &call);
697 break;
698 case UDP_ASSOC_DESTROY:
699 udp_assoc_destroy_srv(&client, &call);
700 break;
701 case UDP_ASSOC_SET_NOLOCAL:
702 udp_assoc_set_nolocal_srv(&client, &call);
703 break;
704 case UDP_ASSOC_SEND_MSG:
705 udp_assoc_send_msg_srv(&client, &call);
706 break;
707 case UDP_RMSG_INFO:
708 udp_rmsg_info_srv(&client, &call);
709 break;
710 case UDP_RMSG_READ:
711 udp_rmsg_read_srv(&client, &call);
712 break;
713 case UDP_RMSG_DISCARD:
714 udp_rmsg_discard_srv(&client, &call);
715 break;
716 default:
717 async_answer_0(&call, ENOTSUP);
718 break;
719 }
720 }
721
722 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: terminated");
723
724 n = list_count(&client.cassoc);
725 if (n != 0) {
726 log_msg(LOG_DEFAULT, LVL_WARN, "udp_client_conn: "
727 "Client with %lu active associations closed session.", n);
728 /* XXX Clean up */
729 }
730
731 /* XXX Clean up client receive queue */
732
733 if (client.sess != NULL)
734 async_hangup(client.sess);
735}
736
737/** Initialize UDP service.
738 *
739 * @return EOK on success or an error code.
740 */
741errno_t udp_service_init(void)
742{
743 errno_t rc;
744 service_id_t sid;
745
746 async_set_fallback_port_handler(udp_client_conn, NULL);
747
748 rc = loc_server_register(NAME);
749 if (rc != EOK) {
750 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering server.");
751 return EIO;
752 }
753
754 rc = loc_service_register(SERVICE_NAME_UDP, &sid);
755 if (rc != EOK) {
756 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service.");
757 return EIO;
758 }
759
760 return EOK;
761}
762
763/**
764 * @}
765 */
Note: See TracBrowser for help on using the repository browser.