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

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

Move UDP's client association to a separate module

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